雪花算法生成 ID,如何便捷的生成机器 ID - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
NoKey
V2EX    程序员

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

  •  
  •  NoKey 2022-09-26 12:02:00 +08:00 5223 次点击
    这是一个创建于 1121 天前的主题,其中的信息可能已经有所发展或是发生改变。

    打算用雪花算法生成 ID

    这里面有个机器 ID

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

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

    请大家赐教。

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

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

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

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

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

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

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



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

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

    但这里得注意, 如果机器码是三位的, 那集群下机器数量不能超过 1000, 还有废弃机器占用 id 的问题.
    wangritian
        14
    wangritian  
       2022-09-26 15:28:04 +08:00
    k8s 有状态应用的 hostname 或 redis 的 incr (程序版本为 key )
    changdy
        15
    changdy  
       2022-09-26 19:09:24 +08:00
    @wu00
    也不大行 ,8 个节点的时候 都不重复的概率大概就是 3% 发布 100 次可能就,期望就是 3 了.
    ScepterZ
        16
    ScepterZ  
       2022-09-26 19:24:45 +08:00
    容器编号如何
    odirus
        17
    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
        18
    xuanbg  
       2022-09-26 20:23:08 +08:00
    首选注册中心分配,部署多个服务没有注册中心有些不可思议啊。
    zibber
        19
    zibber  
       2022-09-26 21:14:53 +08:00
    在 etcd 维护一个 key, pod 启动的时候写入 etcd, pod 停止的时候从 etcd 删掉
    cp19890714
        20
    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);
    }
    ```
    Marcoo
        21
    Marcoo  
       2022-09-27 16:18:51 +08:00
    我们是使用 Redis ,起一个自增一个 然后检查对应 key (有 5 分钟失效时间)是否存在 如果存在则再自增。
    然后维持好心跳,一分钟发次心跳,续 5 分钟的失效时间
    NoKey
        22
    NoKey  
    OP
       2022-09-28 10:57:58 +08:00
    @Marcoo 自增到 workID 最大值了怎么处理?回头来?或者场景上就不可能达到最大值
    Marcoo
        23
    Marcoo  
       2022-09-29 15:38:06 +08:00
    @NoKey 是的 有个最大值 到最大值了就再从 0 开始 就这样一直循环下去。反正我们的实例数不多 基本上很少会冲突的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5383 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 05:56 PVG 13:56 LAX 22:56 JFK 01:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86