PostgreSQL重启后序列起始值不可预测的原因及解决方法是什么?
PostgreSQL序列重启后回退/跳号问题解决方案
问题根源分析
你的序列当前配置cache=1,正常情况下默认行为确实应该是重启后从上次持久化的位置继续生成值,你遇到的两种异常分别对应以下原因:
- 序列回退到1的情况:由数据库非正常关闭导致。序列当前值的更新还未完成WAL刷盘或持久化到系统表,进程就被强制终止(比如macOS强制关机、PostgreSQL进程被
kill -9强杀),重启后就会读取到上次持久化的旧值。 - 序列跳号到35的情况:有两种可能,一是过去曾经修改过序列的cache参数为32等大于1的值,数据库预分配的批量序列值还未用完就重启,未使用的预分配值直接被废弃;二是中间有插入事务回滚的操作,PostgreSQL序列不会随事务回滚,消耗的ID不会回收,属于序列的正常设计。
修复&规避方案
1. 彻底解决序列回退主键冲突问题
执行以下SQL手动同步序列值到表当前最大ID,即可立刻修复当前的回退问题:
SELECT setval('birthday_id_seq', (SELECT COALESCE(MAX(id), 1) FROM birthdays), false);
如果需要避免重启后再次出现该问题,可以将该语句配置为PostgreSQL启动后的自动执行任务,每次启动后自动同步所有业务表的序列值。
2. 规避非正常关机导致的序列持久化失败
- 不要使用
kill -9强制终止PostgreSQL进程 - macOS上如果是brew安装的PostgreSQL,关机前先执行
brew services stop postgresql主动正常关闭数据库,避免系统强制结束进程 - 生产环境建议配置UPS防止意外断电导致的WAL刷盘失败
3. 减少序列跳号
你当前的序列配置cache=1已经是避免预分配跳号的最优配置,只要保持该参数不修改,就不会出现批量跳号的问题。如果业务要求ID严格连续,不能接受事务回滚导致的少量ID跳号,那么不建议使用内置序列,可自行实现基于计数器表的主键生成逻辑,不过会损失并发插入性能。
额外注意事项
保持序列的no cycle配置(你当前配置已符合要求),如果开启cycle,序列达到最大值后会从最小值重新开始生成,也会触发主键冲突问题。
内容的提问来源于stack exchange,提问作者Kode Charlie
相关产品推荐
相关产品推荐

