分布式系统生成全局唯一 ID 的方式请教

2023-07-25 14:53:59 +08:00
 jiobanma

咨询各位大佬们一个问题目前有两台服务器负载,使用 apache 的 SnowflakeShardingKeyGenerator 生成雪花算法作为 id ,业务上需要生成的 id 是递增的。 之前两台服务器的 SnowflakeShardingKeyGenerator 的 workId 都是默认的,高并发情况下,两台服务器的时间可能会有误差 就会导致生成的 id 是重复的。但是两台服务器根据不同的 workId 去生成虽然能解决重复的问题,但是会导致生成的 id 不是连续递增的。 有什么其他的方式实现吗(排过坑的[旺柴])。

9642 次点击
所在节点    Java
114 条回复
wxd21020
2023-07-26 08:42:46 +08:00
借楼,请教大佬们,如何生成一个 10-13 位的唯一 id ,目前是通过雪花算法生成 id 后,将 id 顺序调转,然后一直循环除以 10 ,最终的到一个 10-13 位的数字,经过简单的并发测试都能是唯一的,不知道时间长了会不会有不唯一的值。
msg7086
2023-07-26 08:48:49 +08:00
全局层面上做成递增,那势必一台机器要依赖另一台机器产生的 ID ,那就没办法做并发。
jiobanma
2023-07-26 09:02:48 +08:00
@xiangyuecn #56 认真审题好吗? 分配唯一编号现在会出现不是递增的。
jiobanma
2023-07-26 09:09:49 +08:00
@sampeng #58 append 给出解释了刚才
jiobanma
2023-07-26 09:13:02 +08:00
@JohnChang #71 同志你好!
wqhui
2023-07-26 09:25:55 +08:00
@wxd21020 雪花是时间戳+机器唯一标识+序列号,同一节点同一时间戳生成时序列号会递增,同一节点的情况下需要截取时间戳跟序列号才能保证唯一,你这种截取方式并发高点就冲突了
MonkeyJon
2023-07-26 09:26:49 +08:00
@xrzxrzxrz #61 我们系统订单 id 跟你这个实现类似
knightgao2
2023-07-26 09:42:39 +08:00
雪花算法多台机器也可以递增呀,只要机器的时钟对的就行
zpf124
2023-07-26 09:52:59 +08:00
非要保证递增,那就只能使用锁或者单独的 id 生成服务,以并发的代价来保证 id 生成同一时间唯一。
lmmlwen
2023-07-26 09:56:15 +08:00
你们业务并发多大啊?
KickAssTonight
2023-07-26 09:57:43 +08:00
两个机器时钟不一致,那应该必然会出现不递增的情况吧
leonshaw
2023-07-26 10:00:00 +08:00
问题的核心就是为什么需要递增,被你一句话带过去了?
vanityfairn
2023-07-26 10:13:38 +08:00
专门的服务批量生成 ID ,最通行,最常用
zhh0000zhh
2023-07-26 10:22:23 +08:00
有一次面阿里的时候面试题就是要求实现分布式单调递增主键生成,必须没有仲裁来实现,我不会,被阿里面试官鄙视了,最后我问他,他没告我咋弄。
mark 一下看看有没有人能搞出来,我至今觉得面试官实际上是个糊涂蛋
xiangyuecn
2023-07-26 10:26:47 +08:00
@jiobanma #84 我在#56 讲的是“ id 是重复的”这个问题

只要你单个节点里面,时间不倒流,雪花算法是不可能产生重复 id 的,每个节点都要有唯一编号,最多 1024 个节点,这个是保证全局不重复的基础。

雪花算法的时间精度是 1 毫秒,单个节点 1 毫秒内最多产生 4096 个 id (会加锁串行生成),单节点只要 1 毫秒内超过了 4096 个就会 sleep 等待下 1 毫秒再分配 id ,你开了 2 个节点,那就 1 毫秒能产生 4096*2=8192 个 id ,1 毫秒里面这些 id 只能通过节点 id 来区分保证不重复

单个节点内生成的 id 因为是串行生成的因此是完全有序的,但同 1 毫秒内多个服务器生成的 id 就不能保证有序了,1 秒内看这 1000 毫秒生成的 id 又是有序的,这叫时间上粗略有序

“id 碰撞” “重复” 每个节点分配了唯一编号,只要你不乱改服务器时间时间怎么可能出现这种情况
gundam0603
2023-07-26 10:40:23 +08:00
雪花的 id 本来就不是连续的啊 ,按理说改下 workId 就行
Aresxue
2023-07-26 10:56:54 +08:00
1.改下算法把 workId 放到 id 尾部(不依赖外部服务和中间件);
2.使用 redis 自增序列(大多数项目已经引入 redis ,只有代码改造成本);
3.号段 使用序列表按照 size 取号段到内存(更稳健的模式但改造成本大,且只有趋势递增失去了单调递增性);
4.引入新服务(对原有服务耦合小,新增永远比修改更安全,但维护成本拉满);
5.使用数据库代理层中间件(如果已经有了算是性价比最高的方案,不然就没有操作意义);

顺便一提如果集群大了时钟同步是个麻烦的事情,基本上总是会有时钟回拨,对雪花算法有一定的影响,但本身也有一定措施缓解这个情况,如使用缓存的 id 等,还是要早做考虑。
cheng6563
2023-07-26 11:02:46 +08:00
没得治,找个单独的组件生成 ID ,并且还得牺牲可用性。
iamfenges
2023-07-26 11:14:41 +08:00
workId 放 id 的最后面会有什么问题吗
daye
2023-07-26 11:15:32 +08:00
把大家的回复都看了,提供一个新思路给 OP ,可以通过雪花算法 + Redis 的 increment 命令来实现,能保证在两台服务器生成的 ID 不重复、绝对的递增,那么 ID 里的时间戳值就不是真实的生成时间(雪花 id 优点之一)

基本原理是:
1. 判断 Redis 的 ID_KEY 是否存在,若不存在,则加分布式锁通过雪花算法生成 ID 设值
2. 对 ID_KEY 进行 increment 命令获取递增后的值作为 ID

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/959560

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX