最有效手段是增大序列CACHE值,因NOCACHE或CACHE=1时每次NEXTVAL均需磁盘IO、加锁和日志写入,而CACHE预分配多值至内存可大幅减少争用;实测CACHE 50较CACHE 1耗时降低95%以上。
为什么 NEXTVAL 在循环里反复调用会变慢?
每次执行 select seq.nextval into v_id from dual,oracle 都要获取序列的内存缓存块(sequence cache),并做原子递增、日志记录和共享池锁竞争。在高并发或大循环中,这会引发 enq: sq - contention 等等待事件,尤其当序列 cache 值过小(如默认 20)时,频繁回刷磁盘重载缓存,io 和 latch 开销陡增。
- 序列默认
CACHE 20,1000 次调用需至少 50 次缓存重载 - 多会话争用同一序列时,
SEQ$表行锁和共享池 latch 成瓶颈 -
ORDER属性强制单点串行生成,彻底废掉并发能力,务必禁用
用 BULK COLLECT + FORALL 批量预取 NEXTVAL
不逐行查序列,改用集合批量获取——本质是把多次 NEXTVAL 调用压成一次内存块申请,再本地分发。适用于插入前需主键、且数据源可一次性加载的场景。
DECLARE
TYPE t_ids IS TABLE OF NUMBER;
v_ids t_ids;
BEGIN
-- 一次性取 1000 个值(只要序列 CACHE >= 1000,就只触发 1 次缓存加载)
SELECT seq_name.NEXTVAL BULK COLLECT INTO v_ids
FROM DUAL CONNECT BY LEVEL <= 1000;
<p>FORALL i IN 1..v_ids.COUNT
INSERT INTO t1 (id, name) VALUES (v<em>ids(i), 'item</em>' || i);
END;- 确保序列已设
CACHE 1000或更高:ALTER SEQUENCE seq_name CACHE 1000 - 避免在
FORALL外部用seq.NEXTVAL混用,否则破坏批量效果 - 若数据源本身是游标结果集,优先用
ROWNUM或临时表生成代理键,绕开序列
用 IDENTITY 列替代序列(12c+)
Oracle 12c 引入的 IDENTITY 列底层使用优化过的序列机制,自动绑定、免显式调用,且支持 GENERATED ALWAYS + CACHE 隐式配置,写入路径更短。
CREATE TABLE t1 ( id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1 CACHE 1000), name VARCHAR2(50) );
- 插入时直接省略
id字段:INSERT INTO t1(name) VALUES ('abc') - 无需 PL/SQL 变量承接,消除
INTO开销和上下文切换 - 不兼容老版本(
序列值不够用?别急着扩容,先看是不是设计问题
真正卡住的往往不是序列本身,而是业务逻辑误用:比如在循环内为每条日志都申请一个序列号,但实际只需事务级唯一标识;或用序列模拟时间戳,却忽略 SYSTIMESTAMP 更轻量。
- 日志类场景:用
DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER或ORA_ROWSCN替代 - 批处理唯一标记:用
DBMS_UTILITY.GET_TIME+ 进程号拼接,避免序列争用 - 真需要全局有序号?考虑分库/分表序列(如
seq_shard1,seq_shard2)隔离热点
序列不是万能 ID 生成器,它的强一致性代价很高。判断是否必须用序列,比怎么优化它更重要。



















