← 返回题目列表

号段模式本地缓存为什么能提升性能?

中等 第 20 / 25 题 更新于 2026/10/05
号段模式本地缓存双Buffer分布式ID
学习 AI 实战项目

简化版

本地缓存把“每生成一个 ID 访问一次数据库”变成“每消耗一个号段访问一次数据库”,绝大多数请求只做内存原子递增,因此吞吐更高、延迟更稳,也能容忍中心短暂故障。 安全前提是号段由中心原子分配且绝不重叠;未用完的号码宁可浪费,也不能回收给别的实例。

详细版

实例领取 [start,end] 后用 AtomicLong 发号。若步长 100000、实例 5000 ID/s,则一次数据库申请可支撑约 20 秒。双 buffer 在当前段剩余约 10%~20% 时异步预取下一段,使切段不阻塞业务。

预取要 singleflight,避免多个线程重复申请;申请失败按退避重试,并根据 remaining/rate 计算耗尽风险。进程重启丢弃未用区间会产生空洞但不重复。数据库更新 max_id 必须用事务锁/CAS,并防备份恢复造成高水位回退。

完整版教学

一、逐号访问数据库的瓶颈

若每个 ID 都执行 UPDATE ... RETURNING,10 万 ID/s 就意味着 10 万次事务、网络往返和日志写,数据库成为全局串行点。本地号段把中心协调频率降低为 QPS / step,唯一性仍由区间互斥保证。

记忆钩子:中心负责“分地”,实例负责“数地里的门牌号”。

二、基本分配流程

DB max_id=100000, step=10000
atomic update -> max_id=110000
instance owns [100001,110000]
local next(): AtomicLong.incrementAndGet()

两个实例并发申请时,行锁或 CAS 让更新串行,分别拿到不重叠区间。客户端本地发号不再访问数据库。

三、性能提升如何量化

步长发号 QPS理论申请频率单实例可脱机时间
150005000/s0.2ms
100050005/s0.2s
10000050000.05/s20s

步长越大中心压力越低,但重启浪费和低流量业务占用空间越多。要根据峰值、数据库 RTO 和可接受空洞平衡。

四、双 Buffer 消除切段尾延迟

当前 segment 消耗到阈值时,后台线程申请 next segment。当前耗尽后通过原子切换把 next 变 current,再继续预取。若多个请求同时触发,只允许一个加载任务,其他请求仍消费当前段。

current [1..100000] --剩余15%--> async load next [100001..200000]
current exhausted -> atomic swap -> next becomes current

五、故障时怎样处理

数据库不可用但 current/next 尚有余量时继续发号;余量耗尽则停止,不能本地猜一个新区间。申请失败使用有界指数退避,且不能让所有实例同一时刻重试数据库。进程重启后未用段直接废弃,恢复前可持久化段信息,但复用逻辑必须证明没有另一实例接管。

数据库从旧备份恢复时,max_id 可能低于曾分配上界。恢复流程要以独立保存的分配审计高水位为准并向上跳过安全区间;业务最大 ID 不能单独证明安全,因为实例可能仍持有更高但尚未使用的号段。若审计不可用,只能先 fencing 全部旧实例并证明没有未决区间,再选择足够高的新起点。

六、监控与容量预警

核心指标是 current/next 余量、消耗速度、time-to-exhaust、预取耗时/失败、数据库更新冲突、段切换 P99 和浪费率。剩余 20% 并不总安全:低峰可用数小时,高峰可能 2 秒耗尽。

压测要覆盖切段瞬间、DB 中断、预取线程卡死和实例批量重启。唯一约束冲突应作为最高级故障处理。

七、常见误区与追问

  • 误区:本地缓存会导致多实例重复。 只要中心区间原子且不重叠,本地消费是安全的。
  • 误区:未用完号段应该回收节省号码。 无法证明旧实例已停止时回收会碰撞。
  • 误区:步长越大越好。 会增加空洞、低频浪费和容量规划难度。
  • 误区:剩余比例能准确表示风险。 还必须结合实时消耗速度计算耗尽时间。
  • 追问:双 buffer 如何防重复加载? 用 per-key 状态机或 singleflight 串行预取。
  • 追问:DB 挂了能撑多久? current+next remaining / peakRate 给出可解释时间。
  • 追问:重启后段还能复用吗? 默认丢弃最安全,复用需持久租约与 fencing 证明所有权。

八、加强记忆

号段缓存的价值来自“低频中心划段、高频内存递增”,双 buffer 把申请延迟藏在当前段消费期内。安全底线是区间绝不重叠、故障耗尽就停发、旧段不回收;容量则用剩余量除峰值速率计算,而不是只看百分比。