簡單的聊聊索引的那些事兒

工做經歷中,隨着業務數據長時間積累,Mysql的數據也稍微有必定的量,因而乎當時咱們進行一次服務端慢查詢大排查,肯定慢查詢屬於哪一個工程而且將其優化掉。我工程內也有一個,大致是MQ訂閱到的taskid,taskid關聯task的log表去找最近的一條記錄的時間,而後根據時間校驗是否放行作相應業務處理。我explain下,發現當時寫的時候,log表的taskid也沒有建索引,當log表的記錄積累起來後,這個查詢會顯得很慢,建索引後有立竿見影的效果,固然這只是一個很是簡單的場景。其實這裏還衍生出一個問題:當log表數據量過大時候修改表結構,會形成一段時間的鎖表。雖然有些方式能夠避免鎖表,可是「合理時機」建立索引仍是很重要的。知其然,要知其因此然,來看看索引的那些事兒。html

目錄:

  • 相關知識準備
  • 聊聊BTree和B+Tree
  • MyISAM和InnoDB的索引實現
  • InnoDB的主鍵選擇和優化
  • 經過原理理解最左前綴

相關知識準備

磁盤存儲:

Mysql數據通常以文件的形式存儲在磁盤上,讀取數據時須要在磁盤上進行IO操做。當須要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即肯定要讀的數據在哪一個磁道,哪一個扇區。爲了讀取這個扇區的數據,須要將磁頭放到這個扇區上方,爲了實現這一點,磁頭須要移動對準相應磁道,這個過程叫作尋道,所耗費時間叫作尋道時間,而後磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫作旋轉時間。mysql

計算機局部性原理:

當一個數據被用到時,其附近的數據也一般會立刻被使用。sql

磁盤預讀:

因爲磁盤順序讀取的效率很高(不須要尋道時間,只需不多的旋轉時間),所以對於具備局部性的程序來講,預讀能夠提升I/O效率。預讀的長度通常爲頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操做系統每每將主存和磁盤存儲區分割爲連續的大小相等的塊,每一個存儲塊稱爲一頁(在許多操做系統中,頁得大小一般爲4k),主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,而後異常返回,程序繼續運行。數據庫

索引的目的:

上邊內容能夠看出磁盤IO很是耗時,因此索引歸根結的目的:減小耗時磁盤IO次數,提升獲取數據的性能。緩存

聊聊BTree和B+Tree

BTree有以下特色:
  1. 全部鍵值分佈在整顆樹中;
  2. 任何一個關鍵字出現且只出如今一個結點中;
  3. 搜索有可能在非葉子結點結束;
  4. 在關鍵字全集內作一次查找,性能接近二分查找;

B+Tree是BTree的變體,也是一種多路搜索樹, 它與BTree的不一樣之處在於:

  1. 全部關鍵字存儲在葉子節點,非葉子節點並不存儲真正的data;
  2. 爲全部葉子結點增長了一個鏈指針;

MyISAM和InnoDB的索引實現

MyISAM主鍵索引策略:

MyISAM表的主鍵索引(Primary key)如圖所示。能夠看出MyISAM的索引文件僅僅保存數據記錄的地址。MyISAM的輔助索引和主鍵索引在策略上沒任何區別,只是主鍵索引多了惟一約束。(輔助索引爲惟一索引也有此特性)

InnoDB主鍵索引策略:

能夠看到葉節點包含了完整的數據記錄。這種索引叫作彙集索引。由於InnoDB的數據文件自己要按主鍵彙集,因此InnoDB要求表必須有主鍵(MyISAM能夠沒有),若是沒有顯式指定,則MySQL系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段做爲主鍵,這個字段長度爲6個字節,類型爲長整形。

InnoDB輔助索引策略:

InnoDB輔助索引策略與MyISAM的不一樣是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的全部輔助索引都引用主鍵做爲data域。查詢數據時,首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。輔助索引強依賴於主鍵索引,因此合理的建立主鍵是很是重要的。

InnoDB的主鍵選擇和優化

在使用InnoDB存儲引擎時,若是沒有特別的須要,請永遠使用一個與業務無關的自增字段做爲主鍵。從數據庫索引優化角度看,使用InnoDB引擎而不使用自增主鍵絕對是一個糟糕的主意。上文討論過InnoDB的索引實現,InnoDB使用匯集索引,數據記錄自己被存於主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點內(大小爲一個內存頁或磁盤頁)的各條數據記錄按主鍵順序存放,所以每當有一條新的記錄插入時,MySQL會根據其主鍵將其插入適當的節點和位置,若是頁面達到裝載因子(InnoDB默認爲15/16),則開闢一個新的頁(節點)。若是表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁。以下圖所示:bash

這樣就會造成一個緊湊的索引結構,近似順序填滿。由於每次插入時也不須要移動已有數據,因此效率很高,也不會增長不少開銷在維護索引上。若是使用非自增主鍵(若是身份證號或學號等),由於每次插入主鍵的值近似於隨機,因此每次新紀錄都要被插到現有索引頁得中間某個位置, 此時MySQL不得不爲了將新記錄插到合適位置而移動數據,甚至目標頁面可能已經被回寫到磁盤上而從緩存中清掉,此時又要從磁盤上讀回來,這增長了不少開銷。只要能夠,請儘可能在InnoDB上採用自增字段作主鍵。

經過原理理解最左前綴

假設當前聯合索引爲:KEY a_id_state_index (a_id,name) 查詢驗證:性能

EXPLAIN SELECT * FROM `user` WHERE `a_id` = 5 AND `name` = 'cj_25'
EXPLAIN SELECT * FROM `user` WHERE `name` = 'cj_25' AND `a_id` = 5
複製代碼

注:上面兩句結果都以下圖,由於mysql會對where裏面的條件順序在查詢以前會被mysql自動優化優化

EXPLAIN SELECT * FROM `user` WHERE `a_id` = 5
複製代碼

EXPLAIN SELECT * FROM `user` WHERE `name` = 'cj_25'
複製代碼

爲了分析仍是根據數據,按照上面規則去畫這顆B+樹:

對於輔助索引,樹的構建是按照「最左」字段的順序構建的,當查詢name時,只能順序查找,沒法使用二分。

參考文章地址

blog.codinglabs.org/articles/th…spa

相關文章
相關標籤/搜索