Oracle数据库High Version Count问题诊断与优化
1. High Version Count问题概述
在Oracle数据库10.2.0.4和11.2.0.4版本中,High Version Count是一个常见且棘手的问题。简单来说,当一个SQL语句存在大量子游标(child cursor)时,就会出现High Version Count现象。这种现象不仅会消耗大量共享池(shared pool)内存,还可能导致严重的性能问题甚至数据库挂起。
1.1 什么是Version Count
当一个SQL语句首次执行时,Oracle会进行硬解析(hard parse),创建父游标(parent cursor)和子游标(child cursor)。后续执行相同SQL时,Oracle会先计算SQL语句的hash值,然后在共享池中查找匹配的父游标。如果找到匹配的父游标,就会遍历其下的子游标列表,寻找可重用的执行计划。如果找不到可重用的子游标,就会创建新的子游标。
父游标下的子游标总数就是这个SQL的version count。当version count过高时,就会出现High Version Count问题。
1.2 High Version Count的危害
High Version Count会带来多方面的问题:
- 共享池内存消耗:每个子游标都会占用共享池内存,大量子游标会快速耗尽共享池空间
- 性能下降:查找和匹配大量子游标会增加CPU开销
- 数据库挂起:在某些情况下可能导致数据库完全挂起
- 触发ORA-04031错误:当共享池空间不足时出现
- 引发其他bug:如ORA-600 [kkssearchchildlist*]等
2. 诊断High Version Count问题
2.1 识别高Version Count的SQL
首先需要找出哪些SQL存在High Version Count问题:
-- 查找version count超过100的SQL SELECT sql_id, version_count, sql_text FROM v$sqlarea WHERE version_count > 100 ORDER BY version_count DESC;在AWR报告中,默认version count超过20的SQL就会显示在"order by version count"部分。根据经验,version count超过100就需要引起注意。
2.2 分析子游标不共享的原因
找到问题SQL后,需要分析为什么这些SQL会产生大量子游标:
-- 查看特定SQL的子游标不共享原因 SELECT * FROM v$sql_shared_cursor WHERE sql_id = '8n5bcvc2mwjmj';v$sql_shared_cursor视图中的"Y"值表示对应列存在不匹配(mismatch)情况,这是导致子游标不能共享的直接原因。
2.3 使用version_rpt工具诊断
手工查询上述视图可能比较繁琐,Oracle提供了一个名为version_rpt的小工具,可以更方便地诊断High Version Count问题。这个工具可以从Oracle支持文档(DOC ID 438755.1)下载。
3. 深入诊断技术
3.1 CursorTrace和CursorDump
当v$sql_shared_cursor无法提供足够信息时,可以使用CursorTrace和CursorDump技术进行更深入的诊断。
3.1.1 启用CursorTrace
-- 启用CursorTrace ALTER SYSTEM SET events 'immediate trace name cursortrace level 577, address <hash_value>';CursorTrace有三个级别:
- Level 1: 577
- Level 2: 578
- Level 3: 580
3.1.2 关闭CursorTrace
-- 关闭CursorTrace ALTER SYSTEM SET events 'immediate trace name cursortrace level 2147483648, address 1';注意:在10.2.0.4以下版本存在Bug 5555371,可能导致CursorTrace无法彻底关闭,trace文件会不断增长。生产环境建议谨慎使用CursorTrace。
3.1.3 CursorDump(11g及以上版本)
-- 11g中使用CursorDump ALTER SYSTEM SET events 'immediate trace name cursordump level 16';CursorDump可以收集更全面的信息,包括一些其他方法无法看到的px_mismatch和optimizer_mismatch信息。
3.2 ProcessState Dump和Errorstack(10gR2)
在10gR2中,可以使用processstate dump和errorstack替代CursorDump:
-- 找到问题SQL对应的SPID SELECT spid FROM v$session s, v$process p WHERE s.paddr = p.addr AND s.sql_id = '问题SQL_ID'; -- 使用oradebug进行dump ORADEBUG SETOSPID <spid> ORADEBUG ULIMIT ORADEBUG DUMP PROCESSSTATE 10 ORADEBUG DUMP ERRORSTACK 34. 常见导致High Version Count的SQL模式
根据经验,以下类型的SQL最容易导致High Version Count问题:
4.1 使用绑定变量的INSERT语句
INSERT INTO table(column1, column2, ..., column128) VALUES (:1, :2, :3, ..., :128)特别是当表字段很多且INSERT语句中列出了所有字段时,问题尤为明显。
4.2 使用绑定变量的SELECT INTO语句
SELECT a, b, c, ... INTO :1, :2, :3 FROM table14.3 使用INSERT...RETURNING语句
INSERT INTO table(...) VALUES (...) RETURNING id INTO :id4.4 使用长IN列表且包含绑定变量
SELECT * FROM table WHERE column1 IN (:1, :2, :3, ..., :128)4.5 超长SQL且包含多个绑定变量
非常长的SQL语句如果使用了绑定变量,更容易出现High Version Count问题。
4.6 在DBLINK调用的SQL中使用绑定变量
-- 不推荐的做法 SELECT * FROM table@dblink WHERE column = :15. 配置参数相关问题
5.1 cursor_sharing参数
cursor_sharing参数设置不当容易导致High Version Count问题:
- 绝对不要使用cursor_sharing=similar:这个设置在10gR2以上版本会导致各种bug,包括产生大量不可共享的子游标
- 在11.2.0.3版本,cursor_sharing=similar与=force效果相同
- 在12c中已不支持cursor_sharing=similar
- 建议设置为exact,除非经过充分测试,否则不要设置为force
5.2 Adaptive Cursor Sharing(ACS)
11g引入的Adaptive Cursor Sharing特性也容易导致High Version Count问题。在未经充分测试前,建议关闭此特性:
ALTER SYSTEM SET "_optimizer_adaptive_cursor_sharing"=FALSE;相关bug:
- Bug 12334286:High version counts with CURSOR_SHARING=FORCE
- Bug 7213010:Adaptive cursor sharing generates lots of child cursors
- Bug 8491399:ACS does not match the correct cursor version for queries using CHAR datatype
6. 常见Bug及解决方案
6.1 Bug 8575528 / Patch 6795880
这是10gR2中一个非常严重且隐蔽的bug,会导致:
- 数据库挂起,必须手工重启
- 系统资源耗尽导致宕机
- 触发ORA-600 [kkssearchchildlist*]或ORA-07445[kkssearchchildlist*]错误
虽然在10.2.0.5中声称已修复,但实际仍然常见,因为:
- 修复代码默认不生效,需要手动设置"_cursor_features_enabled"=10
- 即使设置参数,仍可能遇到问题
解决方案:
- 升级到11gR2
- 调整问题SQL
6.2 变长字符串绑定变量问题
当表字段使用VARCHAR等变长类型,而应用传入的字符串长度变化很大时,会导致bind mismatch,产生大量子游标。
解决方案:
-- 设置固定的字符串buffer长度 ALTER SYSTEM SET events '10503 trace name context forever, level 4000';6.3 Bug 8981059
这个bug影响所有10gR2版本,是由绑定变量窥测(bind peeking)导致的。
解决方案:
-- 关闭绑定变量窥测 ALTER SYSTEM SET "_optim_peek_user_binds"=FALSE;6.4 清除高Version Count的SQL
对于version count特别高的SQL,可以将其从共享池中清除:
-- 10.2.0.4和10.2.0.5中的清除方法 ALTER SESSION SET events '5614566 trace name context forever'; EXEC dbms_shared_pool.purge('&address, &hash_value', 'C');注意:event 5614566是为了规避Bug 5614566,该bug会导致dbms_shared_pool.purge无法清除parent cursor。
7. 11g中的增强特性
7.1 _cursor_obsolete_threshold参数
11g引入了_cursor_obsolete_threshold参数(默认100),当子游标数量超过此阈值时,parent cursor会被废弃并创建新的parent cursor。这有效解决了High Version Count问题。
启用方法:
-- 11.2.0.1 ALTER SYSTEM SET "_cursor_features_enabled"=34 SCOPE=SPFILE; ALTER SYSTEM SET event='106001 trace name context forever,level 1024' SCOPE=SPFILE; -- 11.2.0.2 ALTER SYSTEM SET "_cursor_features_enabled"=1026 SCOPE=SPFILE; ALTER SYSTEM SET event='106001 trace name context forever,level 1024' SCOPE=SPFILE;8. 最佳实践建议
8.1 SQL改写建议
- 对于INSERT INTO或SELECT INTO,考虑不使用绑定变量
- 字段很多的表,INSERT时不要列出所有字段
- 控制IN列表中绑定变量的数量,或改用临时表
- 避免编写特别长的SQL语句
- 尽量避免在DBLINK调用的SQL中使用绑定变量
8.2 配置建议
- 设置cursor_sharing=exact
- 关闭adaptive cursor sharing
- 对于10.2.0.4,考虑升级到更高版本
- 监控并定期清理高version count的SQL
8.3 监控脚本
-- 监控shared pool中SQLA区域大小 SELECT * FROM V$SGASTAT WHERE pool='shared pool' AND name='SQLA' AND bytes/1024/1024/1024 > 5; -- 监控硬解析高的非绑定变量SQL SELECT FORCE_MATCHING_SIGNATURE, COUNT(1) FROM v$sql WHERE FORCE_MATCHING_SIGNATURE > 0 AND FORCE_MATCHING_SIGNATURE != EXACT_MATCHING_SIGNATURE GROUP BY FORCE_MATCHING_SIGNATURE HAVING COUNT(1) > 5000 ORDER BY 2; -- 查找硬解析最多的SQL SELECT TO_CHAR(force_matching_signature), COUNT(*) hard_parses FROM v$sqlarea GROUP BY TO_CHAR(force_matching_signature) HAVING COUNT(*) > 5 ORDER BY 2 DESC;9. 总结
High Version Count问题在Oracle 10.2.0.4和11.2.0.4中是一个复杂且棘手的问题,其产生原因多样,表现形式各异。通过合理的诊断方法和适当的解决方案,可以有效地应对这一问题。从11gR2开始,通过_cursor_obsolete_threshold特性,这个问题得到了根本性的解决。
