测试1:使用method_opt => 'for all columns size 1'收集了统计信息
SQL> @traceon 10046 12 fl2
[MESSAGE] EVENT:10046 LEVEL:12
[MESSAGE] GREP:fl2 TRACEFILE:/oracle10/admin/SOURCE10/udump/source10_ora_15709.trc
SQL> SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>&a;
Enter value for a: 1
old 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>1
oacflg=10 fl2=0300 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
16923
1 row selected.
SQL> /
Enter value for a: 2
old 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>2
oacflg=10 fl2=0300 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
15384
1 row selected.
SQL> /
Enter value for a: 3
old 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>3
oacflg=10 fl2=0300 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
13845
1 row selected.
SQL> /
Enter value for a: 4
old 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq2 */COUNT(*) FROM test WHERE ID>4
oacflg=10 fl2=0300 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
12306
1 row selected.
SQL> SELECT parse_calls,executions,loads,version_count FROM v$sqlstats WHERE sql_text LIKE '%zq2%'
2 ;
PARSE_CALLS EXECUTIONS LOADS VERSION_COUNT
----------- ---------- ---------- -------------
4 4 4 4
SQL> SELECT sql_id,plan_hash_value FROM v$sql WHERE sql_text LIKE '%zq2%';
SQL_ID PLAN_HASH_VALUE
------------- ---------------
fph8hajpxnj20 3508397080
fph8hajpxnj20 3508397080
fph8hajpxnj20 3508397080
fph8hajpxnj20 3508397080
可以看到,SQL执行了4次,解析了4次,VERSION_COUNT为4
游标不能重用的原因为:0x0300,按照MOS文档
High Version Count with CURSOR_SHARING = SIMILAR or FORCE [ID 261020.1]
if the field named oacflg2 or flg2 has a value 300 this means the literal was replaces (100) and the literal is unsafe (200).
ORACLE认为这种绑定是不安全的(unsafe),导致绑定失败,使得本来应该共享的游标没有共享,从而导致硬解析
Unsafe Literals or Peeked Bind Variables [ID 377847.1]
Note that, prior to 11g, unsafe literals are NOT covered by 'bind mismatch' in V$SQL_SHARED_CURSOR as this is for user bind metadata mismatches.
因为cursor_sharing=similar,SQL的字面值在替换后相同,其是1个父游标(对应替换后的SQL语句),下带N个子游标(对应subheap 0)
这里4次执行的执行计划是相同的,硬解析是完全没有必要的
一般来说,在cursor_sharing=similar时,当查询相关的列在存在直方图的情况下,并且运算条件非“=”的情况下,ORACLE会认为是unsafe的。
目前的情况是,已经未收集直方图,DBA_HISTOGRAMS中的2条记录是在收集列统计信息的情况下的最小值(2条数据,一条代表min value,一条代表max value)
测试2:使用DBMS_STATS.DELETE_COLUMN_STATS(NULL,'TEST','id')删除列的统计信息
SQL> @traceon 10046 12 fl2
[MESSAGE] EVENT:10046 LEVEL:12
[MESSAGE] GREP:fl2 TRACEFILE:/oracle10/admin/SOURCE10/udump/source10_ora_16394.trc
SQL> SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>&a;
Enter value for a: 1
old 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>1
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
16923
1 row selected.
SQL> /
Enter value for a: 2
old 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>2
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
15384
1 row selected.
SQL> /
Enter value for a: 3
old 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>3
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
13845
1 row selected.
SQL> /
Enter value for a: 4
old 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq4 */COUNT(*) FROM test WHERE ID>4
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
12306
SQL> SELECT column_name,endpoint_number,endpoint_value
2 FROM DBA_HISTOGRAMS
3 WHERE TABLE_NAME = 'TEST'
4 AND WNER = 'SYS'
5 ORDER BY 2, 3;
COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
------------------------------ --------------- --------------
NAME 0 5.0563E+35
NAME 1 5.0563E+35
SQL> SELECT parse_calls,executions,loads,version_count FROM v$sqlstats WHERE sql_text LIKE '%zq4%';
PARSE_CALLS EXECUTIONS LOADS VERSION_COUNT
----------- ---------- ---------- -------------
4 4 1 1
SQL> SELECT sql_id,plan_hash_value FROM v$sql WHERE sql_text LIKE '%zq4%';
SQL_ID PLAN_HASH_VALUE
------------- ---------------
97dm6rb1bf8ah 2901380809
可以看到,当删除了ID列统计信息,以彻底删除ID列的直方图后,SQL能如期望中的共享游标
根据MOS文档
QUERIES HAVING HUGE VERSION COUNTS WHEN CURSOR_SHARING=SIMILAR [ID 731468.1]
Note that the usual reason for seeing high version counts with CURSOR_SHARING=SIMILAR is that there is histogram data on some column/s involved in predicates where the value of the
replaced literal in the predicate varies a lot (eg: on a primary key columns).
In this case we only share the cursor if the values are identical.
Be sure to avoid histogram data on such columns.
Unsafe Literals or Peeked Bind Variables [ID 377847.1]
With CURSOR_SHARING=SIMILAR whenever the optimizer looks at a replaced bind value to make a decision then that bind is checked to see if it should be considered unsafe. The check made is :
Is the operator NEITHER of '=' or '!='
OR
Are there Column Histograms present on the column.
If either of these are true then the bind is deemed to be unsafe and a new cursor will be created (So binds used in non equality predicates (eg >, =, <=, LIKE) are unsafe).
这里的描述具有迷惑性,直方图信息的来源是sys.histgrm$,min/max value的来源为sys.Hist_Head$,这2张表都是DBA_HISTOGRAMS的基表,按照描述理解,删除直方图信息(sys.histgrm$)后,SQL就应该认为是safe的,应该共享游标,而实际情况下,其参考的是sys.Hist_Head$中的数据(sys.Hist_Head$为dba_tab_cols的基表),也就是列统计数据。
测试3:重新收集统计信息,包括直方图,然后清除sys.Hist_Head$中的列统计
收集直方图,手工删除sys.Hist_Head$中的数据(列统计)(非客户测试环境)
dbms_stats.gather_table_stats(NULL,'TEST',method_opt => 'for all columns',cascade => TRUE);
DELETE sys.Hist_Head$ WHERE obj#=14023
SQL> @traceon 10046 12 fl2
[MESSAGE] EVENT:10046 LEVEL:12
[MESSAGE] GREP:fl2 TRACEFILE:/oracle10/admin/SOURCE10/udump/source10_ora_16835.trc
SQL> SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>&a;
Enter value for a: 1
old 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>1
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
16923
1 row selected.
SQL> /
Enter value for a: 2
old 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>2
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
15384
1 row selected.
SQL> /
Enter value for a: 3
old 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>3
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
13845
1 row selected.
SQL> /
Enter value for a: 4
old 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>&a
new 1: SELECT /*+zq6 */COUNT(*) FROM test WHERE ID>4
oacflg=10 fl2=0100 frm=00 csi=00 siz=24 ff=0
COUNT(*)
----------
12306
1 row selected.
SQL> SELECT column_name,endpoint_number,endpoint_value
2 FROM DBA_HISTOGRAMS
3 WHERE TABLE_NAME = 'TEST'
4 AND WNER = 'SYS'
5 AND column_name='ID'
6 ORDER BY 2, 3;
COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
------------------------------ --------------- --------------
ID 1538 0
ID 3077 1
ID 4616 2
ID 6155 3
ID 7694 4
ID 9233 5
ID 10772 6
ID 12310 7
ID 13848 8
ID 15386 9
ID 16924 10
COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
------------------------------ --------------- --------------
ID 18462 11
ID 20000 12
SQL> SELECT A.LOW_VALUE, A.HIGH_VALUE, A.AVG_COL_LEN, A.HISTOGRAM
2 FROM DBA_TAB_COLUMNS A
3 WHERE TABLE_NAME = 'TEST'
4 AND WNER = 'SYS';
LOW_VALUE HIGH_VALUE AVG_COL_LEN HISTOGRAM
-------------------- -------------------- ----------- ---------------
NONE
NONE
是否认为SQL的变量是unsafe的,很大一部分是参考列统计
ORACLE未提供不收集列统计信息的子句,只有method_opt选项,可以明确指定一个在查询谓词中不会使用的列给他分析,避免其对其他列生成列统计信息,但是,列统计信息(如density)是CBO成本计算的重要来源,缺失列的统计可能导致执行计划突变
还可以修改cursor_sharing = force,但force可能导致一些原来值倾斜的列执行计划不正常,修改前应该先检查SQL的子游标的执行计划是否相同:
select hash_value,plan_hash_value,count(*) from v$sql
where sql_text like '%:"SYS_B_0"%'
group by hash_value,plan_hash_value;
如果全部相同,才能修改。
如果部分不同,可以修改后,对这些特定的SQL使用cursor_sharing_exact 提示或者opt_param提示
cursor_sharing = similar将在12G被废除
ANNOUNCEMENT: Deprecating the cursor_sharing = ‘SIMILAR’ setting [ID 1169017.1]
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/8242091/viewspace-676570/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/8242091/viewspace-676570/