V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jiobanma
V2EX  ›  Java

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

  •  
  •   jiobanma ·
    banmajio · 2023-07-25 14:53:59 +08:00 · 9045 次点击
    这是一个创建于 485 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    第 1 条附言  ·  2023-07-26 09:09:07 +08:00
    解释一下为什么要求递增,已经目前的现状
    1. 历史原因,开发初期的同事没有考虑到负载已经后续并发的问题。项目最初的时候是单节点的所以还不会出现什么明显的问题。加上代码中存在大量的根据雪花算法生成的 id 排序比大小的操作。而目前换成了负载,两个节点轮训分担压力。就会导致两个节点生成的 id 会有碰撞的可能。别问为啥一定要求递增,问就是不想改动别人的之前写的逻辑,给自己挖坑。
    2. 出现重复的原因,a 用户的请求分发到了 A 节点,此时 b 用户的请求分发到了 B 节点,两个节点同时生成的 id 就出现碰撞的问题。

    3. 还有说使用有问题的各位,op 确实对雪花算法不太熟悉,没那么深入的了解,所以才发这个问题请教,抱着学习的太多。麻烦各位嘴下留情!
    114 条回复    2023-07-26 20:53:40 +08:00
    1  2  
    my3157
        1
    my3157  
       2023-07-25 14:57:16 +08:00   ❤️ 7
    用一个专门的服务批量生成 ID , 其他服务直接从这个服务拿
    mineralsalt
        2
    mineralsalt  
       2023-07-25 14:57:28 +08:00
    没用过 SnowflakeShardingKeyGenerator, 但是如果是我实现这样的需求, 能想到 2 种办法, 第一就是 redis 加锁, 但是会影响并发效率. 第二种就是发现 id 重复时递归生成一次,直到不重复为止
    mineralsalt
        3
    mineralsalt  
       2023-07-25 14:59:29 +08:00
    1L 这种多引入一个服务感觉太重了, 多一个服务就多一份负担和不稳定性, 不是太优雅
    thevita
        4
    thevita  
       2023-07-25 15:25:10 +08:00
    2L 提的第一个方法与 1L 本质上是一样的,都是引入一个全局一致的协调者(目前看这其实是比较现实的办法,功能简单,稳定性和性能应该能做很多优化,当然具体看你场景能否接受)
    thevita
        5
    thevita  
       2023-07-25 15:29:04 +08:00
    2L 第二种也是一个办法,相当于 用 db 作为一致性保证?,乐观冲突检测的方式来做,但是 db 的事务的貌似还是得依赖全局锁的方式来支持 insert id 有序
    zhzy0077
        6
    zhzy0077  
       2023-07-25 15:29:11 +08:00
    2 台服务器能承载的业务直接用数据库的 auto increment 不行吗
    CocaCola001
        7
    CocaCola001  
       2023-07-25 15:31:19 +08:00
    我觉得还是新建一个服务用来生成 id 比较好,后期也方便扩展
    thevita
        8
    thevita  
       2023-07-25 15:31:30 +08:00
    还不如看看你的需求只是要 “递增“ 呢,还是真的需要严格的全局有序
    SenLief
        9
    SenLief  
       2023-07-25 15:32:42 +08:00   ❤️ 3
    有没有考虑过最简单的方案,使用一个大数,一台往上递增,一台往下递增
    luciankaltz
        10
    luciankaltz  
       2023-07-25 15:34:57 +08:00   ❤️ 1
    > 两台服务器的时间可能会有误差 就会导致生成的 id 是重复的

    snowflake 算法初始化的时候会填入一个 machine id ,为什么两台服务器上生成出来的 id 会重复呢(
    还是说你故意设置成了一样的 machine id ,是希望所有生成出来的 id 在全局上单调递增并且唯一?

    如果是的话那就必须引入一个单点的算号服务,不管是一个 app 服务还是 db 的自增
    veike
        11
    veike  
       2023-07-25 15:35:33 +08:00
    ulid 可行?
    Masoud2023
        12
    Masoud2023  
       2023-07-25 15:36:40 +08:00   ❤️ 2
    想了半天为什么这东西能重复,一仔细看帖子原来没改 workerId...

    你如果非想要连续递增的 id ,那么只能考虑做个服务专门搞这种自增

    是不是某些架构设计出问题了,感觉不应该有这种问题..
    thevita
        13
    thevita  
       2023-07-25 15:36:49 +08:00
    想到一个比较破的方案:

    就是你在 db 里面 预先 “生成, 分配” 一批 id (假设这里你的 全局有序 id 是主键)

    这样就能让服务来`抢` next id, 对 行加锁了,并发应该会好一些,

    就是不能回滚,需要让签名的 id 失效
    jiobanma
        14
    jiobanma  
    OP
       2023-07-25 15:38:27 +08:00
    @mineralsalt #2
    @thevita #4
    @CocaCola001 #7 这个比较简单 缺点就是可能会有性能问题已经服务掉线之后的问题
    @luciankaltz #10 目前都是默认的 没有传参,所以你说的 machine id 应该是个默认值,两台服务器应该是一样的值
    @veike #11 uuid 缺点太多了
    nekolr
        15
    nekolr  
       2023-07-25 15:40:06 +08:00
    大致上是趋势递增的就可以了呀,或者像楼上几位说的,独立出来一个服务
    dw2693734d
        16
    dw2693734d  
       2023-07-25 15:41:20 +08:00
    @jiobanma 能讲讲 uuid 缺点吗
    bugmakerxs
        17
    bugmakerxs  
       2023-07-25 15:41:57 +08:00
    https://github.com/Meituan-Dianping/Leaf/blob/master/README_CN.md
    为什么要连续?简单点引入这个就行
    nekolr
        18
    nekolr  
       2023-07-25 15:43:10 +08:00
    @nekolr 没注意看楼主要求,楼主想要的是严格的单调递增是吗?如果是用雪花的话,那只能独立出来一个服务
    silentsky
        19
    silentsky  
       2023-07-25 15:43:11 +08:00
    很简单的事情 搜 Redisson IdGenerator
    bugmakerxs
        20
    bugmakerxs  
       2023-07-25 15:44:27 +08:00
    另外的简单方案是 timestamp + (redis.incr(key) % 100000)
    服务器时钟用 ntp 同步
    tabris17
        21
    tabris17  
       2023-07-25 15:44:34 +08:00
    又要严格自增,又要分布式,你这是既要又要啊
    veike
        22
    veike  
       2023-07-25 15:44:46 +08:00
    @jiobanma 我说的 ulid ,不是 uuid ,不是一个东西
    ns09005264
        23
    ns09005264  
       2023-07-25 15:45:48 +08:00 via Android   ❤️ 5
    把 workid 放到最后试试,比如服务 1 生成序列号 123777701 服务 2 同一时间生成 123777702 ,这样不管从全局来看还是单个服务来看,id 都是连续的
    nekolr
        24
    nekolr  
       2023-07-25 15:46:29 +08:00
    @dw2693734d 1 太长 2 可能的安全性问题 3 不是递增的,会导致数据存储不够紧凑,不能充分利用 B+ 树的优势
    Belmode
        25
    Belmode  
       2023-07-25 15:56:54 +08:00
    修改一下 workerId ,你这问题不是迎刃而解了
    jiobanma
        26
    jiobanma  
    OP
       2023-07-25 16:01:20 +08:00
    @Belmode #25 workId 影响的是中间的某几位,修改没法解决呀
    @bugmakerxs #17 这个我了解下
    crysislinux
        27
    crysislinux  
       2023-07-25 16:01:30 +08:00 via Android
    @jiobanma machine id 如果都一样,那还设计这个位干嘛呢。
    opengps
        28
    opengps  
       2023-07-25 16:04:50 +08:00   ❤️ 1
    “ id 是递增”天然的设计优势就是自增步长设置为 2 啊,一台 1 作为种子,一台 2 作为种子
    yangyaofei
        29
    yangyaofei  
       2023-07-25 16:05:05 +08:00
    给机器分配 ID(0 到 N 的自然数)不就好了, 比如 10 台, 就找个 N=11, 每个机器自己生成的时候乘以 11 加上自己的 ID 就好了, N 可以有个比较大的余量, 虽然很工程但是感觉应该挺好用
    qingshengwen
        30
    qingshengwen  
       2023-07-25 16:06:11 +08:00
    没太明白,时间戳是在最前面的,这样一定是可以做到大致递增的,跟 workId 有什么关系呢
    fuis
        31
    fuis  
       2023-07-25 16:06:15 +08:00
    为什么需要严格的单调递增?
    icedir
        32
    icedir  
       2023-07-25 16:07:24 +08:00
    了解一下美团的 leaf ,解决了这个场景
    cat
        33
    cat  
       2023-07-25 16:14:25 +08:00
    或许可以看看这个:
    Sharding & IDs at Instagram
    https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c
    leonshaw
        34
    leonshaw  
       2023-07-25 16:18:13 +08:00
    这种需求不管怎么折腾都等效于一个单点
    taevas
        35
    taevas  
       2023-07-25 16:20:18 +08:00
    @fuis 可读性呀,不必时间排序,仅通过主键可知时序
    bringyou
        36
    bringyou  
       2023-07-25 16:25:41 +08:00   ❤️ 1
    美团开源过分布式 ID 的生成服务,并有写文章介绍,可以参考: https://tech.meituan.com/2019/03/07/open-source-project-leaf.html
    xiaoHuaJia
        37
    xiaoHuaJia  
       2023-07-25 16:26:56 +08:00
    uuid 重写加入时间,可以实现是有排序的。目前我们系统的方案就是这个。除了 id 看起没没纯数字那么规整,差一点点性能,其它都是小问题
    yc8332
        38
    yc8332  
       2023-07-25 16:32:12 +08:00
    写个服务预先生成好,放 redis 或者自己的服务。然后用的直接取就行了。。
    yaodong0126
        39
    yaodong0126  
       2023-07-25 16:34:46 +08:00
    @mineralsalt 我倒是觉得 1L 非常优雅
    PythonYXY
        40
    PythonYXY  
       2023-07-25 16:36:55 +08:00
    需要递增就没法采用分布式的方式,无论是 snowflake 还是基于数据库自增 id ,最后肯定还是会有单点问题。
    dddd1919
        41
    dddd1919  
       2023-07-25 16:39:43 +08:00
    所有服务连同一个 redis ,使用同一个 key 的 incr 生成 id ,既能自增又分布式
    mineralsalt
        42
    mineralsalt  
       2023-07-25 16:51:20 +08:00
    @yaodong0126 #39 多运行一个服务和使用 redis 加锁没有本质区别, 一般后端项目都会引入 redis, 所以几乎是没有额外成本的. 另外我简单说一下多运行一个服务的缺点.
    1. 浪费内存, java 后端项目启动就几百兆
    2. 稳定性降低, 本来他是分布式部署的项目强行被这个单点服务拖累了, 一旦这个服务挂掉, 其他服务都 GG
    3. 开发任务多了, 多维护一个模块, 也要多上线一个实例

    当然这是一个好的解决方案, 但是一个小项目, 我还是秉承着资源最小化的理念, 不要过多的引入服务和模块
    wendellup2018
        43
    wendellup2018  
       2023-07-25 17:03:25 +08:00
    @mineralsalt 还是有区别的, 每个项目都引入 redis, 连接数要爆掉了。引入项目可以增加网关控制调用。
    quantal
        44
    quantal  
       2023-07-25 17:04:14 +08:00
    直接用 ULID 吧,生成的 id 带时间戳精确到毫秒,毫秒级有序
    luciankaltz
        45
    luciankaltz  
       2023-07-25 17:06:21 +08:00   ❤️ 1
    @dddd1919 热点 key 警告(
    IDAEngine
        46
    IDAEngine  
       2023-07-25 17:48:08 +08:00
    @jiobanma 那直接用比特币钱包地址生成算法
    jiobanma
        47
    jiobanma  
    OP
       2023-07-25 17:49:46 +08:00
    @luciankaltz #45 了解的有点少,想问下热点 key 可能会导致哪些问题
    jiobanma
        48
    jiobanma  
    OP
       2023-07-25 18:02:38 +08:00
    @tabris17 #21 “自增” 和 “ 递增” 是有区别的,注意看
    jiobanma
        49
    jiobanma  
    OP
       2023-07-25 18:07:05 +08:00
    @qingshengwen #30
    public static Comparable<?> geneSnowFlakeIDByWorkId(String workId) {
    shardingKeyGenerator.getProperties().setProperty("worker.id", workId);
    return shardingKeyGenerator.generateKey();
    }


    public static void main(String[] args) {
    System.out.println(geneSnowFlakeIDByWorkId("100")); // 890660284086501376
    System.out.println(geneSnowFlakeIDByWorkId("200")); // 890660284095299585
    System.out.println(geneSnowFlakeIDByWorkId("100")); // 890660284094889986
    System.out.println(geneSnowFlakeIDByWorkId("200")); // 890660284095299587
    }

    这样是不就理解了
    wangxiaoaer
        50
    wangxiaoaer  
       2023-07-25 18:07:51 +08:00 via iPhone
    也没有大佬解释下 1 楼的方案?目前两台雪花算法在高并发下会重复,说明业务并发还是不低的,再加一个专门生成 id 的服务,是不是还是调用雪花服务器?如果这样的话这个服务就会面临瓶颈,服务如果再通过集群搞成分布式,感觉会面临同样的问题啊。
    joesonw
        51
    joesonw  
       2023-07-25 18:23:24 +08:00 via iPhone
    通过一个中心服务器拿自增号码段

    https://cloud.tencent.com/developer/article/1352896
    lovelylain
        52
    lovelylain  
       2023-07-25 18:23:26 +08:00 via Android
    @jiobanma 自己实现,时间戳放最高位,自增数放中间,workid 放最低位
    hsymlg
        53
    hsymlg  
       2023-07-25 18:42:09 +08:00
    单调递增和趋势递增是 2 个概念,1L 其实就是解决方案,再起一个服务专门做 id 生成,另外其他楼说的美团的 leaf ,那个玩意儿的基于号段的和雪花的也是 2 种不同场景,要注意甄别
    IDAEngine
        54
    IDAEngine  
       2023-07-25 18:49:25 +08:00
    @wangxiaoaer 他这个两台服务器的 machine id 用的同一个,当然有重复的可能性。
    luciankaltz
        55
    luciankaltz  
       2023-07-25 19:04:01 +08:00 via Android
    @jiobanma 想象一下你对一张表的数据行做 10 次 update 。如果是 10 条记录,那么这些操作之间不会有并发锁问题;如果 10 次更新的是一条记录,那么 10 次操作会退化成串行执行

    热点问题会直接导致各种分片或者分布策略失效,大多数情况下直接退化到单机性能(甚至可能影响到其他的操作
    xiangyuecn
        56
    xiangyuecn  
       2023-07-25 19:28:58 +08:00   ❤️ 2
    一言难尽
    说不懂吧,又用上了雪花 id 算法
    说懂吧,又不给服务器分配唯一编号🐶
    leonshaw
        57
    leonshaw  
       2023-07-25 19:37:32 +08:00
    不知道你业务顺序是怎么定义的?
    两个不相关的客户端几乎同时发送的请求本身就是没有顺序的,网络随便抖一下到服务器的顺序就变了,更不要说还有相对论了。技术上讲,事件先后只有在同一个点比较才有意义,你分 ID 的时候就是要保证和这个顺序一致。比如来自同一个连接的请求在同一个线程处理。
    sampeng
        58
    sampeng  
       2023-07-25 19:54:17 +08:00 via iPhone
    lz 始终没解释为啥一定要递增。
    产品需求?只有疯了的产品才关注这个。leader 的需求?那是技术不太行…
    dayudayupao
        59
    dayudayupao  
       2023-07-25 19:57:58 +08:00
    @veike 万万不可,会降低 b 树的效率,降低 pagecache 的利用率
    chenluo0429
        60
    chenluo0429  
       2023-07-25 20:21:18 +08:00 via Android   ❤️ 1
    1. 给定正确的 workerId ,
    2. 雪花算法中将代表时间的段放在高位
    xrzxrzxrz
        61
    xrzxrzxrz  
       2023-07-25 20:29:38 +08:00   ❤️ 1
    跟 23L 说的一样。把 workId 放在 ID 的尾部,时间日期放在前面,这样两个机器生成出来的 id 就是近似连续递增的(当然没法做到绝对递增,要绝对,只能有一个中间服务处理)。例如,{yyyyymmdd} + {workId} + {Hms}。(自己写个雪花算法原理的 id 生成器)
    veike
        62
    veike  
       2023-07-25 20:31:52 +08:00 via Android
    @dayudayupao 能见降低多少啊
    Macrow
        63
    Macrow  
       2023-07-25 21:05:42 +08:00
    https://github.com/rs/xid
    零配置,可排序

    Features:

    Size: 12 bytes (96 bits), smaller than UUID, larger than snowflake
    Base32 hex encoded by default (20 chars when transported as printable string, still sortable)
    Non configured, you don't need set a unique machine and/or data center id
    K-ordered
    Embedded time with 1 second precision
    Unicity guaranteed for 16,777,216 (24 bits) unique ids per second and per host/process
    Lock-free (i.e.: unlike UUIDv1 and v2)

    Name Binary Size String Size Features
    UUID 16 bytes 36 chars configuration free, not sortable
    shortuuid 16 bytes 22 chars configuration free, not sortable
    Snowflake 8 bytes up to 20 chars needs machine/DC configuration, needs central server, sortable
    MongoID 12 bytes 24 chars configuration free, sortable
    xid 12 bytes 20 chars configuration free, sortable
    iwdmb
        64
    iwdmb  
       2023-07-25 21:11:53 +08:00
    如果没有中心化服务的话
    没有这种可以严格递增的方法
    不然 Google 就不用搞原子钟了
    iwdmb
        65
    iwdmb  
       2023-07-25 21:17:16 +08:00   ❤️ 1
    如果你能找到这样一个不依赖中心化服务、原子钟 GPS 硬体的方法
    去面试 Google 一定会上
    可以帮他们数据中心节省大量的成本
    iwdmb
        66
    iwdmb  
       2023-07-25 21:18:06 +08:00
    美团的 Leaf 只有保证整体趋势递增 不保证严格递增
    UUID 有重复的可能 所以也不是严格递增
    iwdmb
        67
    iwdmb  
       2023-07-25 21:24:32 +08:00
    分布式严格递增
    这个问题目前最简单、经济、性能最好的解法就是中心化服务
    信我一把
    mrjnamei
        68
    mrjnamei  
       2023-07-25 21:56:54 +08:00
    你服务是分布式的,要求每个服务生成的 id 必须是全局自增,因此必须引入一个新的组件来通信,不然无法得知当前服务器生成的 id 是否是递增的?

    如果需要引入一个组件就比较好办了,高并发情况考虑高可用就行,高并发是指特别高的并发,假设服务 idGen 服务已经满足高可用的前提下,生成自增 id 显然不是问题,不管使用 redis 、还是自身内存都可以。

    你要解决的问题就是高可用。
    cosmic
        69
    cosmic  
       2023-07-25 22:49:48 +08:00
    用 etcd cluster 实现一个全局唯一且连续递增的 sequence generator,确保 consistency 和 availability. 然后服务器每次请求获取一个 batch 的 id,然后服务器每次 assign 一个,当一个 batch 用完后,向 sequence generator service 请求下一个 batch
    tangtj
        70
    tangtj  
       2023-07-25 22:53:04 +08:00
    雪花算法是有预留,数据中心 id, 机器 id 的位置. 你没改那就需要调整配置.使他们不一样. 这样就不会出现完全一样的.
    JohnChang
        71
    JohnChang  
       2023-07-25 23:09:43 +08:00
    V 站到底多少人用这个头像啊?我怀疑是不是都抄我的
    walkerliu
        72
    walkerliu  
       2023-07-25 23:21:23 +08:00
    什么业务哇需要这么精确严格的递增吗,snowflakeID 前 41 个 bit 是 timestamp ,都能精确到毫秒了 。我觉得不如倒退需求是否需要那么精确的递增
    conn457567
        73
    conn457567  
       2023-07-25 23:23:04 +08:00 via Android
    雪花算法的前提条件就是每个实例的 workerid 不能相同啊,可以用分布式协调中间件 zookeper 给每个实例分唯一 id ,或者简单点用 redis 来实现给每个实例分配唯一 id
    L0L
        74
    L0L  
       2023-07-25 23:26:22 +08:00
    高并发的场景下,雪花的递增不太可靠吧?最好说一下一定要递增的业务场景把,看看能不能换个角度解决这个问题?既然上了分布式 ID 了,尽量就避免用其作为业务含义上排序的用途吧,看看是否可以用时间戳之类的。否则 1 楼说的那个需要单独启动一个序列号服务,感觉如果只有两台服务器的话,代价反而有点高,不经济。
    jdOY
        75
    jdOY  
       2023-07-26 02:12:59 +08:00
    就两台服务器搞这么复杂,服务器做一下时间同步,改一下生成雪花的几个参数就行了
    ryd994
        76
    ryd994  
       2023-07-26 04:00:28 +08:00 via Android
    snowflak 本来就不保证是完全单调递增,而是保证 ID 大体上是单调连续递增,但是允许小规模,短时间的无序
    xuanbg
        77
    xuanbg  
       2023-07-26 06:25:39 +08:00
    分布式系统你是很难保证严格按照时间同步递增的,保证单机单调递增就够了。
    franklu
        78
    franklu  
       2023-07-26 08:06:21 +08:00
    我想问一下,在分布式下,你们怎么测试确保 id 是不重复的,要是有成熟测试方案,我觉得写代码会更简单。
    franklu
        79
    franklu  
       2023-07-26 08:07:05 +08:00
    还是说只能用思维模型测试,只能理论上推理出没问题。
    trzzzz
        80
    trzzzz  
       2023-07-26 08:34:09 +08:00 via iPhone
    为啥不用数据库的自增 id 呢
    wxd21020
        81
    wxd21020  
       2023-07-26 08:42:46 +08:00
    借楼,请教大佬们,如何生成一个 10-13 位的唯一 id ,目前是通过雪花算法生成 id 后,将 id 顺序调转,然后一直循环除以 10 ,最终的到一个 10-13 位的数字,经过简单的并发测试都能是唯一的,不知道时间长了会不会有不唯一的值。
    msg7086
        82
    msg7086  
       2023-07-26 08:48:49 +08:00
    全局层面上做成递增,那势必一台机器要依赖另一台机器产生的 ID ,那就没办法做并发。
    jiobanma
        83
    jiobanma  
    OP
       2023-07-26 09:02:48 +08:00
    @xiangyuecn #56 认真审题好吗? 分配唯一编号现在会出现不是递增的。
    jiobanma
        84
    jiobanma  
    OP
       2023-07-26 09:09:49 +08:00
    @sampeng #58 append 给出解释了刚才
    jiobanma
        85
    jiobanma  
    OP
       2023-07-26 09:13:02 +08:00
    @JohnChang #71 同志你好!
    wqhui
        86
    wqhui  
       2023-07-26 09:25:55 +08:00
    @wxd21020 雪花是时间戳+机器唯一标识+序列号,同一节点同一时间戳生成时序列号会递增,同一节点的情况下需要截取时间戳跟序列号才能保证唯一,你这种截取方式并发高点就冲突了
    MonkeyJon
        87
    MonkeyJon  
       2023-07-26 09:26:49 +08:00
    @xrzxrzxrz #61 我们系统订单 id 跟你这个实现类似
    knightgao2
        88
    knightgao2  
       2023-07-26 09:42:39 +08:00
    雪花算法多台机器也可以递增呀,只要机器的时钟对的就行
    zpf124
        89
    zpf124  
       2023-07-26 09:52:59 +08:00
    非要保证递增,那就只能使用锁或者单独的 id 生成服务,以并发的代价来保证 id 生成同一时间唯一。
    lmmlwen
        90
    lmmlwen  
       2023-07-26 09:56:15 +08:00
    你们业务并发多大啊?
    KickAssTonight
        91
    KickAssTonight  
       2023-07-26 09:57:43 +08:00
    两个机器时钟不一致,那应该必然会出现不递增的情况吧
    leonshaw
        92
    leonshaw  
       2023-07-26 10:00:00 +08:00
    问题的核心就是为什么需要递增,被你一句话带过去了?
    vanityfairn
        93
    vanityfairn  
       2023-07-26 10:13:38 +08:00
    专门的服务批量生成 ID ,最通行,最常用
    zhh0000zhh
        94
    zhh0000zhh  
       2023-07-26 10:22:23 +08:00
    有一次面阿里的时候面试题就是要求实现分布式单调递增主键生成,必须没有仲裁来实现,我不会,被阿里面试官鄙视了,最后我问他,他没告我咋弄。
    mark 一下看看有没有人能搞出来,我至今觉得面试官实际上是个糊涂蛋
    xiangyuecn
        95
    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
        96
    gundam0603  
       2023-07-26 10:40:23 +08:00
    雪花的 id 本来就不是连续的啊 ,按理说改下 workId 就行
    Aresxue
        97
    Aresxue  
       2023-07-26 10:56:54 +08:00
    1.改下算法把 workId 放到 id 尾部(不依赖外部服务和中间件);
    2.使用 redis 自增序列(大多数项目已经引入 redis ,只有代码改造成本);
    3.号段 使用序列表按照 size 取号段到内存(更稳健的模式但改造成本大,且只有趋势递增失去了单调递增性);
    4.引入新服务(对原有服务耦合小,新增永远比修改更安全,但维护成本拉满);
    5.使用数据库代理层中间件(如果已经有了算是性价比最高的方案,不然就没有操作意义);

    顺便一提如果集群大了时钟同步是个麻烦的事情,基本上总是会有时钟回拨,对雪花算法有一定的影响,但本身也有一定措施缓解这个情况,如使用缓存的 id 等,还是要早做考虑。
    cheng6563
        98
    cheng6563  
       2023-07-26 11:02:46 +08:00
    没得治,找个单独的组件生成 ID ,并且还得牺牲可用性。
    iamfenges
        99
    iamfenges  
       2023-07-26 11:14:41 +08:00
    workId 放 id 的最后面会有什么问题吗
    daye
        100
    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
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3394 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 10:59 · PVG 18:59 · LAX 02:59 · JFK 05:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.