随着业务量的增加,单机下数据库的自增主键已经不能满足需求.
考虑到以后的扩展性,可靠性以及对程序的易用性,需要新的ID生成方案.
snowflake算法
snowflake是Twitter开源的分布式ID生成算法,整体长度通常是64位,适合使用 Java 中的 Long 类型进行储存.
其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0.
- 标志位: 二进制中最高位为1的都是负数,但是我们生成的id一般都使用正整数,所以这个最高位固定是0
- 41位: 用来记录时间戳
- 41位只做正整数的话可以表示2^41−1个数字
- 从0开始的话可以表示69年的时间,也就是说雪花算法存在一个69年的极限问题
- 10位: 用来记录工作机器ID
- 这里可以表示1024个节点
- 标准定义是5位数据中心+5位机器ID,区分不同集群几点,这里可以根据自己的业务自由调整
- 12位:序列号,用来记录同毫秒内产生的不同id
- 12位可以表示的最大正整数为4095,也就是说同一机器在同一毫秒内可以产生4095个ID
使用及问题
snowflake是一种非常简单实用的方式,具体位数的定义是可以根据自己系统的实际情况来修改,并不需要严格按照标准设计执行.
snowflake满足以下特征:
- id 按时间戳生成,可以保证整体递增状态,方便排序
- 整个系统内的 id 不会重复
使用中可能会遇到的问题:
- 由于过度依赖时间戳,可能发生时钟偏斜问题,如果出现时钟回拨,会造成时间戳不准确,进而产生重复问题.这里可以缓存历史时间戳,在序列生成之前进行校验,如果当前时间落后于历史时间则进行等待.
- 跨毫秒生成ID序列号始终为偶数,这是因为跨毫秒重置序列计数导致,也可以在代码中进行判断调整.
- 69年极限问题,虽然目前系统设计考虑数十年之后太早,可能还是要思考一下,在没有更大数字的情况下,是不是要时间戳减常量并刷历史ID
对系统进行改造后,还要将所有的历史 ID 进行修改,防止碰撞.
