Oracle序列跳号非bug而是设计使然:事务回滚、CACHE缓存丢失(如FLUSH SHARED_POOL或实例崩溃)、RAC多实例各自缓存及PL/SQL中误用NEXTVAL(如循环预取未使用)均导致跳号;CACHE越大跳号越显著,可控策略是调小CACHE、KEEP序列对象、禁用ORDER、避免应用层依赖ID连续性。
oracle 11g 中序列跳号不是 bug,而是设计使然;跳号严重往往源于 cache 值过大、共享池被刷、或 pl/sql 中不当调用(比如在循环里反复查 nextval 却未真正使用),而非版本缺陷。
为什么 CACHE 20 在 11g 里可能变成跳 200+?
Oracle 11g 默认仍用 CACHE 20,但实际缓存行为受共享池管理机制影响更大:当序列对象被 aging out(如因 ALTER SYSTEM FLUSH SHARED_POOL 或内存压力),整个 cache 段丢失,下次取值直接从「上次分配段末尾 + 1」开始。若之前 cache 段是 1001–1020,刷池后下一次 NEXTVAL 就是 1021——看似只跳 1,但若你之前已用到 1015,又刷池两次,就可能跳到 1041。
更隐蔽的是:11g 引入了更激进的 library cache 清理策略,尤其在 RAC 环境下,不同实例各自缓存不重叠段(如 A 缓 1–20,B 缓 21–40),一旦负载偏移,立刻出现逻辑跳号甚至「回跳」(A 再次取到 2)。
-
CACHE值越大,单次丢失风险越高;设为CACHE 100后一次刷池就跳百位 -
NOORDER(默认)不保证跨实例顺序,ORDER虽可缓解回跳,但强制序列生成串行化,高并发下易引发row cache lock - 11g 中
DBA_SEQUENCES.cache_size显示的是当前设定值,不是剩余缓存数;真想看还剩多少可用,得查V$ROWCACHE中parameter = 'dc_sequences'的gets和getmisses
PL/SQL 里调用 NEXTVAL 的三个典型踩坑点
很多跳号其实不是数据库导致,而是应用层代码把 NEXTVAL 当“预览”用了——查了值却没插入,或者在一个循环里每次查都走一次 NEXTVAL,哪怕只有一条记录最终提交。
- 在
FORALL批量插入前,用SELECT seq.NEXTVAL BULK COLLECT INTO ...预生成 ID 数组:只要数组长度固定,就会消耗对应数量序列值,哪怕后续部分 INSERT 失败回滚 - 写存储过程时,在异常处理块里又调了一次
seq.NEXTVAL(例如日志记录用),导致主业务失败后额外跳号 - 用
SELECT seq.NEXTVAL FROM DUAL测试序列是否正常,随手多执行几次——每执行一次就跳一个 - 正确做法:只在真正要插入的 SQL 中内联使用
seq.NEXTVAL,如INSERT INTO t(id, name) VALUES (seq.NEXTVAL, 'x');避免在 PL/SQL 变量中提前捕获
如何让跳号可控,而不是消失
彻底消灭跳号在 Oracle 11g 中几乎不可行且不推荐;目标应是让跳号可预期、幅度小、不影响业务唯一性校验。
- 将
CACHE改为CACHE 5或CACHE 10:平衡性能与跳号粒度,比默认 20 更安全 - 对关键序列执行
EXEC DBMS_SHARED_POOL.KEEP('SCHEMA.SEQ_NAME', 'Q'):防止被 aging out;注意需先以 SYS 运行dbmspool.sql安装包,并授 EXEC 权限 - 禁用
ORDER,除非你明确跑在 RAC 且能接受性能惩罚;多数单实例系统用NOORDER+ 小 CACHE 更稳 - 监控跳号频率:定期查
SELECT sequence_name, last_number, cache_size FROM DBA_SEQUENCES WHERE sequence_owner = 'XXX',对比last_number增速与业务插入量,偏差持续 > 15% 就该查 PL/SQL 调用逻辑
最常被忽略的一点:跳号本身不可怕,可怕的是业务代码假设「ID 连续」来做分页、范围查询或导出校验——这类逻辑必须剥离对物理序列值的依赖,改用时间戳、版本号或单独维护的连续编号表。











