应调大CACHE缓解锁争用、设INCREMENT BY打散写入热点;CACHE 10~100平衡性能与ID丢失风险,INCREMENT BY配合索引优化写入分布,二者正交配置且需全链路审视序列使用场景。

为什么要调 CACHE 和 INCREMENT BY?
PostgreSQL 的 SEQUENCE 在高并发 INSERT 场景下,常因频繁访问序列对象引发锁争用——本质是所有会话抢同一个序列的“下一个值”,形成单点热点。默认 CACHE 1 每次只预取一个值,每插一行就要加一次序列锁;而 INCREMENT BY 若设为 1,又会让相邻 ID 过于集中,加剧索引页分裂或缓存局部性差的问题。
CACHE 值怎么选才不丢 ID 也不卡性能?
增大 CACHE 是最直接有效的缓解手段,但不是越大越好:
-
CACHE 10~CACHE 100是常见安全区间:100 个值一次加载进内存,可支撑百级并发插入而不明显争抢 - 风险点:数据库异常崩溃时,已分配但未使用的缓存值会永久丢失(比如
CACHE 100,最后分配了 92 个,崩了,剩下 8 个就跳过了)——只要业务不依赖“连续无空洞”的 ID,这完全可接受 - 千万别设
CACHE 10000:共享内存压力大,且重启后可能浪费大量 ID;更糟的是,某些连接池(如 PgBouncer in transaction mode)会复用序列缓存,导致跨会话 ID 错乱 - 实操建议:
CREATE SEQUENCE order_id_seq CACHE 50;
INCREMENT BY 不只是“步长”,它影响索引写入效率
单纯调大 CACHE 只能减锁,不能解决新生成 ID 集中写入同一索引页的问题。这时 INCREMENT BY 就起作用了:
- 设
INCREMENT BY 10后,序列值变成 1, 11, 21, 31…,配合合理的主键索引设计(比如 B-tree),能自然打散写入位置,减少页竞争 - 注意:它和
CACHE是正交配置,可以共存:CREATE SEQUENCE log_id_seq INCREMENT BY 5 CACHE 20;
- 别用奇数/偶数做业务判断依据(比如“偶数代表测试数据”)——因为
INCREMENT BY会暴露序列规律,有信息泄露风险 - 如果用 UUIDv7 或 snowflake 替代自增主键,
INCREMENT BY就基本不用管了,但那是另一套架构决策
真正要警惕的,是「没意识到自己在用序列」
很多团队以为加了 DEFAULT nextval('seq') 就万事大吉,却忽略了 ORM 自动生成的批量 INSERT、COPY 导入、物化视图刷新等场景——它们可能绕过默认值逻辑,或一次性触发数百次 nextval() 调用,瞬间压垮序列。这时候 CACHE 再大也扛不住。
检查方法很简单:
SELECT last_value, is_called, cache_value FROM order_id_seq;如果
cache_value 远小于你设的 CACHE,说明序列被频繁重置或误用。真正在意热点的系统,得把序列使用路径全盘理一遍,而不是只调两个参数。










