Oracle统计信息不准(谓词越界)造成的性能问题
什么是謂詞越界?謂詞越界其實就是SQL語句的查詢條件超出了數(shù)據(jù)庫統(tǒng)計信息所記錄的范圍。謂詞越界會導致Oracle優(yōu)化器錯誤的選擇SQL語句的執(zhí)行計劃,導致性能問題。
這里舉一個簡單的例子說明謂詞越界導致優(yōu)化器選擇了錯誤的執(zhí)行計劃。
create table t1 (col1 number); create index idx_t1 on t1(col1);beginfor i in 1..10000 loopinsert into t1 values (i);end loop;commit; end; /這里創(chuàng)建了t1表,并在col1列上創(chuàng)建了索引,并向表里寫入了10000條數(shù)據(jù)。提供過對t1表收集統(tǒng)計信息,可以得到目前表t1的謂詞情況。
SQL> exec dbms_stats.gather_table_stats('SALP','T1'); SQL> select low_value,high_value from dba_tab_col_statistics where table_name='T1' and owner='SALP';LOW_VALUE HIGH_VALUE ---------- ---------- C102 C302SQL> var x number; SQL> exec dbms_stats.convert_raw_value('C102',:x);PL/SQL procedure successfully completed.SQL> select :x from dual;:X ----------1SQL> exec dbms_stats.convert_raw_value('C302',:x);PL/SQL procedure successfully completed.SQL> select :x from dual;:X ----------10000上面用到了一個系統(tǒng)包,把統(tǒng)計信息表里的上下限裸數(shù)據(jù)轉(zhuǎn)換成可讀的數(shù)值。
在謂詞范圍內(nèi)的條件查詢的執(zhí)行計劃為
explain plan for select * from t1 where col1 between 1 and 10000; select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 1387720244 ------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10000 | 40000 | 7 (0)| 00:00:01 | |* 1 | INDEX FAST FULL SCAN| IDX_T1 | 10000 | 40000 | 7 (0)| 00:00:01 | ------------------------------------------------------------------------------- Predicate Information (identified by operation id): ---------------------------------------------------1 - filter("COL1">=1 AND "COL1"<=10000) 13 rows selected.因為這里的條件包含了t1表內(nèi)的所有數(shù)據(jù),所以采用多塊讀且不需要回表的執(zhí)行計劃是最優(yōu)的(table access full/index fast full scan),這里實際使用的是index fast full scan。
接下來繼續(xù)向t1表寫入數(shù)據(jù)
beginfor i in 10001..10000000 loopinsert into t1 values (i);end loop;commit; end; /在不重新收集統(tǒng)計信息的情況下,檢查表的統(tǒng)計信息
select low_value,high_value from dba_tab_col_statistics where table_name='T1' and owner='SALP';LOW_VALUE HIGH_VALUE ---------- ---------- C102 C302現(xiàn)在來進行一次謂詞越界的查詢,使用謂詞條件 col1 between 10001 and 10000000。按道理來說,這種選擇表里99.9%數(shù)據(jù)的語句應該使用多塊讀且不回表的執(zhí)行計劃(table access full/index fast full scan)。我們來實際試驗一下。
SQL> set timing on; SQL> Select count(*) from t1 where col1 between 10001 and 10000000;COUNT(*) ----------9990000Elapsed: 00:00:11.17 SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- SQL_ID 86kr1tnhns36d, child number 0 ------------------------------------- Select count(*) from t1 where col1 between 10001 and 10000000Plan hash value: 1970818898---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 4 | | | |* 2 | INDEX RANGE SCAN| IDX_T1 | 1 | 4 | 2 (0)| 00:00:01 | ----------------------------------------------------------------------------可以看到這條語句執(zhí)行了11s才出結(jié)果,且執(zhí)行計劃選擇的是單塊讀的index range scan,而不是我們期望的多塊讀不回表的兩種執(zhí)行計劃之一且返回的Rows和Bytes出現(xiàn)了嚴重預估錯誤。
我們重新為t1表收集一次統(tǒng)計信息,再次執(zhí)行同樣的語句并檢查執(zhí)行計劃。
SQL> exec dbms_stats.gather_table_stats('SALP','T1');PL/SQL procedure successfully completed. Elapsed: 00:00:07.92 SQL> select count(*) from t1 where col1 between 10001 and 10000000;COUNT(*) ----------9990000Elapsed: 00:00:00.31 SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- SQL_ID g47843nv7gsdq, child number 0 ------------------------------------- select count(*) from t1 where col1 between 10001 and 10000000Plan hash value: 3724264953--------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 4434 (100)| | | 1 | SORT AGGREGATE | | 1 | 6 | | | |* 2 | TABLE ACCESS FULL| T1 | 9990K| 57M| 4434 (2)| 00:00:01 | ---------------------------------------------------------------------------這次走出了我們希望的執(zhí)行計劃(table access full),預估的Rows和Bytes也都正常了,且語句花了310ms就運行完了。
?
謂詞越界一般會發(fā)生在什么場景下?
1 臨時表
這里指的是業(yè)務上的臨時表而不是Oracle數(shù)據(jù)庫本身的temporary table。在某些系統(tǒng)中會根據(jù)業(yè)務條件創(chuàng)建前臺表和后臺表,數(shù)據(jù)先進入前臺表,處理完畢后,存入后臺表,并用delete語句清理前臺表的數(shù)據(jù),前臺表起到一個臨時表的作用。我們知道,Oracle自動收集統(tǒng)計信息的默認時間窗口是工作日晚上的22點到凌晨2點,或者周末的早上6點到第二天凌晨2點。在自動收集統(tǒng)計信息窗口內(nèi),數(shù)據(jù)庫前臺表基本上處于無數(shù)據(jù),或者數(shù)據(jù)量很小的情況,那么產(chǎn)生的統(tǒng)計信息就會和白天實際處理業(yè)務數(shù)據(jù)時有偏差,就有可能發(fā)生謂詞越界的情況。
2 巨大表
Oracle觸發(fā)自動收集某個表的統(tǒng)計信息的條件是表中修改的數(shù)據(jù)量超過該表數(shù)據(jù)總量的10%,假設一個表每天新增1w條數(shù)據(jù),一年后這個表變成了365w條數(shù)據(jù),那么這意味著這個表需要再過一個多月才會觸發(fā)一次自動收集統(tǒng)計信息的作業(yè)。那么在這個表上的謂詞查詢,尤其是時間、序列等自增條件上的查詢,就可能發(fā)生謂詞越界的情況,影響優(yōu)化器正確選擇執(zhí)行計劃。
轉(zhuǎn)載于:https://www.cnblogs.com/aegis1019/p/9059220.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Oracle统计信息不准(谓词越界)造成的性能问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第九次会议(5.14)
- 下一篇: React技术栈