分布式ID解决方案! 发表于 2025-08-29 更新于 2025-08-29
分布式 分布式 分布式ID解决方案! 月伴飞鱼 2025-08-29 2025-08-29 数据库号段模式
号段模式为从数据库批量的获取自增ID,每次从数据库取出一个号段范围。
例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存。
1 2 3 4 5 6 7 8 CREATE TABLE id_generator ( id int (10 ) NOT NULL , max_id bigint (20 ) NOT NULL COMMENT '当前最大id' , step int (20 ) NOT NULL COMMENT '号段的布长' , biz_type int (20 ) NOT NULL COMMENT '业务类型' , version int (20 ) NOT NULL COMMENT '版本号' , PRIMARY KEY (`id`) )
等这批号段ID用完,再次向数据库申请新号段,对max_id
字段做一次update
操作,update max_id= max_id + step
。
update成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]
。
1 update id_generator set max_id = #{max_id+ step}, version = version + 1 where version = # {version} and biz_type = XXX
由于多业务端可能同时操作,所以采用版本号version
乐观锁方式更新。
这种分布式ID
生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多。
雪花SnowFlake算法
雪花算法生成64位的二进制正整数,然后转换成10进制的数。
64位二进制数由如下部分组成:
1位标识符: 始终是0
41位时间戳 :
存储时间截的差值(当前时间截 - 开始时间截 )得到的值,这里的的开始时间截,一般是ID生成器开始使用的时间,由程序来指定的
10位机器标识码: 可以部署在1024个节点 ,如果机器分机房(IDC)部署 ,这10位可以由 5位机房ID + 5位机器ID 组成
12位序列: 毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
优点:
每秒能够产生409.6万个ID,性能快
时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增
灵活度高,可以根据业务需求,调整bit位的划分,满足不同的需求
缺点:
依赖机器的时钟,如果服务器时钟回拨,会导致重复ID生成
优化思路:
思路就是在系统中监控每台时钟是否回拨 ?如果回拨了,作一下调整就行了。
持久化节点 的作用:就是保存各个ID服务的节点信息,并保存时钟 。
整体流程:
1、ID服务启动,注册到Zookeeper。
2、检查是否之前注册过,如果没有注册过,就把此服务注册到Zookeeper中。
3、节点的信息利用顺序节点,保证唯一。
ID服务把此生成的顺序ID当作workID,保存到本地文件系统中,这样以后再次启动时,就拿着此workId到 zk 上面去查找,是否之前注册过。
4、如果之前没有注册过,生成了ID服务的zk节点,并把本地的时钟保存到此zk节点中。
ID服务会每隔3秒,把本地时钟上报到zk节点上面。
5、如果在注册的时候,发现已经注册了,要去比较一下本地时钟 和 zk 节点上面的时钟。
如果本地时钟 大于 zk 上的,表示正常,启动成功。
否则表示本地的时钟发生了回拨,启动不成功,启动报警机制。
算法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 public class SnowFlakeShortUrl { private final static long START_TIMESTAMP = 1480166465631L ; private final static long SEQUENCE_BIT = 12 ; private final static long MACHINE_BIT = 5 ; private final static long DATA_CENTER_BIT = 5 ; private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT); private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; private long dataCenterId; private long machineId; private long sequence = 0L ; private long lastTimeStamp = -1L ; private long getNextMill () { long mill = getNewTimeStamp(); while (mill <= lastTimeStamp) { mill = getNewTimeStamp(); } return mill; } private long getNewTimeStamp () { return System.currentTimeMillis(); } public SnowFlakeShortUrl (long dataCenterId, long machineId) { if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0 ) { throw new IllegalArgumentException ("DtaCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0!" ); } if (machineId > MAX_MACHINE_NUM || machineId < 0 ) { throw new IllegalArgumentException ("MachineId can't be greater than MAX_MACHINE_NUM or less than 0!" ); } this .dataCenterId = dataCenterId; this .machineId = machineId; } public synchronized long nextId () { long currTimeStamp = getNewTimeStamp(); if (currTimeStamp < lastTimeStamp) { throw new RuntimeException ("Clock moved backwards. Refusing to generate id" ); } if (currTimeStamp == lastTimeStamp) { sequence = (sequence + 1 ) & MAX_SEQUENCE; if (sequence == 0L ) { currTimeStamp = getNextMill(); } } else { sequence = 0L ; } lastTimeStamp = currTimeStamp; return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT | dataCenterId << DATA_CENTER_LEFT | machineId << MACHINE_LEFT | sequence; } public static void main (String[] args) { SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl (2 , 3 ); for (int i = 0 ; i < (1 << 4 ); i++) { System.out.println(snowFlake.nextId()); } } }
百度(uid-generator)
https://github.com/baidu/uid-generator
美团(Leaf)
https://link.zhihu.com/?target=https%3A//github.com/Meituan-Dianping/Leaf
滴滴(Tinyid)
https://github.com/didi/tinyid