分库分表(sharding)后主键全局唯一性的解决方案
随着数据量的增大,在数据库的扩展上通常遇到切分时保证键值的唯一性问题,遇到这种情况,通常有如下几种相对简单的解决方案:
1 UUID 这种方案的优点是实现和管理简单,缺点是占用空间大,查询效率低下。
2 Sequence Number 优点是实现和管理简单,确定是有性能瓶颈和单点问题。
3 不同的集群采用的起始点或者增长间隔不同 这种方案实现简单,但是后期管理麻烦。
除了上述解决方案之外其实还有很多简单可行的办法,但是通用性不太好,在各种解决方案的接触上,本人总结出一个实现和性能上都很好的解决方案,那就是采用时间戳加毫秒数再加随机数来解决,存储字段采用bigint。
下面给出php代码实现:
function ivan_fetch_unique_bigint_id()
{
$start_timestamp = 1238119411;
$ivan_len = 3;
$time = explode( ‘ ‘, microtime());
$id = ($time[1] – $start_timestamp) . sprintf(‘%06u’, substr($time[0], 2, 6));
if ($ivan_len > 0) {
$id .= substr(sprintf(‘%010u’, mt_rand()), 0, $ivan_len);
}
return $id;
}
{
$start_timestamp = 1238119411;
$ivan_len = 3;
$time = explode( ‘ ‘, microtime());
$id = ($time[1] – $start_timestamp) . sprintf(‘%06u’, substr($time[0], 2, 6));
if ($ivan_len > 0) {
$id .= substr(sprintf(‘%010u’, mt_rand()), 0, $ivan_len);
}
return $id;
}
取模测试均分性很好。
–
转载请注明出处,谢谢。
–
Related posts:
高并发下还靠谱不
@神仙
直接插入,如果id重复会抛出exception(因为会模到同一库表中),然后重新生成ID,再插入。不过我想就算高并发,这种碰撞的概率接近于0
其中用了unix的时间戳是32位的,貌似在2037年左右就会失效?到时候,如升级64位时间戳能否保证不发生重复id呢?
@膜拜
我想现在的发展速度,等到2038年的时候,肯定有解决方法的,不用担心。
膜拜讲得不对,这个数生成是这样的
33109660 407978 070
9~10位秒数 6位微妙 3位随机数
UNIX时间戳是(2^32/2)2,147,483,648 ,足够68年的秒数用
而bigint 范围是
-2^63 (-9,223,372,036,854,775,808) 到 2^63-1 (9,223,372,036,854,775,807)
这个 9,223,372,036 足够292年用
非常充足了
这种方案虽然性能好,但是对于多核的机器以及服务器之间,获取Id的操作会在同一时间戳中进行,这时候就需要3位随机数来区分了。一台服务器的CPU核数越多,服务器越多,那么3位随机数重复的概率就越高。已经在一台多核机器上验证,产生将近90万的ID,有400多Id是同一微秒时间戳的Id,靠3位随机数区分,所以还是有一定风险的。
碰撞的概率是非常低的,随机数位数还可以调整。另外如果1微妙有400多的话,那么这个记录量是非常恐怖的,我想世界上还没有哪个系统能有这么大的量,何况还需要在插入的时候做异常处理,这些可以避免和解决的。
可能我表述不清楚,我的意思是在一台机器上并发20,压了1分钟不到,产生了90万的Id,有400条会出现时间戳重复(是分别有重复,不是一个时间戳重复400)。
如果调整随机数位数,比如调整成4位,是可以减低重复概率,但是估计id只能用30多年就会超出19位,也就大于BigInt的范围了。
400条重复数据应该是 同一毫秒的数据是2条记录,然后利用随机数碰撞的概率还是很低的,另外可以做一场处理。以3位随机数为例按照你测试的碰撞概率计算,大概是200万一次的碰撞可能,然后做一次异常处理,再次碰撞的概率就非常低了。随机数位数增加到4这个概率还会更低。
嗯,上面有人提到过,可以使用约70年,其实已经足够了,过度设计并不是好事儿。
@谭俊青
如果是4位随机数,是30多年。不可能70多年。70多年和290多年的是基于3位随机数的。
我验证了在一台机器上,5个并发,跑了几个小时的插入业务,偶尔还是有ID冲突,当然是可以通过异常捕捉重新取Id来尝试。估计服务器越多,这个概率越高,还是要看业务的来取舍,以及加强冲突的尝试才行。
@liangrn
另外要提的是,例中采用的是微妙,是百万分子一秒,不确定碰撞该概率有这么高。还有就是机器多跟这个没有直接关系,最终还是跟同一微妙下的并发多少有关。
还是非常感谢楼主提出的这个思路!:), 如果真的取3位随机数,如果一台机器是8核或16核,然后100台甚至更多这样的服务器,对于频繁写数据的业务,估计重复的几率还是有些高的。
机器多跟这个绝对有关系的,微秒也是时间,多台服务器也是按这个算法去生成Id,重复概率绝对就高了。
我们考虑的角度可能有些偏差,焦点在碰撞的概率上。
如果按照你之前提到的按照毫秒取的话,后面随机数可以取6位。
另外,机器多少没有直接关系指的是,在机器多的时候同一毫秒甚至微妙,在实际业务中碰撞的概率并不是线性的,而且增长缓慢。
建议你采用毫秒+6位随机数或者微妙+3位随机数做下测试,看碰撞概率究竟有多大。
我采用的就是楼主的算法试验的,也是精确度到微秒,不是毫秒。8位毫秒(年月日,减了39年1238119411秒所以8位) + 6位微秒 + 3位随机数。
另外有些业务是会在众多服务器下不断并发插入数据的,所以服务器之间的碰撞概率也需要考虑的。所以我说还是跟业务有关系,如果那些业务并发量少,微秒级已经很少发生重复概率的按问题不大,否则就需要加强冲突重试处理了。
http://code.flickr.com/blog/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/
这个方案比较完美
这个就是中心化的思想,容易出问题,第二点说的就是这个。
我看不懂你这个方法是什么意思。能讲讲吗