分布式主键生成

分布式主键生成

背景:

互联网行业里面,新开一个项目基本上都是需要分库分表的,再差也要先分个表;那么id就需要全局唯一,下面介绍几种ID生成方案。


常见方法介绍:

snowflake(雪花算法):

Long型 64Bit位:1bit保留+41bit时间戳+10bit进程id+12bit序列号。 41-bit的时间可以表示(1L<<41)/(1000L360024*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的
  • 可以根据自身业务特性分配bit位,非常灵活

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态

实现:

sharding-jdbc内置了雪花算法的实现,个人觉得还不错,可以翻翻看里面的代码 ####数据库生成 通过设置不同的业务(biz_tag),每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段

biz_tag step maxId
activity 1000 1000

重要字段说明:biz_tag用来区分业务,max_id表示该biz_tag目前所被分配的ID号段的最大值,step表示每次分配的号段长度。 优点:

  • ID号码是趋势递增的8byte的64位数字,满足数据库存储的主键要求
  • 容灾性高:有号码缓存,即使db宕机了,短时间内仍然可以提供附服务
  • 可以自定义max_id的大小,非常方便业务从原有的ID方式上迁移过来

缺点:

  • TP999数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,tg999数据会出现偶尔的尖刺
  • ID号码不够随机,能够泄露发号数量的信息,不太安全
  • DB宕机会造成整个系统不可用 #####双buffer优化: 对于第一个缺点:主要是内存中的号码段用完了再去DB取,这时候会出现短暂的堵塞。如果请求DB的网络和DB的性能稳定,这种情况对系统的影响是不大的,但是假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢 为此,我们希望DB取号段的过程能够做到无阻塞,不需要在DB取号段的时候阻塞请求线程,即当号段消费到某个点时就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系统的TP999指标。详细实现如下图所示:

采用双buffer的方式,服务内部有两个号段缓存区segment。当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段。当前号段全部下发完后,如果下个号段准备好了则切换到下个号段为当前segment接着下发,循环往复。

猜你喜欢

转载自juejin.im/post/5b7abd8151882542f037fc62