一、背景
分布式系统中数据库中会用到唯一ID
这个唯一ID的需要满足以下特点:
1. 整个系统ID唯一
2. ID是数字类型,而且是趋势递增的
3. ID简短,查询效率快
什么是递增?
如:第一次生成的ID为12,下一次生成的ID是13,再下一次生成的ID是14(或者是第一次ID=12,下一次ID=15,再下一次是20,这叫趋势递增)
二、分布式ID的几种生成方案
2.1、UUID
优点:
1. 代码实现简单。
2. 本机生成,没有性能问题
3. 因为是全球唯一的ID,所以迁移数据容易
缺点:
1. 每次生成的ID是无序的,无法保证趋势递增
2. UUID的字符串存储,查询效率慢
3. 存储空间大
4. ID本事无业务含义,不可读
应用场景:
1. 类似生成token令牌的场景
2. 不适用一些要求有趋势递增的ID场景
2.2、MySQL主键自增
利用MySQL的主键自增auto_increment,默认每次ID加1(步增可配置)。
优点:
1. 数字化,id递增
2. 查询效率高
缺点:
1. 存在单点和高可用高稳定问题,如果mysql挂了,系统就挂了
2. 数据库压力大,高并发抗不住
2.3、MySQL多实例主键自增
这个方案解决mysql了的单点问题,在auto_increment基本上面,设置step步长
每台的初始值分别为1,2,3...N,步长为N(这个案例步长为4)
优点:
解决了单点问题
缺点:
1. 一旦把步长定好后,就无法扩容;
2. 单个数据库的压力大,数据库自身性能无法满足高并发
应用场景:
数据不需要扩容的场景
2.4、雪花snowflake算法
雪花算法生成64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成:
1. 1位标识符:始终是0
2. 41位时间戳:41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截 )得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的
3. 10位机器标识码:可以部署在1024个节点,如果机器分机房(IDC)部署,这10位可以由 5位机房ID + 5位机器ID 组成
4. 12位序列:毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
优点:
1. 此方案每秒能够产生409.6万个ID,性能快
2. 时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增
3. 灵活度高,可以根据业务需求,调整bit位的划分,满足不同的需求
缺点:
依赖机器的时钟,如果服务器时钟回拨,会导致重复ID生成
在分布式场景中,服务器时钟回拨会经常遇到,一般存在10ms之间的回拨
2.5、Redis生成方案
利用redis的incr原子性操作自增,一般算法为:
年份 + 当天距当年第多少天 + 天数 + 小时 + redis自增
优点:
有序递增,可读性强
缺点:
占用带宽,每次要向redis进行请求
整体测试了这个性能如下:
需求:同时10万个请求获取ID
1、并发执行完耗时:9s左右
2、单任务平均耗时:74ms
3、单线程最小耗时:不到1ms
4、单线程最大耗时:4.1s
性能还可以,如果对性能要求不是太高的话,这个方案基本符合要求。但不完全符合业务希望id从 1 开始趋势递增。(当然算法可以调整为 就一个 redis自增,不需要什么年份,多少天等)。
知行研发-刘学千