一线大厂的分布式 ID 生成方案是什么样的?

1 前言

分布式系统中我们会对遇到数据量较大的业务表进行拆分,如:订单表、用户表,但是有时又需要保证id需要在整个系统保证唯一,这个时候就需要使用到分布式id。

2 分布式id需要满足什么条件才是符合要求的

分布式id生成方案需要保证满足一下条件才算是合理的方案

  • 整个系统唯一
  • id是数字类型,并且在一定范围是趋于增长趋势
  • id简短,查询效率快

3 分布式id的几种方案

3.1 UUID

优点:

  • 代码简单
  • 本机生成,没有性能问题,不需要引入任何中间件
  • 全球问一,迁移数据容易

缺点:

  • 每次UUID都是无序的,无法保证趋势递增
  • UUID的字符存储太长,查询效率慢
  • ID无业务含义,不可读

应用场景:

  • 类似生成token令牌的场景
  • 不适合一些要求有趋势递增的ID场景
3.2 数据库自增主键

这个方案是利用了数据库自增auto_increament,默认每次都是ID加1.

优点:

  • 数字化,id递增
  • 查询效率高
  • 具有一定的业务可读

缺点:

  • 存在单点问题,如果mysql挂了,就没有生成id了
  • 数据库在高并发时存在瓶颈,读写压力比较大
3.3 数据库多实例

此方案是来解决数据库单点问题,在auto_increment基本上面,设置step步长

在这里插入图片描述

每台的初始值分别为1,2,3....N,步长为N(这个案例为3)

优点:

  • 解决了单点问题

缺点:

  • 一旦把步长定好之后,就无法扩容;并且单个数据库压力比较大,数据库自身性能无法满足

应用场景:

  • 数据不需要扩容的场景
3.4 雪花算法

雪花算法生成64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成:

[图片上传失败...(image-dac169-1603986434274)]

  • 1位标识符:始终是0
  • 41为时间戳:41位时间戳不是存储当前时间的时间戳,而是存储时间戳的差值(当前时间戳-开始时间戳)得到的值,这里的开始时间戳,一般是我们的id生成器开始使用的时间,由我们程序来指定的。
  • 10位机器标识码:可以部署在1024个节点,如果机器分机房(IDC)部署,这10位可以由 5位机房ID + 5位机器ID 组成
  • 12位序列:毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

优点:

  • 此方案每秒能够产生409.6万个ID,性能快
  • 时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增
  • 灵活度高,可以根据业务需求,调整bit位的划分,满足不同的需求

缺点:

  • 依赖机器的时钟,如果服务器时钟回拨,会导致重复id生成

在分布式场景中,服务器时钟回拨会经常遇到,一般存在10ms之间的回拨;小伙伴们就说这点10ms,很短可以不考虑吧。但此算法就是建立在毫秒级别的生成方案,一旦回拨,就很有可能存在重复ID。

3.5 redis生成方案

利用redis的incr原子操作性自增,一般算法为:

年份 + 当天距当年第多少天 + 天数 + 小时 + redis自增

优点:

  • 有序递增,可读性强

缺点:

  • 占用带宽,每次都要向redis进行请求

性能还行,但是在高并发情况下还需要调用redis发送网络请求(虽然redis性能很高,但是也是存在瓶颈的。)

3.6 zookeeper生成方案

3.7 小结

常见的分布式id生成方案各有优缺点,一线大厂的分布式id方案绝对不会这么简单,他们对高可用,高并发要求比较高。

类似redis方案中,每次都要去redis去请求,有网络请求耗时,并发强依赖redis。而且这个设计有风险,一旦redis挂了,整个系统不可用。

而且一线大厂也会考虑到ID安全性的问题,如:Redis方案中,用户是可以预测下一个ID号是多少,因为算法是递增的。

4 一线大厂是如何设计的?

4.1 改造数据库主键自增

利用数据库的自增主键的特性,可以实现分布式ID;这个ID比较简短明了,适合做userId,正好符合如何永不迁移数据和避免热点? 根据服务器指标分配数据量(揭秘篇)文章中的ID的需求。但这个方案有严重的问题:

  • 一旦步长定下来,不容易扩容
  • 数据库压力山大

数据库压力大,为什么压力大?是因为每次获取id的时候,都要去数据库请求一次。要避免每次都去数据库获取。

思路我们可以请求数据库得到ID的时候,可设计成获得的ID是一个ID区间段。

在这里插入图片描述

有张ID规则表:

  1. id表示为主键,无业务含义。
  2. biz_tag为了表示业务,因为整体系统中会有很多业务需要生成ID,这样可以共用一张表维护
  3. max_id表示现在整体系统中已经分配的最大ID
  4. desc描述
  5. update_time表示每次取的ID时间

整体流程:

1、【用户服务】在注册一个用户时,需要一个用户ID;会请求【生成ID服务(是独立的应用)】的接口

2、【生成ID服务】会去查询数据库,找到user_tag的id,现在的max_id为0,step=1000

3、【生成ID服务】把max_id和step返回给【用户服务】;并且把max_id更新为max_id = max_id + step,即更新为1000

4、【用户服务】获得max_id=0,step=1000;

5、 这个用户服务可以用ID=【max_id + 1,max_id+step】区间的ID,即为【1,1000】

6、【用户服务】会把这个区间保存到jvm中

7、【用户服务】需要用到ID的时候,在区间【1,1000】中依次获取id,可采用AtomicLong中的getAndIncrement方法。

8、如果把区间的值用完了,再去请求【生产ID服务】接口,获取到max_id为1000,即可以用【max_id + 1,max_id+step】区间的ID,即为【1001,2000】

这个方案就非常完美的解决了数据库自增的问题,而且可以自行定义max_id的起点,和step步长,非常方便扩容。

而且也解决了数据库压力的问题,因为在一段区间内,是在jvm内存中获取的,而不需要每次请求数据库。即使数据库宕机了,系统也不受影响,ID还能维持一段时间

4.2 竞争问题

4.1方案中,如果是多个用户服务,同时获取ID,同时去请求【ID服务】,在获取max_id的时候会存在并发问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJIwMrm2-1603985220294)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201029231044207.png)]

可以通过分布式锁或者数据库自身锁方式来解决这个问题

4.3 突发阻塞问题
在这里插入图片描述

多个用户服务同时获得了各自的ID区间,高并发情况下Id用的很快。如果采用分布式锁或者数据库锁方式来控制的话,就会出现有一个操作数据库,其他二个会被阻塞。

如果采用出现的现象就是一会儿突然系统耗时变长,一会儿好了,就是这个原因导致的。

4.4 双buffer方案(美团leaf)

在一般的系统设计中,双buffer会经常看到,怎么去解决上面的问题也可以采用双buffer方案。


在这里插入图片描述

1、当前获取ID在buffer1中,每次获取ID在buffer1中获取

2、当buffer1中的Id已经达到区间的10%

3、达到了10%,先判断buffer2中有没有去获取过,如果没有就立即发起请求获取ID线程,此线程把获取到的ID,设置到buffer2中。

4、如果buffer1用完了,会自动切换到buffer2

5、buffer2用到10%了,也会启动线程再次获取,设置到buffer1中

6、依次往返

双buffer的方案,达到了业务场景用的ID,都是在jvm内存中获得的,从此不需要到数据库中获取了。允许数据库宕机时间更长了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容