對一條基於分割槽的簡單SQL的優化
今天遇到一個很奇怪的問題,在同一個資料庫上,一條簡單SQL訪問同樣的表,一個用常量,一個用變數,但執行計劃看起來一樣,返回相同的資料,效能差別很大,經過一番研究,終於發現原因。
下面通過實驗模擬這個問題。
drop table test purge;
create table test(id int, big_col char(2000))
partition by range(id)
(
partition p0 values less than(1),
partition p1 values less than(2),
partition p2 values less than(3),
partition p3 values less than(4),
partition p4 values less than(5),
partition p5 values less than(6),
partition p6 values less than(7),
partition p7 values less than(8),
partition p8 values less than(9),
partition p9 values less than(10)
)
;
insert into test select mod(rownum,10),'1' from dual connect by level <=10000;
commit;
analyze table test compute statistics for table;
以上SQL模擬10個分割槽的表,下面我們重現問題:
suk@ORA10G> alter system flush shared_pool;
系統已更改。
suk@ORA10G> var v number
suk@ORA10G> exec :v:=9
suk@ORA10G> select count(1) from test where id>1 and id>:v;
執行計劃
----------------------------------------------------------
Plan hash value: 3670302319
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 732 (1)| 00:00:09 | | |
| 1 | SORT AGGREGATE | | 1 | 13 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 25 | 325 | 732 (1)| 00:00:09 | 2 | 10 |
|* 3 | TABLE ACCESS FULL | TEST | 25 | 325 | 732 (1)| 00:00:09 | 2 | 10 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("ID">1 AND "ID">TO_NUMBER(:V))
統計資訊
----------------------------------------------------------
1 recursive calls
0 db block gets
3502 consistent gets
0 physical reads
9836 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
suk@ORA10G> select count(1) from test where id>:v;
執行計劃
----------------------------------------------------------
Plan hash value: 3670302319
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 813 (1)| 00:00:10 | | |
| 1 | SORT AGGREGATE | | 1 | 13 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 500 | 6500 | 813 (1)| 00:00:10 | KEY | 10 |
|* 3 | TABLE ACCESS FULL | TEST | 500 | 6500 | 813 (1)| 00:00:10 | KEY | 10 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("ID">TO_NUMBER(:V))
統計資訊
----------------------------------------------------------
1 recursive calls
0 db block gets
374 consistent gets
0 physical reads
0 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
suk@ORA10G> select count(1) from test where id>:v and id>1 ;
執行計劃
----------------------------------------------------------
Plan hash value: 3670302319
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 813 (1)| 00:00:10 | | |
| 1 | SORT AGGREGATE | | 1 | 13 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 25 | 325 | 813 (1)| 00:00:10 | KEY | 10 |
|* 3 | TABLE ACCESS FULL | TEST | 25 | 325 | 813 (1)| 00:00:10 | KEY | 10 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("ID">1 AND "ID">TO_NUMBER(:V))
統計資訊
----------------------------------------------------------
1 recursive calls
0 db block gets
374 consistent gets
0 physical reads
0 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
這個SQL非常簡單,甚至連表關聯都沒有,但是它卻引發了很嚴重的效能問題。它之所以加上條件id>1是因為業務邏輯要求這個sql的id必須大於1,雖然絕大部分情況下輸入的變數值也會大於1,但開發人員擔心使用者輸入小於1的值,導致資料錯誤。
我剛拿到SQL的時候,直接在PL/SQL DEV中檢視執行計劃,計劃看起來一模一樣,想不通為什麼效能會這麼差,後來在SQLPLUS中檢視執行計劃,才發現其中的端倪。
首先我們看select count(1) from test where id>1 and id>:v,它的執行計劃是範圍分割槽掃描,但是我們看到pstart=2,pstop=10,那意味著雖然我們輸入的變數是9,但這個SQL仍然要掃描9個分割槽。
也就是在生成執行計劃的時候,用的是id>1這個條件而忽略id>:v這個更好的過濾條件。掃描太多不必要的分割槽從而導致效能很差。
從第一個SQL看,繫結變數窺視並不起作用。
再看第二個SQL:select count(1) from test where id>:v,這個SQL對應的pstart=key,pstop=10,這說明掃描的分割槽個數不確定,依輸入的變數值而定。
最後看第三個SQL:select count(1) from test where id>:v and id>1,神奇發生了,它和第二個SQL只是條件的順序發生了變化,但是它的效能要好很多,主要原因是pstart=key,pstop=10,我們這裡輸入的變數值是9,意味著它只需要掃描2個分割槽,效率和第二種情況一樣。
現象貌似很奇怪,但實際上是可以解釋的。首先這個表很簡單,沒有索引,所以優化器在選擇執行計劃時可選計劃並不多。優化器認為where中的兩個條件是平等的,這種情況下,它生成執行計劃時並不考慮變數值(沒有使用繫結變數窺視),在RBO下,兩個平等的條件會優先考慮靠後的條件,而在CBO下更好相反,會優先考慮更靠近where關鍵字的條件。
這些SQL都是基於CBO的,
第一個SQL,id>1靠近where關鍵字,因此優先考慮的是id>1,所以pstart=1
第三個SQL,id>:v靠近where關鍵字,因此優先考慮的是id>:v,所以pstart=key。
從這個結論可以推測,如果變數:v<=1,則第一個SQL比第三個SQL更高效,因為這種情況下它掃描更少分割槽,大家有興趣的可以去驗證一下。
這個文章重點是為了說明如何去分析這些問題,當你對一些優化的基本知識都有所瞭解時,看似很詭異的問題也就豁然開朗了。
最後,這個SQL的解決辦法是把把條件id>:v放在靠近where的位置,而把id>1放在更後面的位置。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/231499/viewspace-1035616/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 34條簡單的SQL優化準則SQL優化
- Oracle 是分割槽表,但條件不帶分割槽條件的SQLOracleSQL
- 一條簡單的SQL語句優化-新年新氣象SQL優化
- [zt] 收集基於成本的優化統計資料 - 分割槽表優化
- split 分割槽的簡單研究
- msyql 簡單的sql優化SQL優化
- 簡單ORACLE分割槽表、分割槽索引Oracle索引
- 關於SQL Server的分割槽表SQLServer
- 對硬碟的一種簡單加密----在分割槽表上做手腳. (轉)硬碟加密
- 一條sql的優化過程SQL優化
- 一條sql語句的優化SQL優化
- 每秒執行6000的簡單SQL優化(一)SQL優化
- 範圍分割槽表和INTERVAL分割槽表對於SPLIT分割槽的區別
- SQL優化思路&結果集重用優化、分割槽索引優化測試SQL優化索引
- 從一條巨慢SQL看基於Oracle的SQL最佳化SQLOracle
- sql server針對表增加新的分割槽SQLServer
- MySQL幾個簡單SQL的優化MySql優化
- SQL優化案例-分割槽索引之無字首索引(六)SQL優化索引
- SQL優化--用各種hints優化一條SQLSQL優化
- 一條執行了3天的"簡單"的sqlSQL
- 34條簡單的SQL最佳化準則SQL
- 大分割槽表的手工並行優化並行優化
- Oracle SQL調優之分割槽表OracleSQL
- 一條SQL語句的優化過程SQL優化
- greenplum 簡單sql優化案例SQL優化
- MySQL分割槽表的分割槽原理和優缺點MySql
- HGDB的分割槽表實現SQL Server的分割槽檢視SQLServer
- [zt] 基於索引的SQL語句優化索引SQL優化
- Spark SQL 效能優化再進一步 CBO 基於代價的優化SparkSQL優化
- 高效的partition(使用分割槽條件)
- 基於Vue.js的簡單的svg進度條Vue.jsSVG
- 每秒執行6000的簡單SQL優化(二)SQL優化
- PL/SQL解數獨在簡單優化基礎上的改進SQL優化
- JOB、分割槽表、效能優化方面優化
- 一條大sql的調優SQL
- oracle實用sql(14)--查詢分割槽表的分割槽列和子分割槽列OracleSQL
- oracle本地分割槽索引跨分割槽對成本的影響Oracle索引
- 對刪除分割槽的分割槽表執行TSPITR