雪花ID

Twitter的分布式雪花算法SnowFlake,经测试每秒能够产生26万个自增可排序ID。

  1. twitter的SnowFlake生成ID能够按照时间有序生成;

  2. SnowFlake算法生成id的结果是一个64bit大小的整数,为一个Long型(转换成字符串后长度为19);

  3. 分布式系统内不会产生ID碰撞(由datacenter和workerId作区分)并且效率较高。

自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景。
GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。

号段解析

  • 1bit: 不用,因为二进制中最高位是符号位,1表示负数,0表示正数,生成的id一般都是用整数,所以最高位固定为0;
  • 41bit-时间戳: 用来记录时间戳,这个时间戳是当前时间和指定时间做差值得到结果,毫秒级;时间范围:(241 - 1)/ (365*24*60*60*1000L) = 67.73年;
  • 10bit-机器码: 用来记录工作机器的id(包含0),最大值为210= 1024
  • 12bit-序列号: 用来记录同一毫秒内产生的不同id(包含0),最大值为212=4096。

单机器生成不碰撞序列的TPS:(212)*1000=409.6万。

SnowFlake可以保证: 所有生成的id按时间趋势递增,整个分布式系统内不会产生重复id。

使用建议

其实雪花算法就是把id按位打散,然后再分成上面这几块,用位来表示状态,这其实就是一种思想。
所以咱们实际在用的时候,也不必非得按照上面这种分割,只需保证总位数在64位即可。

如果你的业务不需要69年这么长,或者需要更长时间。

用42位存储时间戳,(1L << 42) / (1000L * 60 * 60 * 24 * 365) = 139年
用41位存储时间戳,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
用40位存储时间戳,(1L << 40) / (1000L * 60 * 60 * 24 * 365) = 34年
用39位存储时间戳,(1L << 39) / (1000L * 60 * 60 * 24 * 365) = 17年
用38位存储时间戳,(1L << 38) / (1000L * 60 * 60 * 24 * 365) = 8年
用37位存储时间戳,(1L << 37) / (1000L * 60 * 60 * 24 * 365) = 4年

如果你的机器没有那么1024个这么多,或者比1024还多。

用7位存储机器id,(1L << 7) = 128
用8位存储机器id,(1L << 8) = 256
用9位存储机器id,(1L << 9) = 512
用10位存储机器id,(1L << 10) = 1024
用11位存储机器id,(1L << 11) = 2048
用12位存储机器id,(1L << 12) = 4096
用13位存储机器id,(1L << 13) = 8192

如果你的业务,每个机器,每毫秒最多也不会4096个id要生成,或者比这个还多

用8位存储随机序列,(1L << 8) = 256
用9位存储随机序列,(1L << 9) = 512
用10位存储随机序列,(1L << 10) = 1024
用11位存储随机序列,(1L << 11) = 2048
用12位存储随机序列,(1L << 12) = 4096
用13位存储随机序列,(1L << 13) = 8192
用14位存储随机序列,(1L << 14) = 16384
用15位存储随机序列,(1L << 15) = 32768

注意,随机序列建议不要太大,一般业务,每毫秒要是能产生这么多id,建议增加机器,使用机器位。

雪花ID的优缺点

优点

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

缺点

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。可以记录上一次生成的时间戳,如果当前时间戳小于上次生成id的时间戳,则说明系统时钟出现了问题,如果时钟回拨的误差在5毫秒内的情况,在可以容忍的范围,让线程等待偏差的毫秒数,再重新生成,如果还是小于上次,则直接抛出异常,交给业务层处理。

改进

workId需要自身分配,且不同机器不能重复。可以建立workId表,自增主键作为workId。在应用启动时进行初始化,去workId表中查询机器ip对应的workId,不存在首先查找心跳时间在7天前的废弃workId记录,加锁,更新ip进行复用。否则进行初始化,插入一条记录到workId表中。心跳检测使用ScheduledThreadPoolExecutor,1小时上报一次,更新心跳时间。

create table `snowflake_machine_id`(
`id` int(11) unsigned not null auto_increment comment 'id',
`ip` varchar(20) default null comment '机器ip地址',
`heartbeat` datetime default null comment '上次心跳时间',
`ts` timestamp(6) not null default CURRENT_TIMESTSMP(6) ON UPDATE CURRENT_TIMESTAMP(6) comment '时间戳',
primary key ('id'),
unique key 'unique_ip' (`ip`) using btree
) CHARACTER SET utf-8 COLLATE utf8_general_ci comment '雪花ID算法机器ID表'

参考

https://www.zhihu.com/question/447384625

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

posted on 2024-03-06 09:54  zhengbiyu  阅读(18)  评论(0编辑  收藏  举报