MySQL 全索引掃描的bug

來源:互聯網
上載者:User

MySQL 全索引掃描的bug

一 簡介

在檢查某業務資料庫的slowlog 時發現一個慢查詢,查詢時間 1.57s ,檢查表結構 where條件欄位存在正確的複合式索引,正確的情況下最佳化器應該選擇複合式索引,而非為啥會導致慢查詢呢? 且看本文慢慢分析。

二 分析

案例中的MySQL資料庫版本 5.6.16 將生產環境的sql做適當修改,where條件不變。讀者朋友可以測試一下其他的版本。

root@rac1 10:48:11>explain select id,
 
    -> gmt_create,

    -> gmt_modified,

    -> order_id,

    -> service_id,

    -> seller_id,

    -> seller_nick,

    -> sale_type

    -> from lol

    -> where seller_id= 1501204

    -> and service_id= 1

    -> and sale_type in(3, 4)

    -> and use_status in(3, 4, 5, 6)

    -> and process_node_id= 6 order by id desc limit 0,20 \G

*************************** 1. row ***************************

          id: 1

  select_type: SIMPLE

        table: lol

        type: index

possible_keys: idx_sellerid,idx_usestatus_saletype,idx_sellerid_saletype,idx_sidustsvidtype

          key: PRIMARY

      key_len: 8

          ref: NULL

        rows: 3076

        Extra: Using where

1 row in set (0.00 sec)

分析

MySQL選擇的執行計畫利用主鍵進行訪問資料。注意執行計畫中的 access type是index,而index 意味著這個SQL在查詢二級索引的時候,對二級索引進行了全索引掃描,根本沒有進行過濾

這個行為是不合理的,因為where條件中含有 in 查詢,合理的執行計畫的access type應該是range。

我們採用強制索引,看看結果

root@rac1 10:48:07>explain select id,
 
    -> gmt_create,

    -> gmt_modified,

    -> order_id,

    -> service_id,

    -> seller_id,

    -> seller_nick,

    -> sale_type

    -> from lol force index(idx_sidustsvidtype)

    -> where seller_id= 1501204

    -> and service_id= 1

    -> and sale_type in(3, 4)

    -> and use_status in(3, 4, 5, 6)

    -> and process_node_id= 6 order by id desc limit 0,20 \G

*************************** 1. row ***************************

          id: 1

  select_type: SIMPLE

        table: lol

        type: range

possible_keys: idx_sidustsvidtype

          key: idx_sidustsvidtype

      key_len: 19

          ref: NULL

        rows: 5178

        Extra: Using where; Using filesort

1 row in set (0.00 sec)

分析

強制加上索引之後的執行計畫是符合預期的,執行sql的時間由 1.57s 減少為 0.01s 。因此我們推測是在最佳化器選擇索引的時候出現了問題

結合源碼和optimize_trace我們發現第一階段最佳化的時候,最佳化器確實選擇了idx_sidustsvidtype 並且選擇採用range訪問,因為sql 語句中含有order by,在optimizer試圖最佳化 order by limit的時候

清空了儲存訪問方式的quick變數(原本儲存的是range,但是被請空),最終發現採用排序索引(這裡是id)的代價高於複合式索引(這裡是idx_sidustsvidtype)時,還是選擇了idx_sidustsvidtype

但是悲劇的是這時候正確的訪問方式已經被清空,無法還原,這就是這個 bug#78993  的根本成因。

根據分析,我們還可以使用另一種解決方案----去掉 order by 。當然這個對業務所有入侵必須和開發溝通確認sql的結果集是否唯一,如果不唯一還是要使用其他方法。

root@rac1 10:48:15>explain select id,
 
    -> gmt_create,

    -> gmt_modified,

    -> order_id,

    -> service_id,

    -> seller_id,

    -> seller_nick,

    -> sale_type

    -> from lol

    -> where seller_id= 1501204

    -> and service_id= 1

    -> and sale_type in(3, 4)

    -> and use_status in(3, 4, 5, 6)

    -> and process_node_id= 6 \G

*************************** 1. row ***************************

          id: 1

  select_type: SIMPLE

        table: lol

        type: range

possible_keys: idx_sellerid,idx_uts_stp,idx_sid_stpe,idx_sidustsvidtype

          key: idx_sidustsvidtype

      key_len: 19

          ref: NULL

        rows: 5178

        Extra: Using where

1 row in set (0.00 sec)

三 總結
a 修改SQL,添加正確hint。
b 去掉不必要的order by 需要和開發溝通確認是否影響商務邏輯。
c 修改最佳化的bug,保留多個訪問路徑,不清理儲存訪問方式的quick變數,發現orderby 的代價高於複合式索引時,可以選擇最優的訪問路徑。

特別感謝 江疑 的分析,同時也推薦《排序sql升級MySQL 5.6變慢原因分析》 

本文永久更新連結地址:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.