SQL優化 MySQL版 - 單表優化及細節詳講


單表優化及細節詳講

作者 : Stanley 羅昊

轉載請注明出處和署名,謝謝!

注:本文章需要MySQL數據庫優化基礎或觀看前幾篇文章,傳送門:

B樹索引詳講(初識SQL優化,認識索引):https://www.cnblogs.com/StanleyBlogs/p/10413349.html

B樹索引進階(索引分類、創建方式、刪除索引、查看索引、SQL性能問題):https://www.cnblogs.com/StanleyBlogs/p/10416865.html

SQL執行計划於笛卡爾積(了解什么是SQL執行計划,優化原理):https://www.cnblogs.com/StanleyBlogs/p/10422202.html

Type詳講(理解優化級別):https://www.cnblogs.com/StanleyBlogs/p/10426385.html

Extra(理解最終優化概念):https://www.cnblogs.com/StanleyBlogs/p/10429969.html

優化准備

首先我們需要有一個數據庫,bookdb,還要有一張數據表book,有以下字段,我們接下來將用以下這張表來做優化實例;

單表優化

此次教程不再使用可視化工具,因為效率太慢,我還是比較喜歡命令行操作;

下面我們需要編寫以下條件的SQL語句:

查詢authorid = 1並且 typeid為2或3的bid再根據typeid排序

SQL語句:select bid from book where typeid in (2,3) And authorid = 1 order by typeid desc;

我們執行這條SQL語句,然后在前面加上explatin查看sql執行計划:

我們可以清楚的看到,查詢級別是ALL,並且Useing where回表查詢了並且后面還有一個Using filesort表示創建臨時表了,可見此條SQL語句是多么的恐怖,效率極低

這個時候我們就來給它加個索引吧,畢竟都知道,加索引可以提高效率,那我們就來分析一下以上這個sql語句;

首先該語句里面有 bid typeid authorid這三個字段,那么接下來我將給它們創建一個復合索引:

索引名我起名為idx_bta代表它的順序 b 代表 bid t 代表 typeid a 就代表authorid;

加上索引后,我們再執行一下,看看我們這條sql語句有沒有被優化:

首先,我們可以看見,type級別被優化了一些,到了index了,也就是ALL的上一個級別,我在之前的文章也說過,最好優化到ref級別,可見我們現在這條SQL還是不夠優化,並且 我們后面還有Using filesirt,但是出現了Using index,說明還是優化了一些,但是遠遠不夠!

那么為什么呢?我明明加了索引,它居然還性能這么差?

原來,我們忽略了一點,就是SQL解析過程

我在前幾篇文章重點說過,編寫過程,解析過程是不一樣的:

編寫過程: 

select from join on where 條件 group by 分組 having過濾組 order by排序 limit限制查詢個數

解析過程:

from on join where group by having select order by limit 

以上就是mysql的解析過程,我們發現,跟我們編寫的過程完全不一致!

也就是說,我們盡管bid在前面,typeid跟authorid在后面,但是它實際執行的時候卻是先執行where(type、authorid),而不是select(bid);

但是我們索引順序是怎么建的?

是 b t a 的順序(bid typeid authorid),既然where我現在非要先讓bid先執行,很顯然不滿足最佳左前綴,就是從左向右依次執行,我現在的索引並沒有滿足,因為我現在卻讓最右邊的先執行了(bid)

所以,我們需要改變一下索引的順序,既然先解析where,我就讓where后面的倆字段放在前面(typeid authorid),把select放在后面(bid);

根據SQL實際解析的順序,調整索引的順序;

在建立這個索引之前,我們務必刪掉沒用的索引

刪掉后,我們把索引的順序改變一下,之前是 b t a 現在我改成 t a b(typeid authorid bid);

添加索引后,我並且查詢索引,發現創建成功了,我們再運行一下試試,這次我改變了索引順序,順序按照解析順序排列,看看這次的效果如何:

我們發現,type等級仍然是index,因為沒有創建臨時表了也就是額外的查詢,性能明顯提升了,但是我們的type等級仍是index,確實還沒有達到我們想要的ref標准,接下來我們繼續優化;

我們現在開始重點優化索引級別,很顯然,我們的索引級別是index,距離ref還有點距離;

再次優化

system>const>eq_ref>ref>range>index>ALL

很明顯啊,我們現在的這條sql才到index級別,我之前說過,最好達到ref或range級別;

我們來把之前的SQL拿過來:

 select bid from book where typeid in (2,3) And authorid = 1 order by typeid desc;

我現在將where條件后面這兩個字段換一個順序,為啥換順序呢,看這個in

我之前講過范圍查詢,in是有可能導致索引失效的,從而轉為無索引;

我現在思路是,如果in失效了或typeid失效了,那你authorid也就跟着一起失效了,為什么呢?

我們來看一下索引順序,我們是先typeid 后 authorid,如果你typeid都沒了,那么authorid也可能也受干擾了,所以我把它順序換換;

alter table book add index idx_atb(authorid,typeid,bid);

我現在讓它先authorid后typeid,那如果先a 后 t 那即是你 t 失效了,無所謂啊,我先a,a你也用了,這是個思路;

既然typeid會失效,那我們改變一下where后面的順序吧,既然你可能會失效,就把它往后放,別影響別人

explain select bid from book where authorid = 1 And typeid in (2,3) order by typeid desc;

務必也把索引順序也更改一下!

alter table book add index idx_atb(authorid,typeid,bid);

我們改變索引順序跟SQL語句where后面的順序后再執行:

我們再查看type級別會發現已經達到了fef級別,並且也有Using index,但是還有Using where

因為我typeid雖然也有索引,但是我使用了in,就索引失效了就跟typeid沒有索引一樣,這樣就造成了又需要回原表查了,所以盡量避免使用in;

小結:之所以這條SQL語句能達到了ref,是因為我滿足了最佳左前綴,跟處理了索引失效的問題,既然你要失效,我就不要讓你影響后面的字段,我就把你往后排,盡管你失效了,你不影響前面的,所以我把SQL語句where后面的兩個字段換了位置;

索引需要逐步優化,要實時查詢sql執行計划,采取逐步優化措施;

補充

剛剛在上方我把SQL語句字段where后面的字段調換了位置,並且索引也跟着調換了,其實經過測試,其實SQL語句無需調換,僅需調換索引位置即可:

今日感悟:

在你看不見的地方,總有你想不到的辛酸;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM