oracle序列并发插入直接用nextval无需加锁,cache提升性能但会跳号,批量插入用forall+集合预取值,避免循环内commit。
并发插入时直接用 nextval 就行,不用加锁
oracle 的 sequence 本身就是为并发设计的:多个会话同时调用 my_seq.nextval,数据库内部用内存+原子操作保证返回值互不重复,不会阻塞、不争抢行锁或表锁。你完全不需要在存储过程中手动 select ... for update 或显式加锁来“保护序列”。常见错误是误以为要先 select my_seq.currval 判断再插入,这不仅多余,还可能因未调用过 nextval 导致 currval 报错 ora-08002: sequence my_seq.currval is not yet defined in this session。
存储过程里别在循环里反复提交(COMMIT)
哪怕用了 NEXTVAL,如果每插一行就 COMMIT 一次,性能会断崖式下跌——日志写入、事务开销、锁释放都变成高频操作。正确做法是:
- 把所有插入逻辑包在单个事务内(比如用
FORALL批量绑定) - 只在最后统一
COMMIT - 若数据量极大(如百万级),可分批提交(例如每 10000 行
COMMIT),但必须用计数器控制,不能依赖循环索引本身
反例:FOR i IN 1..1000 LOOP INSERT ... VALUES (seq.NEXTVAL, ...); COMMIT; END LOOP; —— 这样跑完 1000 行,实际触发了 1000 次完整事务流程。
CACHE 参数对并发性能影响很大,别用 NOCACHE
创建序列时如果指定 NOCACHE,每次 NEXTVAL 都要写回数据字典并刷盘,严重拖慢高并发场景。默认 CACHE 20 已经够用;生产环境建议设为 CACHE 100 或更高(比如 CACHE 1000)。但要注意:
- 实例异常崩溃时,缓存中未使用的序列值会丢失(表现为跳号)
- 如果业务严格要求“绝对连续不跳号”,
SEQUENCE本身就不适用,得换应用层序列号生成或用表模拟 -
ORDER和NOORDER在 RAC 环境下才有意义;单实例用NOORDER(默认)即可,省去跨节点排序开销
批量插入优先用 FORALL + BULK COLLECT,别写裸循环
用 PL/SQL 循环逐条 INSERT 是最慢的方式。真正高效的做法是把数据先收集进集合(TABLE OF 类型),再用 FORALL 一次性发给 SQL 引擎:
DECLARE
TYPE t_id_tab IS TABLE OF NUMBER;
TYPE t_name_tab IS TABLE OF VARCHAR2(50);
l_ids t_id_tab := t_id_tab();
l_names t_name_tab := t_name_tab();
BEGIN
-- 假设从某处构造数据
FOR i IN 1..1000 LOOP
l_ids.EXTEND; l_ids(i) := my_seq.NEXTVAL;
l_names.EXTEND; l_names(i) := 'Item-' || i;
END LOOP;
<p>FORALL i IN INDICES OF l_ids
INSERT INTO items (id, name) VALUES (l_ids(i), l_names(i));</p><p>COMMIT;
END;</p>
注意:这里 my_seq.NEXTVAL 在 PL/SQL 层调用,不是放在 INSERT 语句里——因为 FORALL 中不能直接用序列伪列,必须提前取好值填入集合。
跳号和缓存丢失是序列的固有行为,不是 bug。只要主键唯一、业务可接受,就该放心用 CACHE + NEXTVAL 组合。强行追求连续性,反而会把简单问题搞成分布式锁难题。











