多机房场景下分布式 ID 如何设计?
简化版
多机房发号的核心是让各地域在网络分区时仍占用互不重叠的命名空间。可在 Snowflake 中预留 RegionId/WorkerId 位,或给各地域分配互斥号段;不要依赖每次跨地域同步协调。 同时规划地域迁移、位数寿命、WorkerId 租约和数据合并,保证故障切换后不会复用原地域仍可能发出的范围。
详细版
Snowflake 可编码 timestamp + region + worker + sequence,控制面只在地域内分配 worker;号段模式则由全局权威预分大区间,再由地域内部细分。网络断开时地域继续在自己的空间发号,唯一性不受跨域 RTT 影响。
RegionId 位数决定最多地域数,增加位数会压缩时间寿命或单毫秒序列。地域永久下线后,其 ID/号段不能立刻复用;流量迁移到新地域时分配新命名空间。若业务还要求按 ID 排序,跨地域时钟偏差意味着只能大致有序。
完整版教学
一、为什么跨地域逐号协调代价高
每次生成 ID 都访问全球中心,会把广域 RTT 和中心故障放入所有创建请求。网络分区时要么停发,要么各地私自发号产生碰撞。更实用的设计是提前划分互斥空间,让数据面本地生成,控制面低频分配。
记忆钩子:多机房唯一性靠“先分地盘”,不是靠每发一个号都全球开会。
二、Snowflake 位布局示例
1 sign | 41 timestamp | 3 region | 7 worker | 12 sequence
3 位 region 最多 8 个地域,7 位 worker 每地域 128 个节点,12 位序列每 worker 每毫秒 4096 个。总位数固定时,这些容量互相交换;要从预期地域数、寿命和峰值反推,不能照搬经典 10 位 worker。
三、号段的地域分层
| 层级 | 分配示例 | 是否高频协调 |
|---|---|---|
| 全球 | region A: [0,10^12) | 低频 |
| 地域 | service X: [0,10^9) | 中低频 |
| 实例 | [100001,200000] | 本地缓存 |
也可用高位 region 前缀加地域内序列。区间必须持久审计,全球恢复到旧备份时不能回退高水位。
四、网络分区与 WorkerId 安全
RegionId 解决地域间碰撞,地域内 WorkerId 仍必须唯一。租约服务分区时,失联 worker 应在本地租约截止前按最大时钟误差预留安全窗口并停发;分配端只能在旧租约截止及安全裕量过去后复用该 ID。若无法约束时钟误差,就要通过外部 fencing 确认旧实例停止,不能让新旧实例同时使用同一 WorkerId。虚拟机快照克隆尤其容易复制 WorkerId 和 lastTimestamp。
Worker 租约携带 epoch,并将 region/worker 暴露到监控。RegionId 不从可变机房名称哈希临时计算,而由受控注册表分配。
五、故障迁移如何处理
地域 A 故障后,业务切到 B 应使用 B 的 RegionId 或新的预留区间,不要模拟 A 继续发号,除非能证明 A 已被 fencing。A 恢复后保留原 ID 身份,但旧 worker 租约全部失效并重新申请。
这种设计会让同一租户迁移前后的 ID 高位变化,这是可接受的;业务路由不能依赖 ID 中 region 永远等于当前归属。
六、有序性、隐私与容量
不同地域时钟偏差 50ms 时,数字大小不能保证真实业务先后。若需要订单因果顺序,应使用业务版本或提交时间,不从 ID 推导。Region/时间位还会泄露来源与规模,对外接口可用随机 token 映射。
监控各地域发号量、位空间利用、WorkerId 冲突、时钟偏差、全球区间余量和跨地域切换。演练断开全球控制面,确认本地还能在已授权空间安全发号多久。
七、常见误区与追问
- 误区:所有地域连接同一个 Redis INCR 最简单。 广域延迟和中心故障会进入每次发号。
- 误区:有 RegionId 就无需管理 WorkerId。 地域内部仍可能节点碰撞。
- 误区:地域下线后 ID 可马上回收。 旧实例或历史数据仍可能存在,复用会碰撞。
- 误区:跨地域 Snowflake 能严格按时间排序。 时钟偏差和并发无法保证全局顺序。
- 追问:RegionId 位不够怎么办? 新版本/新命名空间或改用更长 ID,不能在线无损扩位。
- 追问:全球控制面挂了还能发多久? Snowflake 取决于租约,号段取决于地域剩余区间。
- 追问:迁移后如何路由历史 ID? 使用正式归属表,不把 ID 中地域位当当前真值。
八、加强记忆
多机房 ID 设计按“地域分空间、地域内再分 worker/号段、分区时本地发、迁移时换授权”展开。位布局要算地域数、节点数、峰值与寿命;RegionId 只保证命名空间隔离,不能替代租约、时钟治理和业务路由。
新增地域前应先做全量生产者兼容检查,尤其确认旧 SDK 不会把新增高位误判为负数、溢出或未知版本。
跨地域容灾演练要在控制面断联状态下持续发号,验证各地域命名空间确实互斥,并测出本地缓存耗尽时间。
地域下线后,其编号不能立即复用;历史数据、离线任务和迟到消息仍可能携带旧地域生成的 ID。