1. 程式人生 > >SQL優化案例精彩連載(一)

SQL優化案例精彩連載(一)

導讀

知數堂致力於為學員的職場之路保駕護航,課上課下不遺餘力為廣大學員排憂解難!

SQL優化課鄭鬆華老師根據幫學員優化的案例為原型,和大家分享各式優化思路,本文為連載系列第一篇,將不定期陸續更新,敬請關注!

待優化場景

執行計劃見下:

640?wx_fmt=png

該表的幾個索引見下:

640?wx_fmt=png

SQL優化之路

首先,我們先review下這個SQL:

mysql> select id, user_id from rs

where id> 6350261   

and mutual_plan_id= 1   

and state= 10   

and actived_at< '2017-04-21'   

and mod(id, '16')= '5' 

order by id asc  limit 5000\G

從執行計劃中 Extra 部分有 Using filesort ,而且rows 有將近100w,說明索引選用的選擇率很不理想;並且這個SQL的最大效能瓶頸就是額外排序,並且條件中有函式不能用到索引

其次,我們要考慮,為什麼會出現這種問題呢 ?

我們從表結構中可以得出 id 為主鍵,WHERE 條件裡含有 id 列,按理應該選擇主鍵索引才對,但執行計劃中並沒選中它,因為條件中還包含了 mod(id, '16') =  '5' ,導致優化器認為同時存在函式轉換,沒有優先選擇主鍵索引


第三步,既然我們找到原因了,那就改寫下SQL,好讓優化器能優先選擇主鍵索引。

SQL改寫的做法是:

  1. 改造成子查詢;

  2. 將 mod(id, '16')= '5'  條件放在子查詢外層;

  3. 在子查詢裡增加 distinct(大家猜一下distinct的作用是什麼呢)。

第四步,我們再看下新的SQL及其執行計劃:

mysql> select 

a.id 

from (

 select distinct id   from rs  force index (primary )

 where id> 6350261    

 and mutual_plan_id= 1    

 and state= 10    

 and actived_at< '2017-04-21' 

)a where mod(id, '16')= '5' 

limit 5000 

執行計劃如下:

640?wx_fmt=png

可以看到,Using filesort 被消除了。

順便說下,前面加上 distinct 的作用是為了 防止檢視合併

最後,我們利用子查詢 以及 LIMIT 1 將整個SQL 改寫成如下:

mysql> select

b.id ,(select c.user_id from rs c where

c.id= b.id limit 1) user_id from (

select

a.id

from (

select distinct id   from rs  force index (primary )

where id> 6350261    

and mutual_plan_id= 1    

and state= 10    

and actived_at< '2017-04-21'

)a where mod(id, '16')= '5'

limit 5000

) b

執行計劃見下:

640?wx_fmt=png

優化結果

SQL執行時間由修改之前的 12s 多下降到 6s 內

因為 WHERE條件中有 MOD() 函式,沒辦法用到索引,所以提升效果不是太明顯

延伸方案

1、如果是用MySQL 5.7版本,可以選擇建立虛擬列並加索引,類似下面的做法:

ALTER TABLE rs ADD modid INT(11) UNSIGNED GENERATED ALWAYS AS ( MOD(id, '16') ) NOT NULL,
ADD INDEX idx_modid(modid);

2、如果是MySQL 5.7以前的版本,則可以考慮把 mod() 函式轉化成子查詢,這個相對麻煩點,我們以後找機會再討論。