對一條基於分割槽的簡單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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- split 分割槽的簡單研究
- SQL優化案例-分割槽索引之無字首索引(六)SQL優化索引
- Oracle SQL調優之分割槽表OracleSQL
- HGDB的分割槽表實現SQL Server的分割槽檢視SQLServer
- [20181114]一條sql語句的優化.txtSQL優化
- SQL Server大分割槽表沒有空分割槽的情況下如何擴充套件分割槽的方法SQLServer套件
- Spark SQL 效能優化再進一步 CBO 基於代價的優化SparkSQL優化
- MySQL的分割槽(一)MySql
- 基於Vue.js的簡單的svg進度條Vue.jsSVG
- hive Sql的動態分割槽問題HiveSQL
- 基於mysql資料庫 關於sql優化的一些問題MySql資料庫優化
- 對oracle分割槽表的理解整理Oracle
- SQL SERVER之分割槽表SQLServer
- SSD固態硬碟要分割槽嗎?SSD固態硬碟分割槽與不分割槽的效能對比硬碟
- 對含distinct操作的SQL的優化SQL優化
- Oracle SQL效能優化的40條軍規OracleSQL優化
- 一次簡單的分頁優化優化
- MySQL效能優化之簡單sql改寫MySql優化
- 基於 Hyperf+ SQL Server 實現的一個簡單資料庫 curdSQLServer資料庫
- 關於SQL優化的闢謠SQL優化
- 對於iOS效能優化的一點看法iOS優化
- 對於專案中簡單的多條件查詢的一些心得體會
- Android 基於zxing的二維碼掃描功能的簡單實現及優化Android優化
- 基於Promise實現對Ajax的簡單封裝Promise封裝
- MySQL 針對 like 條件的優化MySql優化
- Oracle分割槽表基礎運維-07增加分割槽(2 HASH分割槽)Oracle運維
- 基於元件化開發,一個簡單的Android專案框架元件化Android框架
- SqlServer關於分割槽表的總結SQLServer
- 介紹幾種提高mysql的效能和對於sql的優化的方法MySql優化
- [oracle] expdp 匯出分割槽表的分割槽Oracle
- Oracle分割槽表基礎運維-07增加分割槽(1範圍分割槽)Oracle運維
- 一個left join SQL 簡單優化分析SQL優化
- 關於SQL優化的小知識SQL優化
- Oracle分割槽表基礎運維-07增加分割槽(3列表分割槽)Oracle運維
- 關於MongoDB的簡單理解(一)--基礎篇MongoDB
- SQL最佳化案例-分割槽索引之無字首索引(六)SQL索引
- Hive的靜態分割槽與動態分割槽Hive
- SQL優化案例-單表分頁語句的優化(八)SQL優化