分享大厂分布式唯一ID设计方案,快来围观

点击上方 "程序员小乐"关注, 星标或置顶一起成长

每天凌晨00点00分, 第一时间与你相约

每日英文

In life, then boring time, also are limited edition. Don't waste your new tears for the old sorrow. 

在生命中,再无聊的时光,也都是限量版。所以,不要为旧的悲伤,浪费新的眼泪。

每日掏心

也许不是每天都那么美好,面对烦恼和忧愁一笑而过,是一种平和释然;面对误解和仇恨一笑而过,是一种坦然宽容。

来自:老顾聊技术 | 责编:乐乐

链接:toutiao.com/i6682672464708764174

程序员小乐(ID:study_tech) 第 887 次推文  图源:百度

往日回顾:可怕!竟然有如此沙雕的代码注释!

     

   正文   

前言

上一篇文章中介绍了分布式唯一ID你想了解一线大厂的分布式唯一ID生成方案吗? 留了一个悬念,这里老顾就介绍一下两种大厂的方案思路。希望能够帮到大家。

改造数据库主键自增

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

1、一旦步长定下来,不容易扩容

2、数据库压力山大

我们小伙伴们看看怎么优化这个方案,先看数据库压力大,为什么压力大?是因为我们每次获取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还能维持一段时间。

竞争问题

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

如用户服务A,取到的max_id=1000 ;用户服务B取到的也是max_id=1000,那就出现了问题,Id重复了。那怎么解决?

其实方案很多,加分布式锁,保证同一时刻只有一个用户服务获取max_id。当然也可以用数据库自身的锁去解决。

利用事务方式加行锁,上面的语句,在没有执行完之前,是不允许第二个用户服务请求过来的,第二个请求只能阻塞。

突发阻塞问题

上图中,多个用户服务获取到了各自的ID区间,在高并发场景下,id用的很快,如果3个用户服务在某一时刻都用完了,同时去请求【ID服务】。因为上面提到的竞争问题,所有只有一个用户服务去操作数据库,其他二个会被阻塞。

小伙伴就会问,有这么巧吗?同时id用完。我们这里举的是3个用户服务,感觉概率不大;如果是100个用户服务呢?概率是不是一下子大了。

出现的现象就是一会儿突然系统耗时变长,一会儿好了,就是这个原因导致的,怎么去解决?

双buffer方案

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

在设计的时候,采用双buffer方案,上图的流程:

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

2、当buffer1中的Id已经使用到了100,也就是达到区间的10%

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

程,此线程把获取到的ID,设置到buffer2中。

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

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

6、依次往返

双buffer的方案,小伙伴们有没有感觉很酷,这样就达到了业务场景用的ID,都是在jvm内存中获得的,从此不需要到数据库中获取了。允许数据库宕机时间更长了。

因为会有一个线程,会观察什么时候去自动获取。两个buffer之间自行切换使用。就解决了突发阻塞的问题。

总结

此方案是美团公司使用的分布式ID算法,小伙伴们如果想了解更深,可以去网上搜下,老顾这里应该介绍了比较详细了。

当然此方案美团还做了一些别的优化,监控id使用频率,自动设置步长step,从而达到对id节省使用。

此ID方案非常适合老顾前几篇文章中的id需求如何永不迁移数据和避免热点? 根据服务器指标分配数据量(揭秘篇) 。

但此ID存在一定的问题,就是太过连续,竞争对手可以预测,不适合订单ID。那还有没有别的方案。下次老顾介绍一下美团的另一种生成ID方案。谢谢!!!

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

欢迎各位读者加入订阅号程序员小乐技术群,在后台回复“加群”或者“学习”即可。

猜你还想看

阿里、腾讯、百度、华为、京东最新面试题汇集

基础原理系列:服务端 TCP 连接的 TIME_WAIT 问题

“秒杀” 问题的数据库和 SQL 设计,看了都说好!

SpringBoot 源码分析之 SpringBoot 可执行文件解析

关注订阅号「程序员小乐」,收看更多精彩内容

嘿,你在看吗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值