深入理解分布式唯一ID:从原理到实战,一篇讲透Snowflake

张开发
2026/4/13 1:14:24 15 分钟阅读

分享文章

深入理解分布式唯一ID:从原理到实战,一篇讲透Snowflake
深入理解分布式唯一ID从原理到实战一篇讲透Snowflake一、为什么我们需要“唯一ID”先从一个最简单的场景说起你有一个订单系统每天产生几百万条订单记录。如果只用数据库的自增主键当系统拆分成多个数据库时ID就会冲突——因为你不能保证两个库的自增序列是独立的。这就是分布式ID要解决的核心问题在分布式环境下生成全局唯一、且尽可能有序的标识符。二、Snowflake的核心原理重点cn.hutool.core.lang.Snowflake生成的唯一性主键是基于Twitter 雪花算法Snowflake的一种 Java 实现。其核心原理是将一个 64 位的 long 型整数划分为多个部分通过组合时间戳、机器标识和序列号来生成一个全局唯一的 ID。雪花算法的 64 位结构Hutool 的Snowflake生成的 ID 是一个 64 位的 long 型数字其二进制位具体分配如下位数长度作用说明第 1 位1 bit符号位固定为 0表示生成的 ID 为正数。接下来 41 位41 bits时间戳记录当前时间与一个自定义起始时间epoch的差值毫秒级。这 41 位可以支撑算法使用69 年。接下来 10 位10 bits机器标识用于区分不同的节点。Hutool 将其拆分为 5 位的数据中心 ID (datacenterId)和 5 位的工作机器 ID (workerId)最多支持部署32 * 32 1024个节点。最后 12 位12 bits序列号在同一毫秒内为同一机器生成的 ID 分配不同的序号。12 位可以生成4096个不同的序列号。ID 生成流程与核心机制Snowflake的工作流程通过其nextId()方法实现该方法使用synchronized关键字保证线程安全。获取当前时间戳获取当前系统的毫秒级时间戳。处理时间回拨如果检测到当前时间小于上一次生成 ID 的时间说明发生了“时钟回拨”。Hutool 的处理策略是容忍 2 秒内的微小回拨通过等待时间来规避若回拨过大超过 2 秒则会直接抛出异常防止 ID 重复。生成序列号如果当前时间戳与上一次相同处于同一毫秒内则让序列号自增sequence 1。如果自增后序列号超过 4096则会等待直到下一毫秒并将序列号重置为 0。如果当前时间戳与上一次不同进入新的一毫秒则直接将序列号重置为 0。拼接最终 ID将计算好的时间戳差值、数据中心 ID、工作机器 ID 和序列号通过左移和按位或|操作拼接成一个 64 位的 long 型整数并返回。唯一性保证该算法能够保证分布式环境下的唯一性主要依赖于以下三点时间戳唯一性确保不同时间生成的 ID 必然不同。机器标识唯一性通过为每个服务节点分配唯一的workerId和datacenterId确保了不同节点生成的 ID 不会冲突。序列号唯一性在同一毫秒内通过自增序列号确保单节点上生成的 ID 不重复。使用示例在 Hutool 中使用IdUtil.getSnowflake(...)可以非常方便地创建Snowflake对象importcn.hutool.core.lang.Snowflake;importcn.hutool.core.util.IdUtil;// 为当前节点分配数据中心ID (0-31) 和 机器ID (0-31)SnowflakesnowflakeIdUtil.getSnowflake(1,1);// 生成一个唯一的 long 类型 IDlongidsnowflake.nextId();System.out.println(id);// 例如1553932736402362368// 生成一个 String 类型的 IDStringidStrsnowflake.nextIdStr();System.out.println(idStr);三、你可能想问的几个问题问题1第一位为什么必须是0因为Java的long是有符号整数第一位是符号位0 正数1 负数谁也不想自己的订单号是个负数吧所以第一位固定为0保证ID永远是正数。问题2我从来没设置过起始时间它从哪来的Hutool给你内置了一个默认值2016-05-20 00:00:00北京时间。算法存储的不是绝对时间而是差值时间戳部分 当前时间 - 起始时间这样做的原因是如果直接存绝对时间戳比如2026-04-10的毫秒数约为17万亿41位根本存不下。存差值的话2^41毫秒 ≈ 69年够用了。问题369年后会发生什么会不会重复这是一个很好的问题。答案是不会重复但会抛异常。因为41位时间戳有上限2^41 - 1 2199023255551毫秒 ≈ 69.7年。当时间差值超过这个上限时算法装不下了会直接抛出异常拒绝生成ID。if(timestampDiffMAX_TIME){thrownewRuntimeException(Clock is beyond the maximum timestamp limit);}至于69年后怎么办说实话能跑69年的系统凤毛麟角。届时要么升级算法要么你早就换工作了四、其他方案的简要对比Snowflake虽然好用但也不是唯一的选择。这里简单列出几种常见方案方案优点缺点适合场景数据库自增简单、有序并发瓶颈、分布式下冲突小项目、单机应用UUID本地生成、全局唯一无序、占空间大客户端生成ID的场景Redis自增性能高引入额外组件已使用Redis的系统Snowflake性能极高、趋势递增依赖时钟、需要分配机器ID互联网高并发系统五、总结回到最初的问题Snowflake是怎么保证唯一性的答案其实很清晰不同时间生成的ID靠时间戳区分同一时间不同机器生成的ID靠机器ID区分同一时间同一机器生成的ID靠序列号区分三个维度叠加理论上每毫秒每台机器可以生成4096个不重复的ID。如果你正在做分布式系统的技术选型Snowflake是一个成熟可靠的选择。Hutool的实现封装得很好几行代码就能用起来性价比非常高。希望这篇文章能帮你彻底理解分布式ID的生成原理。如果你有更多问题欢迎在评论区交流。

更多文章