MySQL 優化實戰 - 索引篇

關於SQL優化,這個問題,相信你們過多過少都有過一些瞭解。最近我也在研究SQL優化方面的東西,分享一些經驗。

首先簡單介紹下索引,"索引" 是SQL優化中很重要的一部分(可是索引並非優化的惟一選項)html

索引原理簡述

如何理解索引?索引其實就是一種數據結構,用於快速定位和訪問數據庫中的數據。mysql

一般來講索引使用的數據結構是 B-Tree / B+Tree。以B-Tree爲例,假設每一個節點存儲100個Key,三層的B-Tree 可存儲一百萬數據,若是將根節點存入內存中的話,只須要讀取兩次磁盤就能夠從100萬數據中找到指定數據web

B-Tree

關於B-Tree 推薦閱讀這篇 https://www.geeksforgeeks.org/introduction-of-b-tree-2/ 包含B-Tree的查詢,新增,刪除操做如何實現算法

MySQL 執行計劃

SQL優化中查看執行計劃是必不可少的一項,經過 explain 關鍵字能夠查看MySQL中的執行計劃sql

image.png

注:G 含義是縱向顯示結果

若是以前沒有了解的 EXPLAIN 的同窗,看到這個列表確定是一臉懵逼。不要緊咱們先來挑幾個重要的屬性認識一下。數據庫

  • type:ALL 表明全表掃描
  • key:表明使用的索引,NULL 表明沒有使用索引
  • rows:掃描行數

關於explain 再擴展一下,先執行 explain extended ...; ,再執行 SHOW WARNINGS 能夠看到MySQL優化器對咱們的SQL作了什麼優化。以下圖所示服務器

image.png

利用索引來優化SQL

使用索引的優勢:減小服務器掃描的數據量、避免排序和臨時表、將隨機I/O變爲順序I/O

經過下圖,咱們能夠看到,添加了索引以後掃描行數從三十萬行降到了1,性能提高可想而知數據結構

image.png

生產環境要注意,建立索引是一個很是耗時的操做,而且會阻塞其餘操做。

生產環境添加索引有沒有什麼完美方案?
有的,若是你的MySQL使用主從策略的時候,能夠像Nginx不停機升級web服務那樣,先移除一個節點爲該節點執行 ALTER TABLE 操做,而後巴拉巴拉,由於具體我也沒操做過就不細說了,感興趣你們能夠Google一下,動手嘗試一下。若是是單機部署的話,只能用戶少的時候在執行這種操做了函數

使用索引鏈接表

索引也能夠提升錶鏈接的性能,下面是個例子,用戶表左連訂單表,對user_id 添加索引的先後對比性能

未添加索引

添加索引

like優化

image.png

經過上述例子,咱們能夠看出,若是模糊查詢時以%開頭的話,MySQL沒法使用索引,可是一般來講模糊查詢時咱們的匹配方式都會是 %xxx%,那麼如何優化呢?

這裏能夠經過存"反值"的方式巧妙的解決這個問題,例如我如今在數據庫加一列 reverse_order_no 存儲訂單號的反值(並添加索引),匹配的時候再經過 REVERSE('%910') 函數將參數取反。

image.png

這裏也可使用 or,以下圖,查看執行計劃會發現Extra 屬性返回 "Using sort_union(order_no,reverse_order_no); Using where" 這裏表明MySQL發生了索引合併,後文咱們會講到

image.png

排序以及多列索引

排序須要加索引!相信你們可能知道這個道理,可是以下圖所示,user_id 和 addtime 兩列都創建了索引,那麼下面這條查詢排序使用索引了嗎?

image.png

答案是:並無!爲何?注意 Extra 中的 using filesort,表明MySQL 使用了內部文件排序算法對結果集進行了排序。MySQL 一般在一個表上只選擇一個索引(有例外的狀況),這種狀況若是咱們但願排序使用索引的話,能夠創建一個多列索引,以下圖所示

image.png

並且多列索引最左邊的列,能夠看成單列索引來使用

MySQL 優化器特性

咱們剛剛說過 MySQL 一般在一個表上只選擇一個索引,如何理解?例如索引A和索引B 一個須要掃描十萬行,一個須要掃描五萬行,那麼MySQL必定選擇開銷最小的索引方式。

在一些特殊狀況下,MySQL 會選擇 Index Merge(索引合併),即在一個表上使用多個索引

  • Union:兩個基數很高的索引執行OR操做時

image.png

  • Sort-Union:與上述相似,一旦or的左右兩邊出現範圍查詢,會使用該算法,區別是Sort-Union會進行排序

image.png

  • intersect:針對惟一值很少的索引列,例如在 is_pay(0-未支付,1-支付),is_send(0-未發貨,1-發貨) 兩列創建索引,查詢已支付而且未發貨的訂單,以下圖所示

image.png

根據MySQL 5.7開發文檔所示,還有一種會使用intersect,InnoDB 主鍵上的任何範圍搜索

image.png

關於Index Merge的更多信息,參考MySQL開發文檔
https://dev.mysql.com/doc/refman/5.7/en/index-merge-optimization.html

索引的影響

添加索引雖然能夠提高咱們的SQL性能,可是隨之而來也會帶來必定的開銷

  • 數據插入和更新的性能,由於須要構建索引的緣由,在數據量大的時候會比較明顯,下圖是 《Effective MySQL之SQL語句最優化》中對添加索引先後的插入性能對比

image.png

  • 磁盤空間的影響,一樣也是來自於書中的測試

image.png

image.png

能夠看到在添加了索引以後,空間佔用是原來的7倍,在數據量龐大時,這是一個須要關注的點。

還有須要注意的一點是,在MySQL Innodb 中有聚簇索引和二級索引,通常來講主鍵就是聚簇索引,而其餘的索引都是二級索引。

二級索引所存儲的值是聚簇索引。因此當使用二級索引來進行檢索時,MySQL 會先經過該索引找到對應的聚簇索引,再經過該聚簇索引找到對應的數據。這時使用佔用字節更小的類型來作主鍵會更好,會節省索引佔用空間

參考

Effective MySQL之SQL語句最優化
相關文章
相關標籤/搜索