前言
为了标识请求的唯一性,项目中往往采用雪花算法生成全局ID,但运行过程中偶现主键冲突情况,分析结果为雪花算法生成ID相同。
知识介绍与拓展
1、首先分析ID生成需要具备的特性:
唯一性 | 不管是单机还是分布式场景下,生成的ID必须全局唯一 |
时序性 | 生成的ID与时间戳相关联,有利于分析和回溯,了解ID的生成时间 |
递增性 | ID一般作为数据库主键,保持递增趋势入库,能够提高B+树索引结构 节点插入性能 |
低延迟 | 生成ID延迟低,效率高 |
高QPS | 能够兼容大量并发请求,保证以上特性 |
2、现有常用的全局唯一ID方法包括:
算法 | 优点 | 缺点 |
UUID | 生成效率高,能够保证全局唯一 | ID无序,且与时间戳无关,入库性能差 |
MySQL自增主键 | ID递增,全局唯一 | 不适用于分布式场景,效率低,加表锁(锁不是在每次事务完成后释放,而是在完成对自增长值插入的SQL语句后释放,要等待其释放才能进行后续操作。) |
雪花算法 | 与时间戳有关,递增生成 | 存在ID相同的情况;依赖机器时钟, |
问题分析
项目初始化雪花算法时,workId和dataId相同,因此同一时间戳可能会产生相同的ID;
解决方法
暂只讨论同一个微服务,从服务之间、服务之内两个角度提出解决方案(服务容器化部署):
- 不同pod使用不同的workId和dataId,初始化雪花算法;
- 相同pod获取雪花ID的静态方法增加单机锁(synchronized)
雪花算法ID重复的分析与在项目中的解决_雪花算法 重复-CSDN博客
不同pod之间如何获取不同的初始化信息?
- 每个服务启动时,根据workId的特性,从0到31依次判断redis中是否存在该值,若无,则写入当前微服务信息,唯一标识一个workId;
- 每个服务运行过程中,每隔一段时间,为自己所占有的workId续时,避免被其他服务占用;
- 服务重启或挂掉时,workId自动过期,即可被新服务占用;
可优化点
synchronize加在静态方法上,锁粒度太大,可根据不同的业务场景细化锁粒度