最近在研究分布式ID的生成方法,发现Twitter的snowflake算法挺有意思,因此亲自动手用Java进行了实现。
snowflake算法的原理就是用64位整数来表示主键,其结构如下图:
1 bit符号位:设计者不喜欢负数主键?方便使用负数标识不正确的ID?
41 bit毫秒时间:2^41 / (365 * 24 * 3600 * 1000) ≈ 69年
10 bit机房ID + 机器ID:最大值为1023
12 bit递增序列:最大值为4095
因为使用机房ID + 机器ID来标识机器,因此可以分散到每台业务机器运行而不会产生重复,不需要集中产生主键,这是这个算法最大的优点。
每秒最多可以生成主键数:4096 * 1000毫秒 = 4096000。以当前机器的配置情况和业务情况,单机每秒400万不重复ID无论如何都已经足够。
虽然算法本身很简单,但分布式集群面临的情况很复杂,编码过程中要考虑的因素有很多。废话不多说,“翠花!上代码!”
1.0 分布式时间发生器
1.1 设计考虑
(1) System.currentTimeMillis()方法每次执行都要进行一次系统内核调用,系统开销较大。对于当前的这个序列号生成器来说,只要保证递增序列从4095归0时获取的时间 比 上次归0时获取的时间大就不会产生重复值,因此使用一个long变量缓存了最近一次时间。
(2) 机房ID 和 机器ID正常情况下不会发生改变,因此每次从系统更新时间后立即进行或运算并保存,避免频繁的更新操作。
(3) 配置类AbstractRMConfig 设计成抽象类,用户可自由实现并注册到时间发生器即可。
(4) 为避免业务平静期递增序列长时间无法到达4096,导致缓存时间过旧引发其它问题,因此使用定时线程