资讯专栏INFORMATION COLUMN

数据库分表后,并发环境下,生成全局id生成的几种方式

testbird / 512人阅读

摘要:对支持很好,分表后无需考虑全局的问题。但是这个项目使用的是进行开发,必须自己生成全局。语句的是为了保证并发环境下的值只增不减。每次生成全局前,先检测指定的是否存在。代码如下另外对于全局的生成,和也都公布了自己的方案。

最近一个项目由于数据量变大,需要进行数据分表。数据存储在淘宝的tddl上。分表后,原先的自增id就不能使用了。tddl对java支持很好,分表后无需考虑全局id的问题。但是这个项目使用的是php进行开发,必须自己生成全局id。以下列出几种分表方案,仅当抛砖引玉。

1:使用CAS(compare and swap)

其实这里并不是严格的CAS,而是使用了比较交换原子操作的思想。
生成思路如下:每次生成全局id时,先从sequence表中获取当前的全局最大id。然后在获取的全局id上做加1操作。把加1后的值更新到数据库。更新时是关键。
如加1后的值为203,表名是users,数据表结构如下:

</>复制代码

  1. CREATE TABLE `SEQUENCE` (
  2. `name` varchar(30) NOT NULL COMMENT "分表的表名",
  3. `gid` bigint(20) NOT NULL COMMENT "最大全局id",
  4. PRIMARY KEY (`name`)
  5. ) E

那么更新语句是。
update sequence set gid = 203 where name = "users" and gid < 203;
sql语句的 and gid < 203 是为了保证并发环境下gid的值只增不减。
如果update语句的影响记录条数为0说明,已经有其他进程提前生成了203这个值,并写入了数据库。需要重复以上步骤从新生成。
代码实现如下:

</>复制代码

  1. //$name 表名
  2. function next_id_db($name){
  3. //获取数据库全局sequence对象
  4. $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();
  5. $threshold = 100; //最大尝试次数
  6. for($i = 0; $i < $threshold; $i++){
  7. $last_id = $seq_dao->get_seq_id($name);//从数据库获取全局id
  8. $id = $last_id +1;
  9. $ret = $seq_dao->set_seq_id($name, $id);
  10. if($ret){
  11. return $id;
  12. break;
  13. }
  14. }
  15. return false;
  16. }
2:使用全局锁

在进行并发编程时,一般都会使用锁机制。其实,全局id的生成也是解决并发问题。
生成思路如下:
在使用redis的setnx方法和memcace的add方法时,如果指定的key已经存在,则返回false。利用这个特性,实现全局锁。
每次生成全局id前,先检测指定的key是否存在。
如果不存在则使用redis的incr方法或者memcache的increment进行加1操作。这两个方法的返回值是加1后的值。
如果存在,则程序进入循环等待状态。循环过程中不断检测key是否还存在,如果key不存在就执行上面的操作。
代码如下:

</>复制代码

  1. //使用redis实现
  2. //$name 为 逻辑表名
  3. function next_id_redis($name){
  4. $redis = Wk_Redis_Util::getRedis();//获取redis对象
  5. $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();//获取存储全局id数据表对象
  6. if(!is_object($redis)){
  7. throw new Exception("fail to create redis object");
  8. }
  9. $max_times = 10; //最大执行次数 避免redis不可用的时候 进入死循环
  10. while(1){
  11. $i++;
  12. //检测key是否存在,相当于检测锁是否存在
  13. $ret = $redis->setnx("sequence_{$name}_flag",time());
  14. if($ret){
  15. break;
  16. }
  17. if($i > $max_times){
  18. break;
  19. }
  20. $time = $redis->get("sequence_{$name}_flag");
  21. if(is_numeric($time) && time() - $time > 1){//如果循环等待时间大于1秒,则不再等待。
  22. break;
  23. }
  24. }
  25. $id = $redis->incr("sequence_{$name}");
  26. //如果操作失败,则从sequence表中获取全局id并加载到redis
  27. if (intval($id) === 1 or $id === false) {
  28. $last_id = $seq_dao->get_seq_id($name);//从数据库获取全局id
  29. if(!is_numeric($last_id)){
  30. throw new Exception("fail to get id from db");
  31. }
  32. $ret = $redis->set("sequence_{$name}",$last_id);
  33. if($ret == false){
  34. throw new Exception("fail to set redis key [ sequence_{$name} ]");
  35. }
  36. $id = $redis->incr("sequence_{$name}");
  37. if(!is_numeric($id)){
  38. throw new Exception("fail to incr redis key [ sequence_{$name} ]");
  39. }
  40. }
  41. $seq_dao->set_seq_id($name, $id);//把生成的全局id写入数据表sequence
  42. $redis->delete("sequence_{$name}_flag");//删除key,相当于释放锁
  43. $db = null;
  44. return $id;
  45. }
3:redis和db结合

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

</>复制代码

  1. function next_id($name){
  2. try{
  3. return $this->next_id_redis($name);
  4. }
  5. catch(Exception $e){
  6. return $this->next_id_db($name);
  7. }
  8. }

另外对于全局id的生成,Flicker和Twitter也都公布了自己的方案。感兴趣的人,可以了解下。

http://my.oschina.net/u/142836/blog/174465

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21267.html

相关文章

  • 金币(积分)商城架构漫谈

    摘要:开篇金币积分商城下称商城是众多内的一个产品,随着使用的用户越来越多,商城对于用户留存的提升,扮演着重要的角色做为提高用户黏性的核心产品,在拥有很好用户体验的同时,也必须存在着一个高效稳定的系统。分析上述两点,得到结论按用户进行分库分表。 开篇 金币(积分)商城(下称商城)是众多App内的一个产品,随着App使用的用户越来越多,商城对于用户留存的提升,扮演着重要的角色;做为提高用户黏性的...

    Ethan815 评论0 收藏0
  • 谈谈服务端缓存几种用法

    摘要:缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。这是缓存最原始的意义,同时也引申出了缓存最普遍的用法。但是现实中还有一种缓存,是主动更新的。 缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过memcached类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。 说起...

    CocoaChina 评论0 收藏0
  • 谈谈服务端缓存几种用法

    摘要:缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。这是缓存最原始的意义,同时也引申出了缓存最普遍的用法。但是现实中还有一种缓存,是主动更新的。 缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过memcached类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。 说起...

    tianhang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<