1. 程式人生 > 其它 >通過IP定位區域的SQL優化思路(r10筆記第10天)

通過IP定位區域的SQL優化思路(r10筆記第10天)

昨天中午吃飯的時候,突然手機收到一條報警資訊,提示資料庫的負載突然提高了。對於一個高配,穩定,核心的系統來說,出現這麼一個報警會立刻引起關注。 連線到環境之後,發現在問題發生時間段快照中資源消耗較大的SQL情況如下:

可以看到有兩個語句的執行頻率還是比較高,整體佔用了絕大多數的CPU資源。 對應的前兩個SQL語句如下: SQL_FULLTEXT ---------------------------------------------------------------------------------------------------- SELECT IP_ID,COUNTRY,PROVINCE,CAPITAL FROM SWD_IP2COUNTY WHERE STRIPTOINT(:B1 ) BETWEEN IP2NUM_LEFT_LINE AND IP2NUM_RIGHT_LINE

看起來語句很簡單,第一感覺是不是全表掃描導致的,也就潛意識中感覺是不是沒有相關的索引。 但是查看錶結構資訊,發現在IP2NUM_LEFT_LINE和IP2NUM_RIGHT_LINE有兩個索引,索引最初的猜想不成立。 然後開始看這個語句的細節,發現這個語句還真是有點意思,之前還沒見過這種寫法。 首先是裡面看起來是是存在一個函式STRIPTOINT處理繫結變數,然後有一個過濾條件between xxx and xxx,兩端的可是欄位,不是具體的數值。 如果函式STRIPTOINT處理某個欄位的數值,那麼就是一種使用不規範的情況,肯定會走索引,但是這裡的情況又有些特殊。 看到這個邏輯,我是有些懵了,咱也不能隨便猜。看看錶結構。 COLUMN_ID COLUMN_NAME DATA_TYPE DATA_LENGTH NULLABLE
---------- ------------------------------ --------------- ----------- ---------- 1 IP_ID NUMBER(10,0) 22 N 2 IP_LEFT_LINE VARCHAR2(15) 15 N 3 IP_RIGHT_LINE VARCHAR2(15) 15 N 4 IP2NUM_LEFT_LINE NUMBER(10,0) 22 N 5 IP2NUM_RIGHT_LINE NUMBER(10,0) 22 N 6 COUNTRY VARCHAR2(20) 20 Y 7 PROVINCE VARCHAR2(20) 20 Y 8 CAPITAL VARCHAR2(20) 20 Y
可以看出是和IP相關的,而且裡面存在一些地理區域的資訊。 函式STRIPTOINT是對傳入的IP做過濾,比如傳入一個IP 10.1.3.5 ,函式會把這個IP按照".”進行分隔,從第一部分開始到第四部分,會逐步拉開資料的梯度。 比如傳入一個IP,就會轉換為一個較大的數值,儘可能保證不會重複。 SQL> select STRIPTOINT('124.115.229.74') from dual; STRIPTOINT('124.115.229.74') ---------------------------- 208797012 所以這個SQL語句就類似下面的形式。 SELECT IP_ID,COUNTRY,PROVINCE,CAPITAL FROM tlbb.SWD_IP2COUNTY WHERE 208797012 BETWEEN IP2NUM_LEFT_LINE AND IP2NUM_RIGHT_LINE 這樣理解起來還真是有點費勁,我們繼續介紹一些相關的業務情況,這些也是我根據資料猜出來的,後面和開發的同學聊,和我想的是一樣的。 比如資料是這樣的形式:


 IP_LEFT_LINE    IP_RIGHT_LINE   IP2NUM_LEFT_LINE IP2NUM_RIGHT_LINE COUNTRY    PROVINCE   CAPITAL
----- --------------- --------------- ---------------- ----------------- ---------- ---------- -------
  5.34.184.0      5.34.191.255            86161408          86163455  中國       北京       北京  
  5.34.192.0      5.34.223.255            86163456          86171647  中國             河北       石家莊

比如IP 5.34.184.0~5.34.191.255 這個區間代表的是北京地區,5.34.192.0 ~ 5.34.223.255 這個區間代表的是河北石家莊。那麼傳入一個IP就開始對映得到一個數值,通過這個範圍區間來找到對應的地區。 這樣一個語句,明白了需求,好像還是有點道理。 來看看awr的SQL報告怎麼說吧。 Snap Id Snap Time Sessions Curs/Sess --------- ------------------- -------- --------- Begin Snap: 2397 01-Sep-16 13:00:52 567 1.9 End Snap: 2398 01-Sep-16 13:30:54 566 1.9 Elapsed: 30.03 (mins) DB Time: 138.02 (mins) 可以看到在短期內,資料庫的負載還是比較高。 而SQL的執行統計資訊如下:

如果想得到更多的詳細的資訊,使用診斷事件也是不錯的選擇,這也是11g的一個功能。 比如對某一條SQL開啟sql_trace,可以使用如下的方式。 開啟: alter system set events 'sql_trace [sql: 63f970ck8r3kc] level 12'; 關閉: alter system set events 'sql_trace [sql: 63f970ck8r3kc] off'; 而如果有大量的會話頻繁呼叫,那還是不建議使用的,會生成大量的trace檔案,目前的情況就是如此,需要謹慎使用。 而我們怎麼去分析這個SQL呢,來做一個10053事件吧。 ALTER SESSION SET EVENTS='10053 trace name context forever, level 1'; SELECT IP_ID,COUNTRY,PROVINCE,CAPITAL FROM test.SWD_IP2COUNTY WHERE 208797012 BETWEEN IP2NUM_LEFT_LINE AND IP2NUM_RIGHT_LINE; ALTER SESSION SET EVENTS '10053 trace name context off'; 這個時候trace的資訊可以看到,內部做了查詢轉換,會把原有的語句轉換為下面的形式: Final query after transformations:******* UNPARSED QUERY IS ******* SELECT "SWD_IP2COUNTY"."IP_ID" "IP_ID","SWD_IP2COUNTY"."COUNTRY" "COUNTRY","SWD_IP2COUNTY"."PROVINCE" "PROVINCE","SWD_IP2C OUNTY"."CAPITAL" "CAPITAL" FROM "TEST"."SWD_IP2COUNTY" "SWD_IP2COUNTY" WHERE "SWD_IP2COUNTY"."IP2NUM_LEFT_LINE"<=208797012 AND "SWD_IP2COUNTY"."IP2NUM_RIGHT_LINE">=208797012 對於執行路徑和表資訊的統計資訊如下: Access path analysis for SWD_IP2COUNTY *************************************** SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for SWD_IP2COUNTY[SWD_IP2COUNTY] Column (#4): NewDensity:0.000006, OldDensity:0.000006 BktCnt:254, PopBktCnt:0, PopValCnt:0, NDV:163088 Column (#4): IP2NUM_LEFT_LINE( AvgLen: 7 NDV: 163088 Nulls: 0 Density: 0.000006 Min: 16910592 Max: 4261412864 Histogram: HtBal #Bkts: 254 UncompBkts: 254 EndPtVals: 255 Column (#5): NewDensity:0.000006, OldDensity:0.000006 BktCnt:254, PopBktCnt:0, PopValCnt:0, NDV:163246 Column (#5): IP2NUM_RIGHT_LINE( AvgLen: 7 NDV: 163246 Nulls: 0 Density: 0.000006 Min: 16912383 Max: 4278190079 Histogram: HtBal #Bkts: 254 UncompBkts: 254 EndPtVals: 255 Table: SWD_IP2COUNTY Alias: SWD_IP2COUNTY Card: Original: 163246.000000 Rounded: 1114 Computed: 1114.40 Non Adjusted: 1114.40 Access Path: TableScan Cost: 444.56 Resp: 444.56 Degree: 0 Cost_io: 443.00 Cost_cpu: 57439773 Resp_io: 443.00 Resp_cpu: 57439773 Access Path: index (RangeScan) Index: IND_IP2NUM_LEFT_LINE resc_io: 15.00 resc_cpu: 713197 ix_sel: 0.006874 ix_sel_with_filters: 0.006874 Cost: 15.02 Resp: 15.02 Degree: 1 Access Path: index (RangeScan) Index: IND_IP2NUM_RIGHT_LINE resc_io: 1902.00 resc_cpu: 101091934 ix_sel: 0.993126 ix_sel_with_filters: 0.993126 Cost: 1904.74 Resp: 1904.74 Degree: 1 ****** finished trying bitmap/domain indexes ****** Best:: AccessPath: IndexRange Index: IND_IP2NUM_LEFT_LINE Cost: 15.02 Degree: 1 Resp: 15.02 Card: 1114.40 Bytes: 0 最大的發現是就是這個語句的邏輯結合自己的分析終於清晰了,但是怎麼優化還沒想好。為什麼效能如此差。 我用查詢轉換後的語句,直接在客戶端執行,檢視執行計劃。 SQL> SELECT "SWD_IP2COUNTY"."IP_ID" "IP_ID","SWD_IP2COUNTY"."COUNTRY" "COUNTRY","SWD_IP2COUNTY"."PROVINCE" "PROVINCE","SWD_IP2COUNTY"."CAPITAL" "CAPITAL" FROM "TEST"."SWD_IP2COUNTY" "SWD_IP2COUNTY" WHERE "SWD_IP2COUNTY"."IP2NUM_LEFT_LINE"<=3030728598 AND "SWD_IP2COUNTY"."IP2NUM_RIGHT_LINE">=3030728598 ; 發現這個時候CBO是分析得出需要走全表掃描的。

Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 1530 consistent gets 。。。 1 rows processed 這樣就徹底明白了,這裡走索引掃描的選擇度本身就很低,其實效果和走全表差距也不大,這樣一來也就容易理解上面的統計資訊中大量的buffer gets的原因了。 這個語句如何優化呢,問題還是擺在我們面前,根據和開發同學的溝通,對這個需求有了更多的一些瞭解,而且根據IP來確定範圍,是不會出現多個區域的情況,也就是說,這個結果集要不是1條,要麼是沒有對映到的。 所以語句可以簡單優化一下,只需要新增rownum=1即可。 SELECT "SWD_IP2COUNTY"."IP_ID" "IP_ID","SWD_IP2COUNTY"."COUNTRY" "COUNTRY","SWD_IP2COUNTY"."PROVINCE" "PROVINCE","SWD_IP2COUNTY"."CAPITAL" "CAPITAL" FROM "TEST"."SWD_IP2COUNTY" "SWD_IP2COUNTY" WHERE "SWD_IP2COUNTY"."IP2NUM_LEFT_LINE"<=3030728598 AND "SWD_IP2COUNTY"."IP2NUM_RIGHT_LINE">=3030728598 and rownum=1 ; 這個時候走了索引,而且一致性讀大大降低。

Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 3 consistent gets 。。。 1 rows processed 這個語句在實際環境中的改進效果如何呢,我抓取了對比的資料,發現效能提升了50%。

執行計劃如下:

這種優化方式還是值得借鑑的。而且在這個基礎上可以考慮對索引進行優化,也就是建立符合索引,根據數值的分佈情況(存在》=,《=的數值區間),可以考慮對索引進行排序 比如:(IP2NUM_LEFT_LINE desc , IP2NUM_RIGHT_LINE asc)

這一點上還得感謝兔子的建議,看了下老白總結的案例就是這樣的改進,都是套路,哈哈。

對於效能的比對情況如下,DB time有了很多改進。