有時我們會在應用程序中用到數據庫全局臨時表(global temporary tables)用於存放臨時的數據集, 在12c之前對於全局臨時表上的統計信息處理非常的棘手, 就像我之前的一篇案例( 臨時表不要收統計信息 )中遇到的, 當時建議是不對臨時表收集統計信息解決方案, 讓數據庫使用動態采樣來估算實際表中的數據, 因為臨時表對所有用戶可見,但數據只對當前的用戶可見,數據保留分事務級和會話級, 除了動態采樣外也可以使用hint固定或者使用dbms_stats表set一個更佳接近實際數據的值以生成正確執行計劃. 以前就想過CBO應該改進對於GTT的統計信息收集的方法,後來發現在12c中Oracle已經解決了這個問題.
在12C以前oracle不會主動維護全局臨時表(global temporary tables)的統計信息, 並且如果手動收集也只是存在一份統計信息,即使不同的會話級臨時表的數據量不一致,一旦存在對所有的會話可見,從12.1起可以使用GLOBAL_TEMP_TABLE_STATS 控制GTT表的統計信息是所有會話共享還是session級私有,默認session級統計信息是啟用的. CBO 在查看表的統計信息時順序是先看當前session級是否存在統計信息,如果沒有再使用共享的統計信息(如果存在), 這樣在dba_tab_statistics表中一個臨時表就可能存在一個共享統計信息和一個session級統計信息記錄, 以scope字段做區分.
另外註意在12c前如果臨時表數據是事務級(on commit delete rows)在做dbms_stats收集表統計信息時會先隱性的發起一個commit, 最終臨時表的記錄為0, 而在12C中則不會刪除記錄.與該特性相關的隱藏參數是_optimizer_use_gtt_session_stats, default值為true. 該特性對SYS無效,測試請使用其他用戶,下面演示一下這個特性.
12C以前的版本的不再演示, 如果收集了臨時表統計信息, 所有會話使用相同的統計信息,可能產生錯誤的執行計劃.
SQL> select * from v$version;
BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c EE Extreme Perf Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0
驗證當前默認臨時表stat scope
SQL> SELECT DBMS_STATS.get_prefs('GLOBAL_TEMP_TABLE_STATS') FROM dual;
DBMS_STATS.GET_PREFS('GLOBAL_TEMP_TABLE_STATS')
-------------------------------------
SESSION
如果想修改為共享方法
BEGIN
DBMS_STATS.set_global_prefs (
pname => 'GLOBAL_TEMP_TABLE_STATS',
pvalue =http://ju.outofmemory.cn/entry/>'SHARED');
END;
/
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
PDB$SEED READ ONLY NO
PDBANBOB READ WRITE NO
oracle@anbob ~]$ export TWO_TASK=pdbanbob
[oracle@anbob ~]$ sqlplus anbob/anbob
SQL*Plus: Release 12.2.0.1.0 Production on Tue Feb 21 16:45:47 2017
Copyright (c) 1982, 2016, Oracle. All rights reserved.
Last Successful login time: Mon Feb 20 2017 21:32:52 +08:00
Connected to:
Oracle Database 12c EE Extreme Perf Release 12.2.0.1.0 - 64bit Production
SQL> create global temporary table GTT(id int, name varchar2(20)) on commit preserve rows;
Table created.
SQL> select DBMS_STATS.GET_PREFS('GLOBAL_TEMP_TABLE_STATS','ANBOB','GTT') FROM DUAL;
DBMS_STATS.GET_PREFS('GLOBAL_TEMP_TABLE_STATS','ANBOB','GTT')
--------------------------------------------------------------------------------
SESSION
SQL> insert into gtt
select rownum,'anbob'||rownum from dual connect by rownum<=1e6;
000 rows created.
SQL> commit;
Commit complete.
SQL> select TABLE_NAME, BLOCKS,NUM_ROWS, SCOPE from USER_TAB_STATISTICS where TABLE_NAME = 'GTT';
TABLE_NAME BLOCKS NUM_ROWS SCOPE
-------------------- ---------- ---------- -------
GTT SHARED
SQL> @gts gtt
Gather Table Statistics for table gtt...
PL/SQL procedure successfully completed.
SQL> select TABLE_NAME, BLOCKS,NUM_ROWS, SCOPE from USER_TAB_STATISTICS where TABLE_NAME = 'GTT';
TABLE_NAME BLOCKS NUM_ROWS SCOPE
-------------------- ---------- ---------- -------
GTT SHARED
GTT 3018 1000000 SESSION
收集共享統計信息的方法
SQL> BEGIN
DBMS_STATS.set_global_prefs (
pname => 'GLOBAL_TEMP_TABLE_STATS',
pvalue =http://ju.outofmemory.cn/entry/>'SHARED');
END;
/
PL/SQL procedure successfully completed.
SQL> @gts gtt
Gather Table Statistics for table gtt...
PL/SQL procedure successfully completed.
SQL> select TABLE_NAME, BLOCKS,NUM_ROWS, SCOPE from USER_TAB_STATISTICS where TABLE_NAME = 'GTT';
TABLE_NAME BLOCKS NUM_ROWS SCOPE
-------------------- ---------- ---------- -------
GTT 3018 1000000 SHARED
GTT 3018 1000000 SESSION
SQL> set autot trace exp
SQL> select /*+ gather_plan_statistics */ * from gtt;
Execution Plan
----------------------------------------------------------
Plan hash value: 917624683
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 16M| 824 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| GTT | 1000K| 16M| 824 (1)| 00:00:01 |
--------------------------------------------------------------------------
Note
-----
- Global temporary table session private statistics used
# SESSION 2
SQL> BEGIN
DBMS_STATS.set_global_prefs (
pname => 'GLOBAL_TEMP_TABLE_STATS',
pvalue =http://ju.outofmemory.cn/entry/>'SESSION');
END;
/
PL/SQL procedure successfully completed.
[oracle@anbob ~]$ sqlplus anbob/anbob
SQL*Plus: Release 12.2.0.1.0 Production on Tue Feb 21 16:56:23 2017
Copyright (c) 1982, 2016, Oracle. All rights reserved.
Last Successful login time: Tue Feb 21 2017 16:45:47 +08:00
Connected to:
Oracle database 12c EE Extreme Perf Release 12.2.0.1.0 - 64bit Production
SQL> select * from gtt;
no rows selected
SQL> insert into gtt
select rownum,'anbob'||rownum from dual connect by rownum<=10;
rows created.
SQL> @gts gtt
Gather Table Statistics for table gtt...
PL/SQL procedure successfully completed.
SQL> select TABLE_NAME, BLOCKS,NUM_ROWS, SCOPE from USER_TAB_STATISTICS where TABLE_NAME = 'GTT';
TABLE_NAME BLOCKS NUM_ROWS SCOPE
------------------------------ ---------- ---------- -------
GTT 3018 1000000 SHARED
GTT 1 10 SESSION
SQL> set autot trace exp
SQL> select /*+ gather_plan_statistics */ * from gtt;
Execution Plan
----------------------------------------------------------
Plan hash value: 917624683
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 100 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| GTT | 10 | 100 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Note
-----
- Global temporary table session private statistics used
Note:
現在可以看到在不同的session級, 查看執行計劃使用了session級專有的統計信息, 這樣在與其它表關連時不會再像12c以前的版本那樣使用錯誤的cardinality而造成產生錯誤的執行計劃. 並且在使用auto trace或使用dbmt_xplan查看執行計劃時會有”Global temporary table session private statistics used“的提示, 同時會註意到在session之間相同的sql使用自己新生成的SQL child cursor. 查看沒有共享的原因是”Session Specific Cursor Session Mismatch”
SQL> @sqlt gtt
HASH_VALUE SQL_ID CHLD# OPT_MODE SQL_TEXT
---------- ------------- ---------- ---------- --------------------------------------------------------
79998 cswbnusftyn0y 0 ALL_ROWS select /*+ gather_plan_statistics */ * from gtt
79998 cswbnusftyn0y 1 ALL_ROWS select /*+ gather_plan_statistics */ * from gtt
rows selected.
SQL> @nonshared2 print cswbnusftyn0y
SQL_ID CHILD# REASON REASON_XML
------------- ---------- ----------------------------------------------- --------------------------------------------------------------
cswbnusftyn0y 0 Session Specific Cursor Session Mismatch(1):
Session Specific Cursor Session Mismatch(1)
x2
3
Session Specific Cursor Session Mismatch(1):
Session Specific Cursor Session Mismatch(1)
x2
3
Summary:
在12c以前如果臨時表收集的統計信息可能與實際數據不一致時可能因為錯誤的CARD值CBO產生了錯誤的執行計劃, 從12C引入了session specific statistics, 這樣就解決了不同的會話之間數據差異而使用不同的統計信息, 避免cardinality的錯誤, 該特性從12C中默認是啟動的.
Tags: 解決方案 應用程序 oracle Oracle commit
文章來源: