你刚下的单,客服一查,同一个订单号下面竟然还有别人的订单。订单太多,数据库拆成了好几个,每个都自己编号,就撞了。这一期讲全局唯一 ID。
1. 为什么本机自增会撞
订单太多,数据库拆成了好几个(多个分片)之后,写压力分散了;但各分片若仍用本机自增当订单号,很容易打出同一个值。Azure 架构中心写明:非分片键上的自增字段,不同分片里的两条数据可能被赋成同一个 ID。1
以 MySQL 8.4 为例:
AUTO_INCREMENT给新行生成「唯一身份」——作用域是这一张表、这一台服务器上的序列,不是跨机全局约定。2- InnoDB 每张带自增的表有内存计数器;重启后从数据字典 / redo 恢复;回滚会留下空洞,但仍然只保证本表内不重复。3
auto_increment_increment/auto_increment_offset本意是循环复制(多源写)时错开序列;会影响该服务器上所有自增表,且步长和偏移量要提前统一规划,预留的范围内可以加分片;超出预留范围就得重新规划,扩容要额外协调。4
结论:每个分片各自从本机序列往上加,互不知道对方发到哪——很容易撞号。这不是 MySQL「坏了」,是自增的作用域本来就在单机。
2. 雪花式
Twitter Snowflake(开源归档,约 2010,仅参考)的目标包括:各机不必互相商量再发号(Uncoordinated)、大致按时间有序、尽量紧凑。5 组成直觉:时间戳部分 + 配置好的机器号 + 同一毫秒内的序号。5
打个比方:时间色带 + 站徽章 + 同瞬流水戳。机器之间多数时候不用排队领号。
时钟往回拨时,原版 IdWorker 会拒绝发号(“Clock moved backwards. Refusing to generate id”)——宁可暂停,也不冒险撞号。6 一句话:宁可先停下来,也不打重复条码。
美团 Leaf 的 Snowflake 模式「算法取自 twitter 开源的 snowflake」;另有号段模式(见下)。78 Apache ShardingSphere 也内置 SNOWFLAKE 主键生成器,说明中间件生态常用同类思路(举例,不做推荐)。9
3. 号段
Leaf 号段模式:服务从 DB 一次取走一段号(max_id + step),在内存里往下发;双 Buffer 可在一段用完前异步取下一段,短期可容忍 DB 不可用。78
开源说明对比:号段「低位趋势增长」「能容忍 MySQL 短时间不可用」;Snowflake「完全分布式,ID 有语义」。7
代价要诚实:发号仍依赖(或阶段性依赖)中心号段表——不是「完全去中心」,而是「少打几次 DB」。比喻:一台发号机一次发给每个站一整叠空白号牌,各站自己撕着用。
4. UUID / ULID
RFC 9562(2024-05)把 UUID 定为 128 位,不需要中央权威;动机里写明:分布式场景下协调连续数字编号的成本很容易变成负担。10
- UUIDv4:随机 / 伪随机;适合不想暴露创建时间;但非时间序版本数据库索引局部性差(连续插入在 B-tree 里东插西插)。10
- UUIDv7:Unix 毫秒时间在高位 + 随机/计数;规范建议在可能时优先用 v7 代替 v1 / v6;便于按时间排序、改善索引局部性。10
- ULID(社区规范):128 位;毫秒时间 + 随机;字典序可排序。RFC 9562 动机列表把它列为既有实践之一;若只点名一种「随机全球号」,优先说 UUID,ULID 作对照。1110
UUID 的特点:各机自己生成、更长;随机版索引更碎。
5. Redis INCR
INCR 原子 +1;键不存在则当 0 再加;限 64 位有符号整数。官方文档主讲 counter / rate limiter,不是「分布式主键最佳实践」专文。12
全局唯一且连续的前提是所有人找同一套 Redis 计数——简单,但重新引入单点 / 中心瓶颈。持久化与故障切换若丢计数,有重复风险;无 Redis 官方「用作订单主键」保证,本文不承诺「绝对安全」。简单说:可以用 Redis 这种公共计数器,但大家又都挤回一台发号机了。
6. 方案怎么选(对照)
| 方案 | 优点 | 代价 | 详见 |
|---|---|---|---|
| 本机自增 | 简单 | 分库后会撞号 | §1 |
| 雪花式 | 各机自己发号、大致按时间有序 | 依赖时钟,回拨要停发 | §2 |
| 号段 | 少打几次数据库 | 仍依赖中心号段表 | §3 |
| UUID / ULID | 不需要中心 | 更长,随机版索引更碎 | §4 |
| Redis INCR | 简单、连续 | 又回到单点 | §5 |
数据库拆开之后,主键也要按「全局不撞」来设计。
Footnotes
-
Azure 架构中心,Sharding pattern,https://learn.microsoft.com/en-us/azure/architecture/patterns/sharding ,2026-10-15 抓取 ↩
-
MySQL 8.4 参考手册,5.6.9 Using AUTO_INCREMENT,https://dev.mysql.com/doc/refman/8.4/en/example-auto-increment.html ,2026-10-15 抓取 ↩
-
MySQL 8.4 参考手册,17.6.1.6 AUTO_INCREMENT Handling in InnoDB,https://dev.mysql.com/doc/refman/8.4/en/innodb-auto-increment-handling.html ,2026-10-15 抓取 ↩
-
MySQL 8.4 参考手册,19.1.6.2 Replication Source Options(auto_increment_increment / offset),https://dev.mysql.com/doc/refman/8.4/en/replication-options-source.html ,2026-10-15 抓取 ↩
-
Twitter Snowflake 开源归档 README(snowflake-2010),https://github.com/twitter-archive/snowflake/blob/snowflake-2010/README.mkd ,约 2010 → 仅参考 ↩ ↩2
-
Twitter Snowflake IdWorker.scala,https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala ,约 2010 → 仅参考 ↩
-
美团 Leaf 开源说明,https://tech.meituan.com/2019/03/07/open-source-project-leaf.html ,2019-03-07 → 仅参考 ↩ ↩2 ↩3
-
Meituan-Dianping/Leaf README_CN,https://github.com/Meituan-Dianping/Leaf/blob/master/README_CN.md ,2026-10-15 抓取 ↩ ↩2
-
Apache ShardingSphere,Key Generate Algorithm,https://shardingsphere.apache.org/document/current/en/user-manual/common-config/builtin-algorithm/keygen/ ,current 版,页面无日期 → 仅参考 ↩
-
IETF RFC 9562,Universally Unique IDentifiers (UUIDs),https://www.rfc-editor.org/rfc/rfc9562.html ,2024-05 ↩ ↩2 ↩3 ↩4
-
ULID 规范仓库 README,https://github.com/ulid/spec/blob/master/README.md ,2026-10-15 抓取(社区规范,页面无 IETF 发布日) ↩
-
Redis 文档,INCR,https://redis.io/docs/latest/commands/incr/ ,2026-10-15 抓取 ↩
幕后
这部片子完全由代码生成。
系列 · 分布式第 1/2 期