热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

Mysql全局ID生成方法_MySQL

本文给大家介绍mysql全局id生成方法,涉及到mysql全局id相关知识,感兴趣的朋友一起学习吧
生产系统随着业务增长总会经历一个业务量由小变大的过程,可扩展性是考量数据库系统高可用性的一个重要指标;在单表/数据库数据量过大,更新量不断飙涨时,MySQL DBA往往会对业务系统提出sharding的方案。既然要sharding,那么不可避免的要讨论到sharding key问题,在有些业务系统中,必须保证sharding key全局唯一,比如存放商品的数据库等,那么如何生成全局唯一的ID呢,下文将从DBA的角度介绍几种常见的方案。

1、使用CAS思想

什么是CAS协议

Memcached于1.2.4版本新增CAS(Check and Set)协议类同于Java并发的CAS(Compare and Swap)原子操作,处理同一item被多个线程更改过程的并发问题

CAS的基本原理

基本原理非常简单,一言以蔽之,就是“版本号”,每个存储的数据对象,都有一个版本号。

我们可以从下面的例子来理解:

不采用CAS,则有如下的情景:

•第一步,A取出数据对象X;
•第二步,B取出数据对象X;
•第三步,B修改数据对象X,并将其放入缓存;
•第四步,A修改数据对象X,并将其放入缓存。

结论:第四步中会产生数据写入冲突。

采用CAS协议,则是如下的情景。

•第一步,A取出数据对象X,并获取到CAS-ID1;

•第二步,B取出数据对象X,并获取到CAS-ID2;

•第三步,B修改数据对象X,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“一致”,就将修改后的带有CAS-ID2的X写入到缓存。

•第四步,A修改数据对象Y,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“不一致”,则拒绝写入,返回存储失败。

这样CAS协议就用了“版本号”的思想,解决了冲突问题。(乐观锁概念)

其实这里并不是严格的CAS,而是使用了比较交换原子操作的思想。

生成思路如下:每次生成全局id时,先从sequence表中获取当前的全局最大id。然后在获取的全局id上做加1操作,加1后的值更新到数据库,如加1后的值为203,表名是users,数据表结构如下:

CREATE TABLE `SEQUENCE` (
  `name` varchar(30) NOT NULL COMMENT '分表的表名',
  `gid` bigint(20) NOT NULL COMMENT '最大全局id',
  PRIMARY KEY (`name`)
) ENGINE=innodb 

sql语句

update sequence set gid = 203 where name = 'users' and gid <203; 

sql语句的 and gid <203 是为了保证并发环境下gid的值只增不减。

如果update语句的影响记录条数为0说明,已经有其他进程提前生成了203这个值,并写入了数据库。需要重复以上步骤从新生成。

代码实现如下:

//$name 表名
function next_id_db($name){
  //获取数据库全局sequence对象
  $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();
  $threshold = 100; //最大尝试次数
  for($i = 0; $i <$threshold; $i++){
    $last_id = $seq_dao->get_seq_id($name);//从数据库获取全局id
    $id = $last_id +1;
    $ret = $seq_dao->set_seq_id($name, $id);
    if($ret){
      return $id;
      break;
    }
  }
  return false;
}

2、使用全局锁

在进行并发编程时,一般都会使用锁机制。其实,全局id的生成也是解决并发问题。

生成思路如下:

在使用redis的setnx方法和memcace的add方法时,如果指定的key已经存在,则返回false。利用这个特性,实现全局锁

每次生成全局id前,先检测指定的key是否存在,如果不存在则使用redis的incr方法或者memcache的increment进行加1操作。这两个方法的返回值是加1后的值,如果存在,则程序进入循环等待状态。循环过程中不断检测key是否还存在,如果key不存在就执行上面的操作。

代码如下:

//使用redis实现
//$name 为 逻辑表名
function next_id_redis($name){
  $redis = Wk_Redis_Util::getRedis();//获取redis对象
  $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();//获取存储全局id数据表对象
  if(!is_object($redis)){
    throw new Exception("fail to create redis object");
  }
  $max_times = 10; //最大执行次数 避免redis不可用的时候 进入死循环
  while(1){
    $i++;
    //检测key是否存在,相当于检测锁是否存在
    $ret = $redis->setnx("sequence_{$name}_flag",time());
    if($ret){
      break;
    }
    if($i > $max_times){
      break;
    }
    $time = $redis->get("sequence_{$name}_flag");
    if(is_numeric($time) && time() - $time > 1){//如果循环等待时间大于1秒,则不再等待。
      break;
    }
  }
  $id = $redis->incr("sequence_{$name}");
  //如果操作失败,则从sequence表中获取全局id并加载到redis
  if (intval($id) === 1 or $id === false) {
    $last_id = $seq_dao->get_seq_id($name);//从数据库获取全局id
    if(!is_numeric($last_id)){
      throw new Exception("fail to get id from db");
    }
    $ret = $redis->set("sequence_{$name}",$last_id);
    if($ret == false){
      throw new Exception("fail to set redis key [ sequence_{$name} ]");
    }
    $id = $redis->incr("sequence_{$name}");
    if(!is_numeric($id)){
      throw new Exception("fail to incr redis key [ sequence_{$name} ]");
    }
  }
  $seq_dao->set_seq_id($name, $id);//把生成的全局id写入数据表sequence
  $redis->delete("sequence_{$name}_flag");//删除key,相当于释放锁
  $db = null;
  return $id;
} 

3、redis和db结合

使用redis直接操作内存,可能性能会好些。但是如果redis死掉后,如何处理呢?把以上两种方案结合,提供更好的稳定性。
代码如下:

function next_id($name){
  try{
    return $this->next_id_redis($name);
  }
  catch(Exception $e){
    return $this->next_id_db($name);
  }
} 

4、Flicker的解决方案

因为mysql本身支持auto_increment操作,很自然地,我们会想到借助这个特性来实现这个功能。Flicker在解决全局ID生成方案里就采用了MySQL自增长ID的机制(auto_increment + replace into + MyISAM)。一个生成64位ID方案具体就是这样的:
先创建单独的数据库(eg:ticket),然后创建一个表:

CREATE TABLE Tickets64 (
      id bigint(20) unsigned NOT NULL auto_increment,
      stub char(1) NOT NULL default '',
      PRIMARY KEY (id),
      UNIQUE KEY stub (stub)
  ) ENGINE=MyISAM 

当我们插入记录后,执行SELECT * from Tickets64,查询结果就是这样的:

+-------------------+------+
| id | stub |
+-------------------+------+
| 72157623227190423 | a |
+-------------------+------+

在我们的应用端需要做下面这两个操作,在一个事务会话里提交:

REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID(); 

这样我们就能拿到不断增长且不重复的ID了。
到上面为止,我们只是在单台数据库上生成ID,从高可用角度考虑,
接下来就要解决单点故障问题:Flicker启用了两台数据库服务器来生成ID,
通过区分auto_increment的起始值和步长来生成奇偶数的ID。

TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1
TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2 

最后,在客户端只需要通过轮询方式取ID就可以了。

&#8226;优点:充分借助数据库的自增ID机制,提供高可靠性,生成的ID有序。

&#8226;缺点:占用两个独立的MySQL实例,有些浪费资源,成本较高。

以上内容是小编给大家分享的Mysql全局ID生成方法,希望大家喜欢。

推荐阅读
  • Redis:缓存与内存数据库详解
    本文介绍了数据库的基本分类,重点探讨了关系型与非关系型数据库的区别,并详细解析了Redis作为非关系型数据库的特点、工作模式、优点及持久化机制。 ... [详细]
  • 本文回顾了作者在求职阿里和腾讯实习生过程中,从最初的迷茫到最后成功获得Offer的心路历程。文中不仅分享了个人的面试经历,还提供了宝贵的面试准备建议和技巧。 ... [详细]
  • PHP面试题精选及答案解析
    本文精选了新浪PHP笔试题及最新的PHP面试题,并提供了详细的答案解析,帮助求职者更好地准备PHP相关的面试。 ... [详细]
  • Redis支持哪几种数据类型?支持多种类型的数据结构1.string:最基本的数据类型,二进制安全的字符串,最大512M。2 ... [详细]
  • binlog2sql,你该知道的数据恢复工具
    binlog2sql,你该知道的数据恢复工具 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • 本文详细探讨了在Web开发中常见的UTF-8编码问题及其解决方案,包括HTML页面、PHP脚本、MySQL数据库以及JavaScript和Flash应用中的乱码问题。 ... [详细]
  • 电商高并发解决方案详解
    本文以京东为例,详细探讨了电商中常见的高并发解决方案,包括多级缓存和Nginx限流技术,旨在帮助读者更好地理解和应用这些技术。 ... [详细]
  • Spring Boot + RabbitMQ 消息确认机制详解
    本文详细介绍如何在 Spring Boot 项目中使用 RabbitMQ 的消息确认机制,包括消息发送确认和消息接收确认,帮助开发者解决在实际操作中可能遇到的问题。 ... [详细]
  • 深入理解Redis中的字典实现
    本文详细介绍了Redis中字典的实现机制,包括其底层数据结构、哈希表与哈希节点的关系、元素添加方法及rehash操作的具体流程。 ... [详细]
  • RocketMQ在秒杀时的应用
    目录一、RocketMQ是什么二、broker和nameserver2.1Broker2.2NameServer三、MQ在秒杀场景下的应用3.1利用MQ进行异步操作3. ... [详细]
  • 深入解析Redis中五大数据类型的实用应用场景与技巧
    本文将深入探讨Redis中五大核心数据类型的实际应用场景与使用技巧。通过详尽的分析和示例,帮助读者理解每种数据类型的特性和最佳实践,内容涵盖广泛,步骤清晰,细节处理得当,旨在为开发者提供全面的指导。 ... [详细]
  • 作为140字符的开创者,Twitter看似简单却异常复杂。其简洁之处在于仅用140个字符就能实现信息的高效传播,甚至在多次全球性事件中超越传统媒体的速度。然而,为了支持2亿用户的高效使用,其背后的技术架构和系统设计则极为复杂,涉及高并发处理、数据存储和实时传输等多个技术挑战。 ... [详细]
  • 黄聪:MySQL主从复制配置,实现高效读写分离
    大型网站为应对高并发访问,不仅需要在前端实现分布式负载均衡,还需在数据业务和访问层采取有效措施。采用传统的数据结构已无法满足需求,通过配置MySQL主从复制,可实现高效的读写分离,显著提升系统性能和稳定性。 ... [详细]
  • MySQL 数据库索引技术原理初探
    概述什么是索引一本书500页的书,如果没有目录,直接去找某个知识点,可能需要找一会儿,但是借助前面的目录,就可以快速找到对应知识点在书的哪一页。这里的目录就是索引。所以,为什么会有 ... [详细]
author-avatar
李妙妙_minioniu_173
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有