推荐号段预分配结合snowflake变种结构:用id_generator表托管biz_tag、max_id、step,通过原子update获取号段,应用内存缓存并动态分配machine_id,id结构为32bit时间戳+10bit machine_id+12bit序列号,兼顾全局唯一、趋势递增与容器化适配。

分库分表后,MySQL 原生 AUTO_INCREMENT 失去全局意义——每个库/表独立递增,必然重复。真正能落地的方案,得兼顾唯一性、性能、可运维性,而不是只看理论是否“听起来正确”。
推荐主线:号段预分配 + Snowflake 变种结构
这不是纯算法或纯数据库方案,而是折中实践最优解:
- 用一张轻量发号表(如
id_generator)托管业务维度的 ID 号段,字段含biz_tag、max_id、step - 通过
UPDATE ... WHERE max_id = ?原子更新获取新号段(严禁先 SELECT 再 UPDATE) - 应用层内存缓存已领取的号段,用尽再取;进程重启后主动拉取,避免 ID 回退
- 生成的 ID 推荐结构:32bit 时间戳 + 10bit machine_id + 12bit 序列号(共 54bit,留余量)
-
machine_id不硬绑物理机,从配置中心或服务注册信息动态读取,适配容器/K8s 环境
慎选但可用的替代方案
没有银弹,只有权衡:
-
UUID v7:前 48bit 是毫秒时间戳,天然有序,插入 B+ 树友好;62bit 随机位保障碰撞概率极低(万亿年量级);比 UUID v4 少 33%~65% 插入耗时,索引体积小 1.8 倍;缺点是 128bit,需用
UUID_TO_BIN()存为BINARY(16),且不能直接当聚簇主键 -
Redis 号段模式:用
INCRBY或 Lua 脚本批量取号,适合中小并发;注意 Redis 故障时降级策略(如切本地缓存+告警),避免雪崩 -
数据库自增 + offset/step:仅限分表数固定、几乎不扩容的场景;例如 10 张表,每张设
AUTO_INCREMENT = i*10 + 1、auto_increment_increment = 10;一旦加表,必须人工重置所有表起始值,风险高
必须避开的坑
这些看似简单,实则高频踩雷:
- 直接用
UNIX_TIMESTAMP()或毫秒时间戳拼接——高并发下毫秒内请求超千级就冲突 - 纯 Snowflake 依赖固定
worker_id——K8s Pod 重建、弹性伸缩时 ID 重复率陡增 - 用
UUID()字符串做主键——36 字节、无序、索引膨胀、JOIN 性能差 - 发号表没加唯一索引或乐观锁机制——并发 UPDATE 导致号段重叠、ID 重复
- 忽略时钟回拨——哪怕 NTP 同步延迟几毫秒,或云主机休眠唤醒,都可能触发 ID 乱序甚至重复;需在生成逻辑中加入回拨检测与等待/拒绝策略
小结:关键不在“怎么生成”,而在“怎么管住生成过程”
分布式 ID 的本质是状态协调问题。号段预分配把数据库压力摊到批量操作上,Snowflake 结构确保趋势递增和时空可追溯,而所有异常路径(重启、故障、时钟跳变)都得有明确应对。稳定不是靠选对一个算法,而是靠约束边界、暴露风险、留出退路。











