梁敬彬梁敬弘兄弟出品
往期回顾
ORACLE开发误区探索【一】(空格、过程与权限、DDL)
ORACLE开发误区探索【二】(insert into、or)
在Oracle数据库开发中,SEQUENCE是生成唯一标识符的常用工具。然而,很多开发人员对其工作机制存在误解,特别是CACHE参数的作用及其对序列连续性的影响。本文深入剖析SEQUENCE的CACHE机制,帮助开发者正确使用这一重要特性。
6. SEQUENCE中的CACHE机制解析
SEQUENCE可以生成一系列连续的唯一数值,但许多开发人员没有意识到:默认情况下,SEQUENCE并不保证生成的值完全连续且无缺失。尤其在配置了CACHE参数后,这种情况更为明显。
6.1 CACHE机制如何工作
当配置CACHE参数时,Oracle会预先分配并缓存一批序列值到内存中,以提高性能。这种机制虽然提升了效率,但也带来了序列值可能断号的问题。
实验演示
-- 创建带20个缓存的序列
CREATE SEQUENCE ljb_test_seq
MINVALUE 1
MAXVALUE 1000
START WITH 1
INCREMENT BY 1
CACHE 20
ORDER;
-- 首次访问序列值
SELECT ljb_test_seq.NEXTVAL FROM dual;
-- 结果: 1
-- 查看当前值
SELECT ljb_test_seq.CURRVAL FROM dual;
-- 结果: 1
-- 清空共享池
ALTER SYSTEM FLUSH SHARED_POOL;
-- 查看当前值(不受清空共享池影响)
SELECT ljb_test_seq.CURRVAL FROM dual;
-- 结果: 1
-- 获取下一个值
SELECT ljb_test_seq.NEXTVAL FROM dual;
-- 结果: 21(而非预期的2!)
这一现象表明,当首次访问SEQUENCE时,Oracle就预先为该会话缓存了20个值(1-20)。当共享池被清空后,这些缓存值丢失,Oracle不得不再次分配20个新值(21-40),导致序列从21继续,中间跳过了19个值。
6.2 导致序列断号的其他情况
不仅是共享池清空会导致序列断号,以下操作也会导致类似问题:
数据库实例重启
-- 重启数据库后
SELECT ljb_test_seq.NEXTVAL FROM dual;
-- 结果将跳过当前缓存中所有未使用的值
事务回滚
-- 创建测试表
CREATE TABLE ljb_test(id1 INT);
-- 插入几条记录
INSERT INTO ljb_test VALUES (ljb_test_seq.NEXTVAL); -- 值:42
INSERT INTO ljb_test VALUES (ljb_test_seq.NEXTVAL); -- 值:43
INSERT INTO ljb_test VALUES (ljb_test_seq.NEXTVAL); -- 值:44
COMMIT;
-- 插入后回滚
INSERT INTO ljb_test VALUES (ljb_test_seq.NEXTVAL); -- 值:45
ROLLBACK;
-- 再次插入
INSERT INTO ljb_test VALUES (ljb_test_seq.NEXTVAL); -- 值:46(而非45!)
事务回滚后,序列值45已被分配但未使用,导致序列断号。
插入操作失败
当INSERT语句因其他原因(如约束违反)失败时,已分配的序列值也不会被回收,同样会导致序列断号。
6.3 NOCACHE是否能解决问题?
有人认为使用NOCACHE选项可以避免序列断号,这是一个误解。
CREATE SEQUENCE no_cache_seq
MINVALUE 1
MAXVALUE 1000
START WITH 1
INCREMENT BY 1
NOCACHE
ORDER;
事实上,即使使用NOCACHE,以下情况仍会导致序列断号:
- 事务回滚
- 插入操作失败
- 数据库实例崩溃
6.4 CACHE的性能优势
虽然CACHE机制可能导致序列断号,但它带来的性能优势是不容忽视的:
- 减少数据字典访问:每次分配新的序列值组时,只需一次数据字典访问
- 降低争用:在RAC环境中,不同实例可以缓存不同范围的序列值,减少争用
- 提高插入性能:特别是在大批量插入操作中,效果显著
最佳实践建议
1. 评估业务需求:
- 如果业务对序列连续性没有严格要求(绝大多数情况),使用CACHE
- 如果必须确保连续性,考虑自定义实现或使用应用级锁定机制
2. CACHE大小设置:
- 一般应用:20-50之间
- 高并发应用:50-200之间
- 特大批处理:200-300之间(超过300提升不明显)
3. 性能考虑:
- 对于RAC环境,适当增大CACHE值可减少实例间争用
- 批量插入操作中,较大的CACHE值可提高性能
4. 避免误解:
- 认识到SEQUENCE(无论是否使用CACHE)都不能保证完全连续
- NOCACHE选项不能解决序列断号问题,反而会影响性能
实际应用对比
6.5 总结
SEQUENCE的CACHE机制是Oracle提供的一种性能优化方案,虽然可能导致序列值不连续,但在绝大多数应用场景中,这种不连续性是可以接受的。正确理解CACHE机制的工作原理,可以帮助开发者根据业务需求做出合理的配置选择。
- 序列断号是常态而非异常:无论是否使用CACHE,序列断号都可能发生
- NOCACHE并非解决方案:不使用缓存不能解决序列断号问题,反而会影响性能
- 适当配置CACHE:根据应用场景和并发需求,选择合适的CACHE大小
- 业务适应:设计业务逻辑时应考虑到序列可能断号的特性
再次强调:对于真正需要连续编号的应用(如票据号码),应考虑在应用层实现自定义序列生成逻辑,而不应完全依赖Oracle SEQUENCE机制。实际应用中,90%以上的序列都不会绝对要求是连续不中断的,此时sequence+cache就发挥了巨大的作用了。我的观点是没有特别的不断号需求,最好都使用sequence,并且加上cache,并且尽量不要太小,当然值太大也浪费,我们测试的结果是一般不要超过300,再往上性能提升不明显了。带cache的sequence能带来两个好处:1、速度能提高,2、RAC环境可避免争用,两个实例可各自缓存一部分sequence值。曾经和同事一起优化过sequence,就是要求开发将所有的带sequence的脚本都加上cache ,后来测试发现对表的插入速度有了显著的提高,并且也缓解了RAC环境的争用。
此外,既然sequence+nochache 也不能绝对保证不断号,这个nocache也没什么用了,所以sequece指定nocache的方式可以抛弃掉了。
未完待续…
系列回顾
“大白话人工智能” 系列
“数据库拍案惊奇” 系列
“世事洞明皆学问” 系列
三分钟讲述个人感悟——感恩,回馈