Oracle營運案例之反序函數索引的使用

來源:互聯網
上載者:User

標籤:Oracle   Sql最佳化   

給大家分享一個我們分組遇到的sql最佳化的案例,案例非本人所負責的資料庫,本人只是搬運工。
這個案例發生在去年,發現原因是nets主機cpu上升,開發運營找到了DA,隨後DA對其情況進行了分析,最後定位到一條低效SQL展開分析。(關於DA,是平安集團資料庫技術部對DBA的一個細分)

以下就是定位到的sql:

SELECT /*+ index(c IDX_CALLINFO_UPDATED_DATE) */COUNT(1)  FROM T_SOFTPHONE_CALLINFO CWHERE C.updated_date >= sysdate - 1 / 48   and (C.ANI like ‘%‘ || :1 or C.DANI like ‘%‘ || :2)   and C.CREATED_BY = :3;

首先,根據這條SQL的相關表(T_SOFTPHONE_CALLINFO)瞭解到以下資訊。
1.這是一張電話呼入的資訊表(這裡情境做COUNT統計)
2.ANI和DANI傳入的變數是電話號碼,一個是有線電話號碼,一個是手機號碼
3.使用的HINT索引是時間欄位(updated_date)常規B-TREE索引,執行計畫也是走的此索引RANGE方式,這點沒有問題。

對上訴SQL有一定瞭解之後,DA首先收集了一下下曆史執行的一些情況,結論如下:
通過對比最近幾個月的增長情況,發現雖然執行計畫沒有改變,但是執行頻率從原來每15分鐘1000次增加到大約60000次,單次邏輯讀的消耗也增長了數倍,隨著業務量和資料量的攀升,這種時間欄位的索引方式越來越低效,成為一條隱患sql,在某個時間點問題就一下子就凸顯出來了,急需最佳化改進。
ps:此時nets的體量已接近30TB

對此,DA提出了一些意見和質疑:
1、 第一、第二個參數都是手機號,而且從曆史來看都是輸入的完整的手機號碼,為啥要用like,能否直接改成等號?
2、 兩個電話欄位都有單獨的索引,如果不用like,可以將以上SQL語句最佳化一下,走對應的電話號碼索引,改寫形式類似如下方式:

SELECT (SELECT /*+ index(c IDX_SOFTPHONE_CALLINFO_ANI) */         COUNT(1)          FROM T_SOFTPHONE_CALLINFO C         WHERE C.updated_date >= sysdate - 1 / 48           and (C.ANI = ‘159******22‘)           and C.CREATED_BY = ‘*******880‘) +       (SELECT /*+ index(c IDX_SOFTPHONE_CALLINFO_DANI) */         COUNT(1)          FROM T_SOFTPHONE_CALLINFO C         WHERE C.updated_date >= sysdate - 1 / 48           and (C.DANI = ‘159******22‘)           and C.CREATED_BY = ‘******880‘)         FROM DUAL;

通過這種改寫方式,原來的平均邏輯讀從3萬可以降低到200左右,這是極大的效率提升。
但是之後,開發和運營給了新的業務反饋,發現事情並沒有這麼簡單了。
開發運營:T_SOFTPHONE_CALLINFO中的電話號碼是從隨機資料中擷取的,可能包含有0等首碼,如果要統計到所有資訊,無法直接使用等號,加0和不加0,與電話呼入所在地有關,外地加0,本地不加0,你在A地呼95511,可能沒加0;你在B地用相同的手機號呼95511,就可能加0。經過DA的排查確實如此,手機號甚至有還有特殊取代符號的存在。
思考:
既然如此,看樣子,LIKE的方式無法改變了,欄位前使用%會抑制索引的使用,這樣就無法用到對應的索引,如何規避這個問題並且使用到高效的索引呢?通過自己的思考和同事的建議,結合目前的業務情境,給出了一個可靠的方案,就是建立一個函數索引,反序函數索引!

當機立斷,在想到方法後立即進行了測試和分析階段。
1.首先建立了兩個對應欄位的函數索引

Create index NETS2DATA.IDX_SOFTPHONE_CI_ANI_REV on NETS2DATA.T_SOFTPHONE_CALLINFO(reverse(ani)) tablespace NETS2DATA parallel 8 ;Createindex NETS2DATA.IDX_SOFTPHONE_CI_DANI_REV on NETS2DATA.T_SOFTPHONE_CALLINFO(reverse(dani)) tablespace NETS2DATA parallel 8;

2.改寫了sql

SELECT COUNT(1)FROM (SELECT /*+index(c IDX_SOFTPHONE_CI_ANI_REV) */        C.CALLINFO_ID         FROM T_SOFTPHONE_CALLINFO C        WHERE C.updated_date >= sysdate - 1 / 48          and (reverse(C.ANI) like :1 || ‘%‘)          and C.CREATED_BY = :2       UNION ALL       SELECT /*+index(c IDX_SOFTPHONE_CI_DANI_REV) */        C.CALLINFO_ID         FROM T_SOFTPHONE_CALLINFO C        WHERE C.updated_date >= sysdate - 1 / 48          and (reverse(C.DANI) like :3 || ‘%‘)          and C.CREATED_BY = :4);

效能測試下來,在大多數情境下效率提升都非常明顯,原來平均幾十萬的消耗基本區間維持在到幾百,原來的走的是時間欄位索引,現在走的是兩個電話號碼欄位的反序函數索引,於是當即開發就安排第一輪整改,期待有好的效果。
附執行計畫類似如下:

Execution Plan----------------------------------------------------------Plan hash value: 1437385812----------------------------------------------------------------------------------------------------------| Id | Operation                   | Name                      | Rows | Bytes | Cost (%CPU)| Time    |----------------------------------------------------------------------------------------------------------|   0 |SELECT STATEMENT             |                           |    1 |    17 |    6   (0)| 00:00:01 ||   1 | SORT AGGREGATE             |                           |    1 |    17 |            |          ||* 2 |   TABLE ACCESS BY INDEX ROWID| T_SOFTPHONE_CALLINFO      |    1 |    17 |    6   (0)| 00:00:01 ||* 3 |    INDEX RANGE SCAN          | IDX_CALLINFO_UPDATED_DATE |    2 |       |    4   (0)| 00:00:01 |----------------------------------------------------------------------------------------------------------Predicate Information (identified byoperation id):---------------------------------------------------   2 - filter("ANI" LIKE ‘%152******96‘AND "ANI" IS NOT NULL)   3 -access("C"."UPDATED_DATE">[email protected]!-1)Statistics----------------------------------------------------------          8 recursive calls          0 db block gets    291086 consistent gets          0 physical reads          0 redo size       515 bytes sent via SQL*Net to client       492 bytes received via SQL*Net from client          2 SQL*Net roundtrips to/from client          0 sorts (memory)          0 sorts (disk)rows processedExecution Plan----------------------------------------------------------Plan hash value: 3534627589------------------------------------------------------------------------------------------------------| Id | Operation                   | Name                  | Rows | Bytes | Cost (%CPU)| Time    |------------------------------------------------------------------------------------------------------|   0 |SELECT STATEMENT             |                       |    1 |    17 |   831K (1)| 02:46:18 ||   1 | SORT AGGREGATE             |                      |    1 |    17 |            |          ||* 2 |   TABLE ACCESS BY INDEX ROWID| T_SOFTPHONE_CALLINFO |    1 |    17 |   831K (1)| 02:46:18 ||* 3 |    INDEX RANGE SCAN          | IDX_SOFTPHONE_ANI_ANT | 4989K|       | 14254   (1)| 00:02:52 |------------------------------------------------------------------------------------------------------Predicate Information (identified byoperation id):---------------------------------------------------   2 -filter("C"."UPDATED_DATE">[email protected]!-1)   3 - access(REVERSE("ANI") LIKE‘69******251%‘)      filter(REVERSE("ANI") LIKE ‘69******251%‘)Statistics----------------------------------------------------------          8 recursive calls          0 db block gets          137 consistent gets          0 physical reads          0 redo size       515 bytes sent via SQL*Net to client       492 bytes received via SQL*Net from client          2 SQL*Net roundtrips to/from client          0 sorts (memory)          0 sorts (disk)rows processed

但是,過了幾天之後,中間又發生了一些小插曲,開發反饋雖然大部分情境效率都是極高的,但仍有一小部分情境效率較差,帶入值後消耗較高,雖然那些值的情境可能不多,但也會偶爾出現。DA分析馬上想到了是否是資料出現傾斜的情況,才會導致少部分值效率差。
在猜測了情況後,馬上登陸系統去查看了一下著這張的資料扭曲情況,果不其然,有些值傾斜非常厲害,有一個800萬,還有很多100到200萬欄位值,當取到這些極值的時候,光靠一個單值索引,效率必定很差,下:

於是進入了新一輪的思考分析,如何整改能滿足所有情境,是否能直接建立更高效的索引?刹那間發現這條sql使用了三個條件(updated_date,ani(dani), CREATED_BY),開發也提供思路說,在三個條件下過濾出來的資料並不會很多,這時候就有新的思路,能否建立一個複合索引呢,按選擇性排列,是否會有驚人的效果?
話不多說,馬上開啟了新一輪的效能測試分析,通過幾種組合的複合索引和單值索引測試,具體步驟不必多說了,請直接看下列測試資料:

想必經過反覆的效能分析測試和實驗,結合上面的測試資料,大家已經知道哪種方式最好了。最後我們也採用了最適合這個情境的改造方案,又進行了一輪整改,監控了後面的幾天運行情況,效果極佳,終於完全解決了所有的問題,皆大歡喜,覺得是個不錯的案例,給大分享一下!

最後在給大家普及一下,可能有人同學會搞錯反序索引和反序函數索引,這是不同的概念:
反向索引也是一種B樹索引,但它物理上將按照列順序儲存的每個索引索引值進行了反轉。例如,索引鍵是20,用16進位儲存這個標準B樹索引鍵的兩個位元組是C1,15,那麼反向索引儲存的位元組就是15,C1,目的主要是減少打散索引葉子塊的爭用,針對大並發插入情境比較實用,但弊端也比較明顯,當使用範圍查詢時,由於資料分散在不同塊內,效能也會有所降低。
函數索引是一種基於函數使用的索引,針對某些欄位使用特殊函數時,如果需要使用索引可以建立相關的索引,這個案例情境中,我需要實現的是將數字進行完全顛倒(並非位元組顛倒),概念有所不用,更多是站在查詢效率和情境使用的角度,所以綜合考慮更適用於反序函數的使用,並且建立相關反序函數的索引。

心得:
這一案例涉及的sql很簡單,但要求DA具備紮實的基本功及良好的業務嗅覺。在資料庫愈發智能、日常營運愈發簡單的今天,DBA需與業務深度融合,根據業務特點進行sql最佳化及架構設計。

Oracle營運案例之反序函數索引的使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.