mcpField
S
sequencer

sequencer

github.com/proyecto26开发113307 次安装1 天前更新
代码仓库

关于 sequencer

sequencer 是用于分布式系统设计的技能,帮助工程师根据吞吐量、排序需求、键宽和信息泄露风险选择 ID 生成方案。它比较自增序列、UUID、ULID、Snowflake 和区间分配,并梳理各自的取舍与故障情形。

主要功能

  1. 01比较五类 ID 生成方案
  2. 02按排序与吞吐需求选型
  3. 03评估时钟偏移与计数器溢出
  4. 04规划位布局、编码和节点 ID
  5. 05检查分片热点与分配器风险

适用场景

  1. 01多节点写入时设计主键
  2. 02评估客户端或离线生成 ID
  3. 03为高吞吐场景设计紧凑 ID
  4. 04排查时钟回拨或节点 ID 冲突

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/proyecto26/system-design-skills --skill sequencer

安装后在 Claude Code 中输入/sequencer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
deep-dive.md
references/providers/
aws.md
gcp.md
generic.md

详细介绍

序列生成器

无需中央瓶颈,即可为所有节点分配唯一标识符;同时还要决定这些 ID 是否必须 可排序 或 单调递增。如果选择不当,问题往往会在后期才暴露,而且代价高昂:冲突会破坏数据,单个分配器会限制写入吞吐量,而泄露创建时间或连续计数的 ID 会暴露业务机密,并助长枚举攻击。

适用场景

系统需要在多个节点上写入新记录,且每条记录都需要主键(订单、消息、上传、事件)。当单个自增列会串行化所有写入、必须在数据库往返前生成 ID(客户端生成、离线生成),或者记录需要大致按时间排序但又不想单独设置排序字段时,适合使用序列生成器。

不适用场景

单个关系型节点仍能轻松满足写入负载(→ back-of-the-envelope)——此时,直接使用 BIGINT AUTO_INCREMENT/SERIAL 就是成本最低且正确的选择;不要为此构建分布式 ID 服务(YAGNI)。 如果已有自然唯一键(邮箱、ISBN、内容哈希),就使用它。除非某项不变量确实需要全局单调性,否则不要强求它——这是这里成本最高的属性,而且通常只需要 每个实体内 有序。

先明确需求

  • 生成位置 — 客户端/边缘、应用服务器还是数据库?(决定每个 ID 是否可以接受一次数据库往返。)
  • 排序需求 — 无、可按时间排序(允许近似有序),还是严格单调递增?按实体还是全局?这是最关键的分岔点。
  • 写入速率和节点数 — 峰值每秒 ID 数,以及生成器数量(→ back-of-the-envelope)。这决定序列计数器所需的位数。
  • 大小和编码预算 — 64 位整数(作为索引键时开销低)还是 128 位(完全无需协调),还是短的 URL 安全字符串?
  • 信息泄露容忍度 — ID 是否可以泄露创建时间或可猜测的计数(枚举攻击/竞争对手情报)?

可选方案

  • 自增 / SQL 序列 — 由一个数据库列分配 ID。适用于由单个节点负责写入,且希望不引入新基础设施的场景。
  • UUIDv4(随机 128 位) — 可在任何地方生成,无需协调,碰撞风险实际上为零。适用于只需要唯一性、从不按 ID 排序的场景。
  • ULID / UUIDv7(时间前缀的 128 位) — 随机生成,但带有毫秒级时间戳前缀,因此 ID 会按创建时间排序。适用于既要保留 UUIDv4 的零协调特性,又需要按时间排序的场景(新键的现代默认选择)。
  • Snowflake 风格(时间戳 + 节点 + 序列,64 位) — 将时间戳、节点 ID 和每毫秒计数器打包进可排序的 64 位整数。适用于写入速率较高,且需要紧凑、近似有序整数键的场景。
  • 数据库票据 / 范围分配(Flickr 风格) — 由中央表分配 ID 区间(例如,每次分配 1000 个);每个节点从内存中的区间分配 ID。适用于希望获得简单、近似单调递增的整数,且不想逐个 ID 协调的场景。

权衡

选项解决的问题造成的缺点何时更换
自动递增 / 序列简单、单调、紧凑的整数写入会串行处理;单节点限制吞吐量;会泄露数量写入量超出单节点承载能力,或需要客户端生成 ID → ticket/Snowflake
UUIDv4(随机)可在任意位置生成,无需协调,也不会泄露信息128 位;随机顺序会破坏索引局部性(页分裂);不可排序需要按时间排序 → ULID/UUIDv7
ULID / UUIDv7无需协调 + 可按时间排序 + 对索引友好仍为 128 位;只能按毫秒排序(并非严格有序);会泄露创建时间需要 64 位键或严格有序 → Snowflake / 序列
Snowflake 风格(64 位)紧凑、近似有序,每节点每秒约 400 万个 ID需要分配节点 ID + 处理时钟偏差;纪元/位数预算会限制使用期限时钟同步不可靠,或无法分配节点 ID → ULID
数据库票据 / 范围近似单调的整数、低协调开销、简单分配器表是单点故障;重启后会出现间隙;跨节点只近似有序分配器成为瓶颈或单点故障 → Snowflake/ULID

压力下的行为

分布式 ID 方案的核心目的,是避免使用单个分配器,因此故障模式主要集中在协调捷径上。

  • **分配器成为单点故障/瓶颈(票据、单个序列):**每次写入都会阻塞在同一行/节点上。流量激增或分配器故障会使所有插入停滞。*缓解措施:*分发更大的范围、复制分配器,或改用 Snowflake/ULID(无需经过中心节点)。更大的范围会牺牲单调性,并在重启时浪费 ID。
  • **时钟偏差与回拨(Snowflake/时间前缀):**如果节点时钟向后跳(NTP 校正、VM 暂停),它可能会再次发出已用过的时间戳,并在其节点+序列空间内发生冲突。*缓解措施:*在 now < last_timestamp 时拒绝发出(阻塞或报错),使用单调时钟源,并针对时钟偏差设置告警。绝不要盲目信任墙上时钟。
  • **序列位耗尽:**单个节点在同一毫秒内生成的 ID 数量超过 2^seq_bits 时,计数器会溢出。*缓解措施:*自旋等待到下一毫秒,或预先根据峰值速率确定合适的位数预算。
  • **节点 ID 冲突:**两个生成器以相同的节点 ID 启动(配置错误、自动扩缩时重复使用),并在无提示的情况下生成重复 ID。*缓解措施:*通过协调器租用节点 ID(→ consistency-coordination),而不是使用静态配置。
  • **顺序键导致分片热点:**将单调 ID 用作分片/分区键,会让所有新写入都进入同一分片。*缓解措施:*对键进行哈希或添加前缀分片——分区方案的修复方法见 data-storage。

**监控:**每个节点的 ID 发放速率、时钟偏差/回拨事件、分配器延迟和范围耗尽率,以及重复键错误(应为零)。

如何应用

  1. 明确输入条件 — 确定生成位置、排序需求 (无 / 可按时间排序 / 严格排序;按实体排序还是全局排序)、峰值 ID/秒、大小 预算和泄露容忍度(参见 先明确)。如果只有一个数据库节点处理写入,就此打住——使用自增 ID(→ back-of-the-envelope)。
  2. 根据权衡表进行选择 — 不需要排序且 128 位可以接受 → UUIDv4; 需要可按时间排序且无需协调 → ULID/UUIDv7;需要高吞吐量下紧凑的 64 位 k 排序整数 → Snowflake;需要简单的单调递增整数 → 票据/范围。
  3. 设置关键参数 — 对于 Snowflake,根据节点数和峰值速率确定 epoch 以及时间戳/节点/序列号的位数划分;对于票据,选择区块大小; 确定节点 ID 的分配方式(租约还是静态分配);选择编码方式 (原始整数、Base62、Crockford Base32)。
  4. 对所选方案进行压力测试 — 检查 压力下的行为:时钟回拨、 序列号耗尽、节点 ID 冲突、分配器故障,以及顺序键对热点分片的影响。确认针对所选配置涉及的每种情况都有缓解措施。
  5. 用数字估算 — 确认位数预算可覆盖每个节点的峰值 ID 速率,且 epoch 可提供足够的使用年限;确认编码后的长度符合键/URL 限制(→ 关键数字)。
  6. 选择提供方 — 默认使用通用方案(Snowflake 库、票据行或 ULID/UUIDv7);只有用户指定云平台时才打开对应的提供方文件(参见 选择提供方)。
  7. 在答案中说明权衡 — 提供一个 Decision | Solves | Worsens | Change it when… 表格,每个所选方案对应一行(例如选项、存储方式、故障策略);最后一列填写具体的触发条件(数字或可观测信号)。然后指出最先会出现什么故障。

注意事项

应该

  • 对于新键,如果需要无需协调且大致按时间排序,默认选择 ULID/UUIDv7 — 这样可以避免 UUIDv4 的随机索引问题。
  • 在选择 Snowflake 之前,根据峰值速率和所需使用年限规划位数预算(时间戳/节点/序列号);记录 epoch。
  • 节点会自动扩缩容时,通过协调器租用节点 ID,而不是使用静态配置。
  • 时钟回拨时拒绝生成 ID,并针对时钟偏差触发告警;将重复键错误视为 P1。
  • 当枚举或信息泄露很重要时,将内部键(可排序,但可能泄露时间)与任何外部不透明 ID 分开。

不应该

  • 在数字表明单个数据库节点无法应付之前,不要构建分布式 ID 服务(YAGNI)。
  • 不要在热点表上使用随机 UUIDv4 作为聚簇/主索引 — 随机顺序会导致页分裂和写放大。
  • 不要在没有复制的情况下,让单个序列或票据行充当整个节点集群的分配器 — 它会成为单点故障和写入瓶颈。
  • 不要使用全局单调递增 ID 作为分片键 — 这会造成热点分片(在 data-storage 中修复)。
  • 不要相信墙上时钟能保证排序;按毫秒排序的 ID 是 k 排序,不是严格排序。

关键数字

64 位 Snowflake 布局(约 41 位时间戳 + 10 位节点 + 12 位序列号)可从其 epoch 起使用约 69 年,支持 1024 个节点,每个节点每毫秒生成 4096 个 ID ≈ 每个节点每秒 400 万个 ID — 足以满足几乎所有单个服务的需求。UUID/ULID 为 128 位 = 16 字节(而 64 位整数为 8 字节),索引键大小翻倍。大小为 N 的票据区块可将分配器调用次数减少 N 倍,但节点重启时最多可能损失 N 个 ID。关于峰值速率和存储容量估算,请参见 back-of-the-envelope;只需重述影响决策的数字。

接口示意

已签发的 ID 是一项契约。应说明其宽度(64 位还是 128 位)、布局 (例如 Snowflake [timestamp:41 | node:10 | seq:12])、排序保证 (无序 / 按毫秒 k 排序 / 严格排序)以及编码方式(原始整数、Base62、 Crockford Base32 — 不区分大小写,适用于 URL)。如有 ID 生成端点,应保持精简:next(entity) -> {id, issued_at}。说明该 ID 是存储键、排序键还是两者皆是 — 该选择会影响 data-storage。

选择提供商

默认采用上述通用方案。如果用户指定了云服务,请阅读 references/providers/<provider>.md,了解托管服务映射、 配额/限制和提供商特有的权衡。如果该提供商没有对应文件,则采用通用方案(大多数云服务没有专用 ID 服务——你需要自行运行库或实现序列)。

图示

要直观展示发号路径(生成器节点 → 64 位布局 → 记录键)或 票据服务的区块分配流程,请使用插件内的 architecture-diagram 技能;快速估算位预算时,内联 [ts | node | seq] 草图就足够了。不要嵌入 Mermaid。

相关构建模块

  • data-storage — 依赖它:ID 会成为主键/排序键,而顺序键可能导致热点的分片/分区由该模块负责。
  • consistency-coordination — 依赖它获取单调性保证和节点 ID 租约背后的因果关系、顺序与领导者选举理论;请链接到该模块,不要在此重复讲解。
  • messaging-streaming — 与它配合处理消息排序和去重;可按时间排序的 ID 能提供天然的序列和幂等性锚点。
  • api-design — 与它配合:ID 生成是安全重试所需幂等键(由该模块负责)的基础。
  • system-design — 归属概念位于编排器中:推理循环、权衡方法和十种故障模式。

参考资料

  • references/deep-dive.md — Snowflake 位布局计算和纪元选择、时钟偏移/单调时钟处理、票据服务范围分配、ULID 与 UUIDv7 字节布局、编码(Base62/Crockford)以及节点 ID 租约。设计生成器的细节时请阅读。
  • references/providers/{generic,aws,gcp}.md — 各环境的库/服务映射、限制和常见问题。

评论

支持 Markdown