摘要:对支持很好,分表后无需考虑全局的问题。但是这个项目使用的是进行开发,必须自己生成全局。语句的是为了保证并发环境下的值只增不减。每次生成全局前,先检测指定的是否存在。代码如下另外对于全局的生成,和也都公布了自己的方案。
最近一个项目由于数据量变大,需要进行数据分表。数据存储在淘宝的tddl上。分表后,原先的自增id就不能使用了。tddl对java支持很好,分表后无需考虑全局id的问题。但是这个项目使用的是php进行开发,必须自己生成全局id。以下列出几种分表方案,仅当抛砖引玉。
1:使用CAS(compare and swap)其实这里并不是严格的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`) ) E
那么更新语句是。
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); } }
另外对于全局id的生成,Flicker和Twitter也都公布了自己的方案。感兴趣的人,可以了解下。
http://my.oschina.net/u/142836/blog/174465
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/21267.html
摘要:开篇金币积分商城下称商城是众多内的一个产品,随着使用的用户越来越多,商城对于用户留存的提升,扮演着重要的角色做为提高用户黏性的核心产品,在拥有很好用户体验的同时,也必须存在着一个高效稳定的系统。分析上述两点,得到结论按用户进行分库分表。 开篇 金币(积分)商城(下称商城)是众多App内的一个产品,随着App使用的用户越来越多,商城对于用户留存的提升,扮演着重要的角色;做为提高用户黏性的...
摘要:缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。这是缓存最原始的意义,同时也引申出了缓存最普遍的用法。但是现实中还有一种缓存,是主动更新的。 缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过memcached类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。 说起...
摘要:缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。这是缓存最原始的意义,同时也引申出了缓存最普遍的用法。但是现实中还有一种缓存,是主动更新的。 缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过memcached类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。 说起...
阅读 1608·2021-09-26 09:55
阅读 1346·2021-09-23 11:22
阅读 2674·2021-09-06 15:02
阅读 2580·2021-09-01 11:43
阅读 3890·2021-08-27 13:10
阅读 3641·2021-08-12 13:24
阅读 2035·2019-08-30 12:56
阅读 2967·2019-08-30 11:22