前言
最近碰到一個單條SQL運行效率不佳導致數據庫整體運行負載較高的問題。
分析、定位數據庫的主要負載是這條語句引起的過程相對簡單,通過AWR報告就可以比較容易的完成定位,這里就不贅述了。
現在直接看一下這個導致性能問題的SQL語句,其對應的SQL REPORT統計如下:
Stat Name | Statement Total | Per Execution | % Snap Total |
Elapsed Time (ms) | 363,741 | 363,740.78 | 8 .42 |
CPU Time (ms) | 362,770 | 362,770.00 | 8 .81 |
Executions | 1 | ||
Buffer Gets | 756 | 756.00 | 0.00 |
Disk Reads | 0 | 0.00 | 0.00 |
Parse Calls | 1 | 1.00 | 0.01 |
Rows | 50,825 | 50,825.00 | |
User I/O Wait Time (ms) | 0 | ||
Cluster Wait Time (ms) | 0 | ||
Application Wait Time (ms) | 0 | ||
Concurrency Wait Time (ms) | 0 | ||
Invalidations | 0 | ||
Version Count | 1 | ||
Sharable Mem(KB) | 28 |
從SQL的性能指標上看,其單次執行需要6分鐘左右,處理5萬多條記錄,邏輯度只有756,主要消耗時間在CPU上。而這里就存在疑點,邏輯讀如此之低,而CPU時間花費又如此之高,那么這些CPU都消耗在哪里呢?當然這個問通過SQL的統計信息中是找不到答案的,我們下面關注SQL的執行計劃:
Id | Operation | Name | Rows | Bytes | TempSpc | Cost (%CPU) | Time |
0 | SELECT STATEMENT | 1226 (100) | |||||
1 | SORT ORDER BY | 49379 | 3375K | 3888K | 1226 (2) | 00:00:05 | |
2 | HASH JOIN ANTI | 49379 | 3375K | 2272K | 401 (3) | 00:00:02 | |
3 | TABLE ACCESS FULL | T_NUM | 49379 | 1687K | 88 (4) | 00:00:01 | |
4 | TABLE ACCESS FULL | T_NUM | 49379 | 1687K | 88 (4) | 00:00:01 |
從執行計劃看,Oracle選擇了HASH JOIN ANTI,JOIN的兩張表都是T_NUM,且都采用了全表掃描,并未選擇索引。僅靠執行計劃也只等得到上面的結論,至于為什么不選擇索引,以及為什么執行時間過長,還需要進一步的分析。
將原SQL進行簡單脫密改寫后, SQL文本類似如下:
SELECT BEGIN, END, ROWID, LENGTH(BEGIN)FROM T_NUM AWHERE NOT EXISTS (SELECT 1FROM T_NUM BWHERE B.BEGIN <= A.BEGINAND B.END >= A.ENDAND B.ROWID != A.ROWIDAND LENGTH(B.BEGIN) = LENGTH(A.BEGIN));
如果分析SQL語句,會發現這是一個自關聯語句,在BEGIN字段長度相等的前提下,想要找到哪些不存在BEGIN比當前記錄BEGIN小且END比當前記錄END大的記錄。
簡單一點說,表中的記錄表示的是由BEGIN開始到END截至的范圍,那么當前想要獲取的結果是找出哪些沒有范圍所包含的范圍。需要注意的是,對于當前的SQL邏輯,如果存在兩條范圍完全相同的記錄,那么最終這兩條記錄都會被舍棄。
業務的邏輯并不是特別復雜,但是要解決一條記錄與其他記錄進行比較,多半采用的方法是自關聯,而在這個自關聯中,既有大于等于又有小于等于,還有不等于,僅有的一個等于的關聯條件,來自范圍段BEGIN的長度的比較。
顯而易見的是,如果是范圍段本身的比較,其選擇度一般還是不錯的,但是如果只是比較其長度,那么無疑容易產生大量的重復,比如在這個例子中:
SQL> select length(begin), count(*) from t_num group by length(begin) order by 2 desc; LENGTH(BEGIN) COUNT(*)――――- ―――-12 2209611 901113 899914 818616 499 458 417 27
大量重復的數據出現在長度為11到14的范圍上,在這種情況下,僅有的一個等值判斷條件LENGTH(BEGIN)是非常低效的,這時一條記錄根據這個等值條件會關聯到近萬條記錄,設置關聯到兩萬多條記錄,顯然大量的實踐消耗在低效的連接過程中。
再來看一下具體的SQL語句,會發現幾乎沒有辦法建立索引,因為LENGTH(BEGIN)的選擇度非常查,而其他的條件都是不等查詢,選擇度也不會好,即使建立索引,強制執行選擇索引,效率也不會好。
那么如果想要繼續優化這個SQL,就只剩下一個辦法,那就是SQL的改寫。對于自關聯查詢而言,最佳的改寫方法是利用分析函數,其強大的行級處理能力,可以在一次掃描過程中獲得一條記錄與其他記錄的關系,從而消除了自關聯的必要性。
SQL改寫結果如下:
SELECT BEGIN, OLDEND END, LENGTH(BEGIN)FROM (SELECT BEGIN, OLDEND, END, LENGTH(BEGIN), COUNT(*) OVER(PARTITION BY LENGTH(BEGIN), BEGIN, OLDEND) CN,ROW_NUMBER() OVER(PARTITION BY LENGTH(BEGIN), END ORDER BY BEGIN) RNFROM(SELECT BEGIN, END OLDEND, MAX(END) OVER(PARTITION BY LENGTH(BEGIN) ORDER BY BEGIN, END DESC) ENDFROM T_NUM))WHERE RN = 1AND CN = 1;
簡單的說,內層的分析函數MAX用來根據BEGIN從小到大,END從大到小的條件,確定每個范圍對應的最大的END的值。而外層的兩個分析函數,COUNT用來去掉完全重復的記錄,而ROW_NUMBER用來獲取范圍最大的記錄(也就是沒有被其他記錄的范圍所涵蓋)。
改寫后,這個SQL避免對自關聯,也就不存在關聯條件重復值過高的性能隱患了。在模擬環境中,性能對比如下:
SQL> SELECT BEGIN, END, ROWID, LENGTH(BEGIN)2 FROM T_NUM A3 WHERE NOT EXISTS (4 SELECT 15 FROM T_NUM B6 WHERE B.BEGIN <= A.BEGIN7 AND B.END >= A.END8 AND B.ROWID != A.ROWID9 AND LENGTH(B.BEGIN) = LENGTH(A.BEGIN))10 ; 48344 rows selected. Elapsed: 00:00:57.68 Execution Plan―――――――――――――――――――-Plan hash value: 2540751655 ――――――――――――――――――――――――――――| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |――――――――――――――――――――――――――――| 0 | SELECT STATEMENT | | 48454 | 1703K| | 275 (1)| 00:00:04 ||* 1 | HASH JOIN ANTI | | 48454 | 1703K| 1424K| 275 (1)| 00:00:04 || 2 | TABLE ACCESS FULL| T_NUM | 48454 | 851K| | 68 (0)| 00:00:01 || 3 | TABLE ACCESS FULL| T_NUM | 48454 | 851K| | 68 (0)| 00:00:01 |―――――――――――――――――――――――――――― Predicate Information (identified by operation id):――――――――――――――――― 1 亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb 成人午夜激情免费视频| 国产日韩欧美中文在线播放| 成人久久久久久| 亚洲国产成人在线播放| 欧美性生活大片免费观看网址| 欧美激情日韩图片| 最近2019年中文视频免费在线观看| 亚洲情综合五月天| 欧美尺度大的性做爰视频| 成人动漫网站在线观看| 久久欧美在线电影| 成人做爽爽免费视频| 亚洲自拍小视频免费观看| 黑人巨大精品欧美一区二区三区| 色中色综合影院手机版在线观看| 97精品一区二区视频在线观看| 亚洲国产精品专区久久| 国产精品7m视频| 91精品国产91久久| 久久久天堂国产精品女人| 亚洲第一精品久久忘忧草社区| 欧美激情中文字幕乱码免费| 亚洲xxx大片| 欧美性视频在线| 在线播放国产一区中文字幕剧情欧美| www.美女亚洲精品| 国产精品永久免费视频| 亚洲激情视频网| 亚洲一级黄色片| 欧美激情精品久久久久| 久久精品国产一区二区三区| 久久久久久亚洲精品| 91精品国产综合久久久久久久久| 国产欧美精品一区二区三区介绍| 成人精品久久一区二区三区| 夜夜嗨av一区二区三区四区| 国产91|九色| 国产欧美精品久久久| 九九九热精品免费视频观看网站| 欧美在线影院在线视频| 欧美成人合集magnet| 黑人巨大精品欧美一区二区三区| 成人信息集中地欧美| 久久久中精品2020中文| 亚洲精品电影网| 精品久久久久久久久久ntr影视| 亚洲精品在线视频| 日韩国产精品亚洲а∨天堂免| 国产69精品久久久| 亚洲成人久久久久| 国产精品高潮在线| 久久国产精品偷| 成人高清视频观看www| 日本一区二区三区四区视频| 亚洲欧洲成视频免费观看| 精品视频在线播放免| 久久久最新网址| 国产日韩综合一区二区性色av| 亚洲网站视频福利| 久久天天躁狠狠躁夜夜躁2014| 欧美一级大片视频| 亚洲国产一区二区三区在线观看| 91精品久久久久久久久久| 在线观看国产精品日韩av| 欧美日韩国产精品一区二区不卡中文| 亚洲精品久久久久中文字幕二区| 亚洲伊人久久综合| 国产在线高清精品| 国产精品成人aaaaa网站| 欧美成人免费一级人片100| 日韩精品视频在线观看网址| 亚洲a成v人在线观看| 久久免费观看视频| www.久久色.com| 国产区精品在线观看| 日韩精品极品毛片系列视频| 在线不卡国产精品| 国产精品久久久久久久app| 欧美性猛交99久久久久99按摩| 国产噜噜噜噜噜久久久久久久久| 国内揄拍国内精品少妇国语| 美女性感视频久久久| 欧美一区在线直播| 日韩av免费在线播放| 久久久久久久久久久亚洲| 91精品国产高清久久久久久久久| 欧美国产日韩精品| 亚洲精品国产品国语在线| 97精品久久久| 久久综合久久美利坚合众国| 精品久久久久久久久久国产| 国产日韩在线看| 国语对白做受69| 91免费看片在线| 欧美丰满老妇厨房牲生活| 久久久这里只有精品视频| 91精品国产91久久久| 日本精品一区二区三区在线| 亚洲成av人影院在线观看| 亚洲午夜久久久影院| 国产日韩视频在线观看| 日韩高清a**址| 亚洲精品丝袜日韩| 欧美人成在线视频| 国产精品久久久精品| 在线视频精品一| 亚洲理论在线a中文字幕| 蜜月aⅴ免费一区二区三区| 亚洲第一色中文字幕| wwwwwwww亚洲| 中文字幕亚洲激情| 欧美日韩精品在线| 国产精品美女免费| 国产日韩欧美影视| 国产精品久久久91| 国产成人97精品免费看片| 久久人人爽国产| 欧美视频国产精品| 日韩av不卡在线| 久久精品中文字幕| 91久久精品美女| 国产亚洲精品美女| 色综合天天狠天天透天天伊人| 欧美日韩国产区| 国产狼人综合免费视频| 国产精品视频专区| 亚洲欧美一区二区精品久久久| 欧美性猛交xxxx乱大交极品| 国产69精品久久久久9| 久久久免费在线观看| 最近的2019中文字幕免费一页| 久久成人综合视频| 国产精品日韩久久久久| 久久久久久成人| 国产精品色午夜在线观看| 亚洲bt欧美bt日本bt| 亚洲成色999久久网站| 日韩电影免费观看中文字幕| 欧美日韩国产综合视频在线观看中文| 亚洲**2019国产| 久久精品青青大伊人av| 一本色道久久综合狠狠躁篇怎么玩| 国产亚洲在线播放| 神马国产精品影院av| 国产精品九九九| 中文字幕亚洲无线码a| 国产精品一区二区在线| 国产三级精品网站| 91香蕉嫩草神马影院在线观看| 国产精品日韩欧美| 中文字幕日韩精品有码视频| 国产精品日韩精品| 国产在线观看一区二区三区| 国产视频一区在线| 欧美性猛交xxxx黑人| 久久精品91久久久久久再现| 欧美视频裸体精品| 亚洲午夜精品久久久久久久久久久久| 久久久国产一区二区三区| 欧美美女15p| 岛国av一区二区在线在线观看| 亚洲天天在线日亚洲洲精| 久久久www成人免费精品张筱雨| 庆余年2免费日韩剧观看大牛|