mysql索引-進階篇(爲查詢創建索引)

innodb索引基礎

很多調優人員(儘管沒經驗)認爲,如果一個SQL語句使用了索引,那這個SQL就是被很好地優化過的,我對此感到很驚訝。你應該總是問自己,“這是不是可用的最好的索引?”或“再添加另外一個索引能否提升響應性能?”,又或者“全表掃描會不會更快地返回結果?”

這是一個系列~ 會持續更新

1、爲SELECT語句創建索引

三星索引–理想的索引

  • 第一顆星:與一個查詢相關的索引行是相鄰的,或者足夠靠近的。即,最小化索引必須掃描的索引片的寬度
  • 第二顆星:索引行的順序和查詢語句的需求一致
  • 第三顆星:索引行包含查詢語句中的所有列

通俗的解釋:第一顆星決定了掃描行數;第二課星決定了是否需要對結果集排序;第三顆星決定了是否需要回表操作【註釋1,回表】

寬索引:把至少包含第三顆星的索引稱爲對應查詢語句的寬索引(mysql裏面稱爲覆蓋索引)

實例

例子1 (簡單sql的三星索引):

SELECT CNO, FNAME FROM CUST WHERE LNAME = :LNAME AND CITY = :CITY ORDER BY FNAME

如上簡單場景,一個三星索引的構造是比較簡單的:

  • 滿足第一顆星,取出所有等值謂詞的列(WHERE LNAME = …)。把這些列作爲索引開頭的列:idx(LNAME, CITY, …) 或者 idx(CITY, LNAME)
  • 滿足第二顆星,取出ORDER BY列,加入索引,得到:idx(LNAME, CITY, FNAME, …)或者 idx(CITY, LNAME, FNAME, …)。這樣結果集的記錄就無需排序了。
  • 滿足第三顆星,將剩餘的列加入索引中,得到:idx(LNAME, CITY, FNAME, CNO)或者idx(CITY, LNAME, FNAME, CNO)

例子2(範圍謂詞和三星索引):

SELECT CNO, FNAME FROM CUST WHERE LNAME BETWEEN :LNAME1 AND LNAME2 AND CITY = :CITY ORDER BY FNAME

分析:

  1. 如果要滿足第三星,毋庸置疑我們需要把 LNAME, CITY, FNAME, CNO都加入索引。現在我們要考慮的就是索引順序。
  2. 如果想要避免排序(滿足第二星),必須把FNAME加在LNAME的前面
  3. 如果將FNAME加在LNAME前面,將破壞第一星-- 查詢行索引需要相鄰

這時候就是一個取捨問題了,是idx_A(CITY, LNAME, FNAME, CNO) 還是 idx_B(CITY, FNAME, LNAME, CNO)?

通常這不是一個很難的選擇,磁盤IO的效率應該是比CPU對結果進行排序的效率低的。
但是這個選擇也不絕對,通常我們查列表,只需要要給用戶顯示屏幕的一頁(例如:limit 20),這時候idx_B 可能會比idx_A快得多。

一些建議:

機械性地爲每個查詢設計最佳索引也是不明智的,因爲索引的維護可能會使得一些程序速度太慢或者使磁盤負載超負荷。最佳索引是個好的開端,但在決定爲一個查詢建立最佳索引前需要考慮這個索引是否多餘。

索引順序

select * from A where b = 1 and c = 1 

針對上面的sql, 如果sum(b) = 1000 ;sum© = 100; 應該建 idx(c, b) 而不是 idx(b, c)。 當然這個依賴查詢的入參,所以需要具體情況具體考慮

2. 爲表連接設計索引

在1中我們講到簡單SELECT(單表)的索引設計,但在實際開發過程中,往往需要進行多表聯合查詢。接下來,我們討論如何爲表連接設計索引

2.1 前提知識

  • 連接謂詞: 定義表與表之間的連接關係的謂詞。
  • 本地謂詞:只用於訪問一張表的謂詞。

join算法有三種,分別是:Nested Loop Join,Hash join,Sort Merge Join,由於mysql只支持Nested Loop Join,所以我們只談這個。
在外表中找到一行滿足本地謂詞的記錄,然後再從內層表中找到這一行數據相關的記錄。

for (row r1: 外表table) {
    for (row r2: 內表table) {
        ...
    }
}

2.2 預測表的訪問順序

循環嵌套連接方式,表的訪問順序可能會對性能產生巨大的影響。未確定最佳訪問順序之前,是無法設計理想的索引。

經驗法則:
將包含最低數量本地行的表作爲外層表。
本地行的數量是指最大的過濾因子過濾本地謂詞之後所剩餘的行數。

經驗法則忽略了:

  1. 排序
  2. 很小的表:非常小的表及其索引可能長期存在數據庫的緩衝池中,至少在一個連接查詢中,沒有頁會被讀取多次。這樣的表不是外層表時,對其大量的隨機讀也不會稱爲問題。
  3. 聚簇比例。索引中行的順序和表中行的順序的關聯性(聚簇索引,該關聯性在表重組後爲100%)

2.3 索引設計

在連接謂詞和本地謂詞有索引 – 但這個需要先分析表的訪問順序,不然可能會導致一些冗餘的索引。

在一個循環嵌套連接中,內層表通常需要好的寬索引,且以連接謂詞做前導列。

總結:
對於爲表連接設計索引時,我們先分析表的訪問順序,再結合‘索引設計與優化 - 1、爲SELECT語句創建索引’提到的單表索引設計,大致就能設計出比較優秀的索引了。

註釋

註釋1,回表

mysql innodb引擎索引分爲聚簇索引和普通索引(非聚簇索引),聚簇索引葉子節點保存了索引值、行記錄數據,普通索引保存了主鍵(聚簇索引)的值

普通索引的查詢過程:
從普通索引查詢到主鍵,再根據主鍵查聚簇索引,定位到到行記錄數據

回表:
先通過普通索引的值定位聚簇索引值,再通過聚簇索引的值定位行記錄數據,需要掃描兩次索引B+樹,它的性能較掃一遍索引樹更低。


[1] 數據庫索引設計.電子工業出版社

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章