雪花算法生成 ID,如何便捷的生成机器 ID

2022-09-26 12:02:00 +08:00
 NoKey

打算用雪花算法生成 ID

这里面有个机器 ID

如果我们部署多个服务的话

在没有注册中心的场景下,怎么能够方便的生成 ID 啊

请大家赐教。

4731 次点击
所在节点    程序员
23 条回复
TsubasaHanekaw
2022-09-26 12:05:07 +08:00
网卡 mac 啊,硬盘序列号啊 这种
qq976739120
2022-09-26 12:08:25 +08:00
用各个硬件的唯一标识号
hzj629206717
2022-09-26 12:11:57 +08:00
也不是很复杂,需要额外引入 Redis 或 MySQL ,每个实例去抢占一个 machine id 并 keep alive 。剩下的就是实现的可靠性了。
changdy
2022-09-26 12:19:49 +08:00
楼上几个 回答是有偏颇的, 如果要做到 服务无感,随时扩容,缩容 ,是需要 一个类似注册中心的 .

我们之前的做法是 固定部署几个实例 ,每个实例的序号就是 机器 id. 当然这也会有问题,

比如项目部署接单会出现 旧的实例没完全下线,新的已经上线了.还有就是无法随时扩容 .

这个其实也是服务治理中的一环了.
wu00
2022-09-26 12:51:41 +08:00
10bit 机器码,便捷就集群内网 IP % 1024 ,量不大没啥问题。
严谨点就按#3 #4 说的注册中心
git00ll
2022-09-26 13:18:08 +08:00
redis 存一个自增的序号
lmshl
2022-09-26 13:44:00 +08:00
UUID v1 满足不了么?

“版本 1 - UUID 是根据时间和 节点 ID (通常是 MAC 地址)生成;” -- 维基百科
wxw752
2022-09-26 13:47:20 +08:00
@lmshl 主要是雪花生成出来的 是纯数字 ID
lmshl
2022-09-26 13:51:33 +08:00
UUID 也是数字啊,一个 128 位大整数

@wxw752
sardina
2022-09-26 14:11:34 +08:00
@lmshl 一个自增 一个不是自增 差太多了
LeegoYih
2022-09-26 14:14:36 +08:00
实际上直接用随机数都行,主键冲突了大不了抛“服务过于繁忙”错误,发生的概率非常小。

雪花算法要保证服务器时间一致,还有时间回拨问题,而且数值还很大,JS 直接用会丢精度。
不如数据库用维护序号,每次获取一批 ID 缓存到客户端。



BSON ObjectID 比 UUID 好用一点
Bromine0x23
2022-09-26 14:40:36 +08:00
说到 UUID 的话,有 V6 V7 两个草案阶段的变种是自增的
Jooooooooo
2022-09-26 14:46:26 +08:00
不要太复杂, 直接用个 mysql 主键维护一下.

每个机器启动的时候都去库里拿一下自己的 id, 如果没有就 insert.

但这里得注意, 如果机器码是三位的, 那集群下机器数量不能超过 1000, 还有废弃机器占用 id 的问题.
wangritian
2022-09-26 15:28:04 +08:00
k8s 有状态应用的 hostname 或 redis 的 incr (程序版本为 key )
changdy
2022-09-26 19:09:24 +08:00
@wu00
也不大行 ,8 个节点的时候 都不重复的概率大概就是 3% 发布 100 次可能就,期望就是 3 了.
ScepterZ
2022-09-26 19:24:45 +08:00
容器编号如何
odirus
2022-09-26 19:33:19 +08:00
我们刚好有这个功能:
1. 提供一个服务端来下发未使用的 workerId ;
2. 客户端获取到 workerId 后定期上报心跳到服务端;
3. 服务端维护 workerId 的心跳信息(并针对异常心跳做断言,提前发现问题),一段时间内没心跳后标记为未使用;

当然还有一些细节:
1. 要考虑某个 Pod 反复重启的情况;
2. 引入命名空间的概念,针对数据不会交叉的场景隔离命名空间,不然 workerId 根本不够用;
3. 我们在 MySQL 还实现了一条有意思的 SQL:通过无锁的方式获取未被使用的 workerId ,返回范围在[0-MAX],不用做取模等;
xuanbg
2022-09-26 20:23:08 +08:00
首选注册中心分配,部署多个服务没有注册中心有些不可思议啊。
zibber
2022-09-26 21:14:53 +08:00
在 etcd 维护一个 key, pod 启动的时候写入 etcd, pod 停止的时候从 etcd 删掉
cp19890714
2022-09-27 09:53:35 +08:00
使用 Redis ,几行代码即可。
```
public class DistributedWorkerIdGenerator {
private static final String ID_WORKER_ID_INCREMENT_KEY = "ID_WORKER_ID_INCREMENT";
private static final int MAX_WORKER_ID = 64;
private static final int EXPIRE_SECONDS = 3600;
private static final String ID_WORKER_ID_PREFIX = "ID_WORKER_ID_";

/**
* 获取新的 workId
* 1.维护自增数字, 数字每次增加 1
* 2.如果 workerId 已经存在, 则循环获取新的 workerId
*
* @param redisTemplate
* @return workId
*/
Long acquireId(RedisTemplate<String, Object> redisTemplate) {
RedisAtomicLong redisAtomicLong = new RedisAtomicLong(ID_WORKER_ID_INCREMENT_KEY, redisTemplate.getConnectionFactory());
Long incrementIndex;
Long workerId;
for (int i = 0; i < MAX_WORKER_ID; i++) {
incrementIndex = redisAtomicLong.getAndIncrement();
workerId = incrementIndex % MAX_WORKER_ID;
if (redisTemplate.opsForValue().setIfAbsent(ID_WORKER_ID_PREFIX + workerId, workerId, EXPIRE_SECONDS, TimeUnit.SECONDS)) {
return workerId;
}
}
throw new ApplicationException("Generate workId failed");
}

/**
* 续签 workerId
* 服务实例需要通过定时任务续签, 定时任务的时间间隔需要小于过期时间
*
* @param redisTemplate
* @param workerId
*/
void renewalId(RedisTemplate<String, Object> redisTemplate, Long workerId) {
redisTemplate.expire(ID_WORKER_ID_PREFIX + workerId, EXPIRE_SECONDS, TimeUnit.SECONDS);
}
```

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

https://www.v2ex.com/t/882977

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

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

© 2021 V2EX