打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
常见的分布式ID生成方式

收藏小程序 IT藏经楼  大量资源免费分享

一、分布式唯一ID生成器用于什么场景

假设在某些场景下,我们要给数据添加一些唯一的标志符,这个我们可以直接使用数据库的自增主键来实现,但是如果涉及到了分库分表的场景,使用数据库的自增主键就有可能出现主键重复的问题,所以需要有一个分布式的唯一ID生成器来生成全局唯一的ID。

二、几种常见的分布式唯一ID生成方案的优缺点对比

1. 数据库自增主键

假设系统已经做了分库分表,比如分成了1024张表,如果主键依赖于每一张表的自增主键,不同的表都从1开始累加id,就会导致主键冲突。

而是要专门搞一个库,搞一个表,专门用于生成全局唯一id,insert into插入一条数据,他会返回给你一个全局唯一id,然后你把这个id设置给数据,插入分表后的1024张表里去,保证id是全局唯一的。

优点:超简单,落实起来非常方便,公司有一个统一的库和表,专门用于生成id;或者你自己的系统的库里你专门弄一张表,用来生成id。

缺点:单库单表,并发抗不住,一旦达到每秒几千的高并发;不停的在表里插入数据获取id,表数据会越来越多,还得定期清理,很麻烦。

适用场景:分库分表是因为数据量大,但是低并发低负载,而且数据库单机有高可用问题,必须上高可用方案,另外是单表数据一直增长也是个问题,一般不会直接投入生产,投入生产环境的时候会用下面说的flickr的数据库唯一id生成方案。

2. UUID

优点:本地生成,没有所谓的并发压力。

缺点:太长了!作为主键绝对是不靠谱的!数据库频繁页分裂问题!

适用场景:除数据库主键之外的其他唯一键场景,都适合,这个方案一般不考虑在分布式唯一ID生成里,在我们的主题里,其实可以忽略

3. Twitter开源的Snowflake方案

核心思想:64个bit位,最高位1个bit是0,41位放时间戳(到毫秒单位,最多使用69年),10位放机器标识(最多把snowflake程序部署在1024台机器上),12位放序号(每毫秒,每台机器,可以顺序生成4096个ID)

64个bit位,通过时间戳+机器id+序号 -> long类型的唯一id

snowflake程序分布式部署在多台机器上,每台机器生成的每个ID,都是这一毫秒、机器id、序号,每台机器每毫秒最多4096个ID,绝对够用了,分布式方案可以抗高并发,大不了加机器,最多1024台机器,纯基于内存生成,性能很高。

优点:高性能,高并发,分布式,可伸缩,最多扩展1024台机器,ID绝对够用。

缺点:光是开源算法还不用,还得考虑时钟回拨等一系列问题,如果要解决那堆问题,需要开发很多机制,开发完了还得独立部署,有独立部署和维护的成本。

适用场景:中大型公司,有高并发生成唯一ID场景,基于snowflake算法自研,加入时钟回拨解决方案,多机房方案,等等,各种生产方案,有人力去维护,有少数大厂采用了这个方案,可以作为生产级方案,但是需要解决很多问题。

4. Redis自增机制

核心思想:Redis单线程,通过incrby来实现有序自增;redis单机就可以抗住很高的并发,如果想要抗住更高的并发,也可以草图采用集群部署,比如部署5台机器,那么每台机器的初始值依次为1、2、3、4、5,每台机器的自增步长是5,第1台机器就是1、6、11、16、21,第2台机器就是2、7、12、17、22,以此类推,直到第5台机器就是5、10、15、20、25。要注意的是每台机器的步长一定要和集群中机器的个数保持一致,这样才能保证生成的ID不会重复。

优点:不用额外开发,一般公司都提供redis集群,直接用就行,高性能,高并发,集群化,高可用,都可以实现,全局唯一。

缺点:客户端需要自己封装,基于Jedis去封装,客户端里需要写死Redis机器数量,每次获取1个ID,都是找到一台机器,然后按步长去incrby,接着返回给系统;而且扩容麻烦,如果5台机器抗不住并发了怎么办?扩容的时候加机器,客户端需要修改代码,或者基于动态感知,这其实也有开发成本,另外扩容的时候,步长就会改变,那之前的ID怎么办?都得重新洗掉,全部从头开始计算,极为麻烦。

适用场景:鉴于他的缺点,一般不用redis集群玩自增主键生成;分库分表了,然后每秒在万左右的高并发,但是可预见的不会达到几万以及十万级的并发,那么此时可以用Redis单机去生成自增主键,避免redis集群扩容的步长改变问题;但是还得部署Redis主从同步+哨兵高可用,可是主从同步是异步的,有id重复问题,所以最终生产一般不用。

5. 基于时间+业务id的组合

核心思想:比如打车软件,可以用时间戳+起点编号+车牌号作为一个id,业务组合上是不会有重复的,订单id、订单编号;比如电商订单,可以用时间戳+用户id,一个用户在1毫秒内一般最多就下一个订单,一般不会重复,除非用户基于程序刷单,否则手点的情况下,这个组合id一般没问题,还可以加个下单渠道、第一个商品id等其他业务id组合起来

优点:实现简单,没额外成本,没并发之类的扩容问题

缺点:有的业务场景(比如订单之类的),还可以用这种方案,但是有的业务场景可能根本没法通过业务来组合,而且始终担心有重复问题

适用场景:很多大厂都用这个方案,做订单编号这些,但是分库分表不光是订单,还有什么用户、账号以及各种其他的业务场景,所以部分适用于生产,推荐第一优先级先考虑这个方案。

6. flickr(雅虎旗下的图片分享平台)公司的方案

此方案是雅虎公司分享出来的,使用此方案需要先在数据库中创建一个表,存储引擎建议使用MyISAM

CREATE TABLE `id_generator` (    `id` bigint(20) unsigned NOT NULL auto_increment,    `stub` char(1) NOT NULL default '',    PRIMARY KEY  (`id`),    UNIQUE KEY `stub` (`stub`)  ) ENGINE=MyISAM;

当要生成一个ID的时候,执行下面的sql语句:

REPLACE INTO id_generator(stub) VALUES ('a');  SELECT LAST_INSERT_ID(); 

此语法是mysql特有的语法,replace into语法替代insert into,数据库中只会有一行数据,每次执行该语句的时候,这条数据的ID字段就会自增,避免表行数过大,一张表就一行数据,然后再select获取这个表的最新id,last_insert_id()函数是connection级别的,就你这个连接的最近insert生成的id,多个客户端之间没影响。

当然,其实也可以优化成这样,就是每次你一台机器要申请一个唯一id,你就REPLACE INTO uid_sequence (stub) VALUES ('ip地址' + 'order_id'),用你自己机器的ip地址去replace into,那么就你自己机器会有id不停自增,完了用select id from table where stub=机器地址就可以了。

这个方案本质跟第一个方案没区别,唯一优化就是用replace into替代了insert into,避免表数据量过大,缺点也在于数据库并发能力不高,所以适用场景,就是分库分表的时候,低并发,用这个方案生成唯一id,低并发场景下可以用于生产。

而且一般会部署数据库高可用方案,MySQL双机高可用方案,两个库设置不同的起始位置和步长,分别是1、3、5,以及2、4、6。

三、美团Leaf方案

1. 美团Leaf方案的利弊

美团Leaf分布式生成系统官方文档:

https://tech.meituan.com/2017/04/21/mt-leaf.html

https://tech.meituan.com/2019/03/07/open-source-project-leaf.html

2. Leaf-Segment数据库号段方案

https://tech.meituan.com/2017/04/21/mt-leaf.html 官方文档中有详细描述

3. Leaf-Snowflake方案

https://tech.meituan.com/2017/04/21/mt-leaf.html 官方文档中有详细描述

4. 美团开源项目Leaf的使用以及源码

有两种方式启动leaf分布式ID生成服务:spring boot start 和leaf server。详细介绍在git中

git地址:https://github.com/Meituan-Dianping/Leaf

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
分布式系统ID生成办法
分布式唯一id:snowflake算法思考
一线大厂的分布式唯一ID生成方案是什么样的?
3个必知必会分布式幂等方案!
不重复的id生成策略
再谈分布式ID生成方案
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服