收藏小程序 IT藏经楼 大量资源免费分享
假设在某些场景下,我们要给数据添加一些唯一的标志符,这个我们可以直接使用数据库的自增主键来实现,但是如果涉及到了分库分表的场景,使用数据库的自增主键就有可能出现主键重复的问题,所以需要有一个分布式的唯一ID生成器来生成全局唯一的ID。
假设系统已经做了分库分表,比如分成了1024张表,如果主键依赖于每一张表的自增主键,不同的表都从1开始累加id,就会导致主键冲突。
而是要专门搞一个库,搞一个表,专门用于生成全局唯一id,insert into插入一条数据,他会返回给你一个全局唯一id,然后你把这个id设置给数据,插入分表后的1024张表里去,保证id是全局唯一的。
优点:超简单,落实起来非常方便,公司有一个统一的库和表,专门用于生成id;或者你自己的系统的库里你专门弄一张表,用来生成id。
缺点:单库单表,并发抗不住,一旦达到每秒几千的高并发;不停的在表里插入数据获取id,表数据会越来越多,还得定期清理,很麻烦。
适用场景:分库分表是因为数据量大,但是低并发低负载,而且数据库单机有高可用问题,必须上高可用方案,另外是单表数据一直增长也是个问题,一般不会直接投入生产,投入生产环境的时候会用下面说的flickr的数据库唯一id生成方案。
优点:本地生成,没有所谓的并发压力。
缺点:太长了!作为主键绝对是不靠谱的!数据库频繁页分裂问题!
适用场景:除数据库主键之外的其他唯一键场景,都适合,这个方案一般不考虑在分布式唯一ID生成里,在我们的主题里,其实可以忽略
核心思想: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算法自研,加入时钟回拨解决方案,多机房方案,等等,各种生产方案,有人力去维护,有少数大厂采用了这个方案,可以作为生产级方案,但是需要解决很多问题。
核心思想: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重复问题,所以最终生产一般不用。
核心思想:比如打车软件,可以用时间戳+起点编号+车牌号作为一个id,业务组合上是不会有重复的,订单id、订单编号;比如电商订单,可以用时间戳+用户id,一个用户在1毫秒内一般最多就下一个订单,一般不会重复,除非用户基于程序刷单,否则手点的情况下,这个组合id一般没问题,还可以加个下单渠道、第一个商品id等其他业务id组合起来
优点:实现简单,没额外成本,没并发之类的扩容问题
缺点:有的业务场景(比如订单之类的),还可以用这种方案,但是有的业务场景可能根本没法通过业务来组合,而且始终担心有重复问题
适用场景:很多大厂都用这个方案,做订单编号这些,但是分库分表不光是订单,还有什么用户、账号以及各种其他的业务场景,所以部分适用于生产,推荐第一优先级先考虑这个方案。
此方案是雅虎公司分享出来的,使用此方案需要先在数据库中创建一个表,存储引擎建议使用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分布式生成系统官方文档:
https://tech.meituan.com/2017/04/21/mt-leaf.html
https://tech.meituan.com/2019/03/07/open-source-project-leaf.html
https://tech.meituan.com/2017/04/21/mt-leaf.html 官方文档中有详细描述
https://tech.meituan.com/2017/04/21/mt-leaf.html 官方文档中有详细描述
有两种方式启动leaf分布式ID生成服务:spring boot start 和leaf server。详细介绍在git中
git地址:https://github.com/Meituan-Dianping/Leaf
联系客服