說起statspack,想到年前一個跟web分頁有關的故障,某天服務器負載狂增加,load由平時的小於10飈升到30以上,數據庫報警不斷。經過檢查我發現大部分進程都在執行同樣的sql,statspack報表顯示這些語句的邏輯讀、物理讀都排在最前面,奇怪,之前報表未見到過這些sql,大過年的誰在胡搞?
--------------- ------------ -------------- ------ -------- ------------
217,146,067 16,789 12,933.8 25.9 2209.51 3174.15 539991006
Module: [email protected] (TNS V1-V3)
select count(*) from test_dream t
217,126,051 16,785 12,935.7 25.9 ######## 23573.04 4126436409
Module: [email protected] (TNS V1-V3)
select * from (select r.*,rownum linenum from (select t.uID, t.NAME, t.XX_NAME, t.XX_URL , t.STATUS, t.GMT_CREATE, t.GMT_MODIFIED, t.FO_MODIFIED from test_dream t order by t.GMT_CREATE desc)r WHERE rownum <= :1) WHERE linenum >= :
很明顯,這兩個語句是用來web頁面分頁展示的。第一個sql統計分頁的總頁數,第二個sql提取需要展示的具體數據。搞清楚開發是要實現web分頁功能,以order by time desc排序來顯示,這是很常見的需求,我們來考慮怎麼進行優化?
從上面的邏輯讀來看,這個表數據量肯定不小:
COUNT(*)
----------
1774011
1 row selected.
如果以這個值做分頁且假設每頁顯示100條數據的話,至少需要分17萬頁,對於web頁面展示來說,人們關注的只是前面幾頁,分這麼多頁沒有什麼意義。
第一條sql,讓開發更改只顯示前20000行數據:
第二條sql,web頁面上展示最新生成的數據,查看其執行計劃:
2 select *
3 from (select r.*, rownum linenum
4 from (select t.ID,
5 ....
12 from test_dream t
13 order by t.GMT_CREATE desc) r
14 WHERE rownum <= :1)
15 WHERE linenum >= :2 ;
SQL> @?/rdbms/admin/utlxpls
---------------------------------------------------------------------
Id | Operation | Name | Rows | Bytes | Cost |
---------------------------------------------------------------------
0 | SELECT STATEMENT | | 1053 | 255K| 31 |
1 | VIEW | | 1053 | 255K| 31 |
2 | COUNT STOPKEY | | | | |
3 | VIEW | | 1053 | 241K| 31 |
4 | SORT ORDER BY STOPKEY| | 1053 | 50544 | 31 |
5 | TABLE ACCESS FULL | test_dream |1053 |50544 | 7 |
---------------------------------------------------------------------
執行計劃走的是全表掃描,而全表掃描代價顯然是很高的,嘗試在GMT_CREATE建立索引,引導sql走時間索引。
.........省略explain語句
SQL> @?/rdbms/admin/utlxpls
---------------------------------------------------------------------
Id | Operation | Name | Rows | Bytes | Cost |
---------------------------------------------------------------------
0 | SELECT STATEMENT | | 1053 | 255K| 31 |
1 | VIEW | | 1053 | 255K| 31 |
2 | COUNT STOPKEY | | | | |
3 | VIEW | | 1053 | 241K| 31 |
4 | SORT ORDER BY STOPKEY| | 1053 | 50544 | 31 |
5 | TABLE ACCESS FULL | test_dream |1053 |50544 | 7 |
---------------------------------------------------------------------
還是走的全表掃描,建了索引也分析過了,執行計劃還是沒有走索引,這是爲什麼呢?
簡單細小的問題,關鍵時刻要立刻反應出來,這應該算是高級dba的能力一部分,我還不是,下面分析是經拖雷提醒我纔想起來的。
分析:Btree索引不存儲NULL值,而在order
by desc中,NULL值總是最大的,sql語句通過索引無法判斷表是否存在NULL,執行計劃還是走全表掃描。
設置GMT_CREATE不爲空:
SQL> explain plan for select *
2 from (select r.*, rownum linenum
3 from (select t.ID,
4 ....
11 from test_dream t
12 order by t.GMT_CREATE desc) r
13 WHERE rownum <= :1)
14 WHERE linenum >= :2;
13:42:13 SQL> @?/rdbms/admin/utlxpls
-----------------------------------------------------------------
Id | Operation | Name | Rows | Bytes | Cost
------------------------------------------------------------------
| SELECT STATEMENT | |1062 |257K |15 |
| VIEW | |1062 |257K |15 |
| COUNT STOPKEY | | | | |
| VIEW | |1062 |243K | 15|
| TABLE ACCESS BY INDEX ROWID| test_dream |1062 |50976| 15|
| INDEX FULL SCAN DESCENDING| IND_DREAM_TIME|1062 | | 4|
------------------------------------------------------------------
搞完這兩步後,服務器負載立刻降下來在10之內。web分頁其實有很多種實現方式,這個例子是最普通的寫法,更多的例子在piner書裏面有詳細的介紹.http://www.ixdba.com/html/y2008/m01/195-book-information.html
通常來說,任何一條部署到生產庫上的sql語句都必須通過dba的審覈,這兩個sql語句開發趕着發佈,沒有提交到dba這邊來,差點就把數據庫服務器整垮了。這明顯是不遵守流程引起的故障,看來sql審覈真的很需要,遵守流程真的很重要,