MySQL鎖的講解

本文直切主題,針對InnoDB引擎描述索引及優化策略。在開始之前,需要讀者瞭解:1)二叉查找樹(包括2-3查找樹、紅黑樹等數據結構)2)MySQL的InnoDB引擎基礎知識

索引初探

    要了解索引,當然要了解其數據結構。樹有很多應用,流行的用法之一是包括UNIX和DOS在內的許多常用操作系統中的目錄結構,二叉查找樹又是Java中兩種集合類TreeSet和TreeMap實現的基礎。那麼對於數據庫,I/O是其性能瓶頸所在,減少樹的深度是直接有效的,BTree和B+Tree應運而生。

BTree和B+Tree(Balance-Tree,多路搜索樹,非二叉)

    BTree

        BTree是一種查找樹,如同二叉查找樹,紅黑樹等,都是爲提高查找效率而產生的,BTree也是如此,可以把它看做二叉查找樹的優化升級。二叉查找樹的特點是每個非葉節點都最多隻有兩個子節點,但是當數據量非常大時,二叉查找樹的深度過深,搜索算法自根節點向下搜索時,需要訪問的節點也就變的相當多。如果這些節點存儲在外存儲器(磁盤)中,每訪問一個節點,相當於就是進行了一次I/O操作,隨着樹高度的增加,頻繁的I/O操作一定會降低查詢的效率。BTree改二叉爲多叉,每個節點存儲更多的指針信息,以此達到減少樹的深度、降低I/O操作數。

        使用BTree結構可以顯著減少定位記錄時所經歷的中間過程,從而加快存取速度。

    定義(對於一個m階BTree)

  • 根節點至少有兩個子節點(除非根結點爲葉節點
  • 每個節點有m-1個關鍵字,並且以升序排列
  • 位於 m-1和m 關鍵字的子節點的值位於 m-1和m 關鍵字對應的值之間
  • 其它節點至少有m/2個子節點

    特性

  • 關鍵字集合分佈在整棵樹中;
  • 任何一個關鍵字出現且只出現在一個節點中;
  • 搜索有可能在非結束;
  • 其搜索性能等價於在關鍵字全集內做一次二分查找
  • 自動層次控制。

    B+Tree

        InnoDB 存儲引擎在絕大多數情況下使用B+Tree建立索引,B+Tree也是關係型數據庫中最爲常用和有效的索引結構,但是B+Tree索引並不能找到一個給定鍵對應的具體值,它只能找到數據行對應的頁,然後正如上一節所提到的,數據庫把整個頁讀入到內存中,並在內存中查找具體的數據行。

    定義(其定義基本與 BTree同,除了:

  • 所有葉節點之間都有一個鏈指針;

  • 所有關鍵字都在葉子結點出現;

  • 非葉子節點只存儲鍵值信息,數據記錄都存放在葉節點中。

    特性

  • 單節點可以存儲更多的元素,使得查詢磁盤IO次數更少,更加高效的單元素查找;
  • 所有查詢都要查找到葉子節點,查詢性能穩定;
  • 葉子節點會包含所有的關鍵字,以及指向數據記錄的指針,並且葉子節點本身是根據關鍵字的大小從小到大順序鏈接,範圍查找性能更優。

    區別

        B+Tree是BTree的一種變形樹,它與BTree的差異在於:

  • B+Tree只有達到葉子結點才命中(BTree可以在非葉子結點命中),其性能也等價於在關鍵字全集做一次二分查找;

  • BTree樹每個葉子節點都有雙向指針;

  • BTree分支節點和葉節點均保存記錄的關鍵碼和記錄的指針;B+Tree分支節點只保存記錄關鍵碼的複製,無記錄指針。所有記錄都集中在葉節點一層,並且葉節點可以構成一維線性表,便於連續訪問和範圍查詢。

 

聚集索引和輔助索引

    數據庫中的 B+Tree索引可以分爲聚集索引(clustered index)和輔助索引(secondary index),它們之間的最大區別就是,聚集索引中存放着一條行記錄的全部信息,而輔助索引中只包含索引列和一個用於查找對應行記錄的“書籤”。即在數據庫的聚集索引中,葉子節點直接包含衛星數據。在輔助索引(NonClustered Index)中,葉節點帶有指向衛星數據的指針。

聚集索引

    InnoDB使用了聚集索引存儲數據。

    與非聚集索引的區別則是,聚集索引既存儲了索引,也存儲了行值。當一個表有一個聚集索引,它的數據是存儲在索引的葉子頁(leaf pages)上的。因此可以說InnoDB是基於索引的表。

    當我們使用聚集索引對錶中的數據進行檢索時,可以直接獲得聚集索引所對應的整條行記錄數據所在的頁,不需要進行第二次操作。

索引的建立規則

  • 如果一個主鍵被定義了,那麼這個主鍵就是作爲聚集索引
  • 如果沒有主鍵被定義,那麼該表的第一個唯一非空索引被作爲聚集索引
  • 如果沒有主鍵也沒有合適的唯一索引,那麼InnoDB內部會生成一個隱藏的主鍵作爲聚集索引,這個隱藏的主鍵是一個6個字節的列,改列的值會隨着數據的插入自增

輔助索引

    輔助索引,也叫做非聚集索引,葉節點不包含行的全部數據。除了包含關鍵字外,還包含了一個標記,這個標記用來告訴InnoDB引擎從哪裏可以找到與索引相對應的行數據。由於InnoDB引擎是索引組織表,因此,這個標記就是相應的行數據的聚集索引關鍵字。 

    輔助索引的存在並不影響數據在聚集索引中的組織,因此一個表可以有多個輔助索引。

    使用輔助索引查找一條表記錄的過程:通過輔助索引查找到對應的關鍵字,最後在聚集索引中使用關鍵字獲取對應的行記錄,這也是通常情況下行記錄的查找方式。

使用建議

聚集索引的優先選擇列

  1. 含有大量非重複值的列

  2. 使用 between,>或<返回一個範圍值的列

  3. 需要經常排序的列,列順序和最常用的排序一致

  4. 返回大量結果集的查詢

  5. 經常被 join 的列

不建議的聚集索引列

  1. 修改頻繁的列

  2. 低選擇性的列,如性別

  3. 新增內容太過離散隨機的列

規範與建議

  1. 命名規則:表名_字段名
  2. 需要加索引的字段,要在where條件中
  3. 如果where條件中是OR關係,加索引不起作用
  4. 能用小類型別用大類型字段
  5. 索引 key_len 長度過大,也會影響 SQL 性能。所以儘量不默認 null,會佔用字節、索引長度。
  6. 常用的字段放在前面;選擇性高的字段放在前面

  7. 對較長的字符數據類型的字段建索引,優先考慮前綴索引,如 index(url(64))

  8. 只創建需要的索引,避免冗餘索引,如:index(a,b),index(a)

  9. 使用聯合索引,以避免回表,達到覆蓋索引

  10. 聯合索引遵循最左原則
  11. 索引不可濫用,索引會佔用存儲空間並且增加數據更新操作的複雜度,降低CUD(create/update/delate)效率

    回表

        先了解一個概念,MySQL對 WHERE 中條件的處理,根據索引使用情況分成三種:index key, index filter, table filter

        1. index key

            用於確定SQL查詢在索引中的連續範圍(起始範圍+結束範圍)的查詢條件,被稱之爲Index Key。由於一個範圍,至少包含一個起始與一個終止,因此Index Key也被拆分爲Index First Key和Index Last Key,分別用於定位索引查找的起始,以及索引查詢的終止條件。

        2. index filter

            在使用 index key 確定了起始範圍和介紹範圍之後,在此範圍之內,還有一些記錄不符合 WHERE 條件,如果這些條件可以使用索引進行過濾,那麼就是 index filter。

        3. table filter

            WHERE 中的條件不能使用索引進行處理的,只能訪問table,進行條件過濾了。

        從普通索引查出主鍵索引,然後查詢出數據的過程叫做回表。回表一次就會執行一次查詢,所以避免回表是減少數據庫壓力、提高效率的有效手段。在InnoDB中,使用聯合索引配合主鍵索引可以直接返回結果而不需要回表查詢。

    聯合索引(複合索引)與前綴索引(最左原則)

        Mysql從左到右的使用索引中的字段,一個查詢可以只使用索引中的一部份,但只能是最左側部分。例如索引是(a,b,c),可以支持 a,b a,b,c 3種組合進行查找,但不支持 b,c 進行查找。這是最左原則的第一層意思:聯合索引的多個字段中,只有當查詢條件爲聯合索引的第一個字段時,索引纔會有效。

        條件 WHERE a LIKE 'perfix%'; 索引也會有效。這是最左原則的第二層意思:根據字段值最左若干個字符進行的模糊查詢,索引有效。

    覆蓋索引

            覆蓋索引是對聯合索引的合理利用。

            比如 SELECT a, b FROM table WHERE a = 'wangnima'; ,如果我們已經創建了(a)或(a,b)的聯合索引,那麼這條語句會直接從索引返回而不會發生回表。即創建索引的字段覆蓋了查詢字段。

            如果執行 SELECT c FROM table WHERE a = 'wangnima';  ,就會發生回表,因爲我們的輔助索引樹中,沒有字段 的數據,需要拿到主鍵索引的關鍵字,去主鍵索引中回表查詢。

            但是需要注意的是,索引雖好不可濫用。

    索引下推(Index Condition Pushdown (ICP))

        結合在 回表 概念中引出的三種索引使用情況(index key, index filter, table filter),ICP 技術,就是 index filter 技術。MySQL的架構分爲服務器層引擎層

        官方解釋https://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html

            索引條件下推(ICP)是對MySQL使用索引從表中檢索行的情況的優化。如果沒有ICP,存儲引擎將遍歷索引以定位基表中的行,並將它們返回到MySQL服務器,該服務器將計算基錶行的where條件。在啓用ICP的情況下,如果部分where條件可以通過只使用索引中的列來計算,MySQL服務器會把where條件的這部分 推入 存儲引擎。然後,存儲引擎通過使用索引條目來評估所推送的索引條件,並且只有在滿足該條件時才從表中讀取行。ICP可以減少存儲引擎必須訪問基本表的次數和MySQL服務器必須訪問存儲引擎的次數。

        根據官方的指導,我們來做個驗證:

      EXPLAIN 
      SELECT * FROM people 
      WHERE zipcode='95054' 
      AND lastname LIKE '%lao%' 
      AND address LIKE '%Main Street%';

            

        官方解釋:

       EXPLAIN使用“索引條件下推”時,輸出顯示 Using index condition在 Extra列中。

            假設一個表包含有關人員及其地址的信息,並且該表的索引定義爲 INDEX (zipcode, lastname, firstname)。如果我們知道一個人的zipcode價值但不確定姓氏,我們可以這樣搜索:

      SELECT * FROM people
      WHERE zipcode='95054'
      AND lastname LIKE '%etrunia%'
      AND address LIKE '%Main Street%';

            MySQL可以使用索引來掃描人 zipcode='95054'。第二部分(lastname LIKE '%etrunia%')不能用於限制必須掃描的行數,因此如果沒有Index Condition Pushdown,此查詢必須爲所有擁有的人檢索完整的錶行 zipcode='95054'

            使用索引條件下推,MySQL lastname LIKE '%etrunia%'在讀取整個錶行之前檢查該 部分。這樣可以避免讀取與索引元組相對應的完整行,這些行匹配 zipcode條件而不是 lastname條件。

            默認情況下啓用索引條件下推。可以optimizer_switch通過設置index_condition_pushdown標誌來控制 系統變量 :

     SET optimizer_switch = 'index_condition_pushdown=off';
     SET optimizer_switch = 'index_condition_pushdown=on';

實踐

*注意語句中的“[ ··· ]”中括號指代變量,書寫時記得去掉

普通索引

這是最基本的索引,它沒有任何限制。它有以下幾種創建方式:

1. 創建索引

CREATE INDEX indexName ON mytable(username(length));

如果不是字符類型的字段,如int,則不要指定length;如果是CHAR,VARCHAR類型,length可以不指定,也可以小於字段實際長度;如果是BLOB和TEXT類型,必須指定 length。

2. 修改表結構(添加索引)

ALTER table tableName ADD INDEX indexName(columnName)

3. 創建表的時候直接指定

CREATE TABLE mytable(  
 
ID INT NOT NULL,   
 
username VARCHAR(16) NOT NULL,  
 
INDEX [indexName] (username(length))  
 
);

唯一索引

它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。它有以下幾種創建方式:

1. 創建索引

CREATE UNIQUE INDEX indexName ON mytable(username(length))

2. 修改表結構

ALTER table mytable ADD UNIQUE [indexName] (username(length))

3. 創建表的時候直接指定

CREATE TABLE mytable(  
 
ID INT NOT NULL,   
 
username VARCHAR(16) NOT NULL,  
 
UNIQUE [indexName] (username(length))  
 
);

刪除索引的語法

DROP INDEX [indexName] ON mytable;

總結 

    合理利用索引對於提升數據庫的性能、減輕數據庫服務器的負擔是最直接有效的手段。

    其實,索引的本質就是通過縮小範圍、把隨機事件變成順序事件來篩選出最終結果,同時可以總是用同一種查找方式來定位數據,這樣就可以兼顧高效率和穩定性。

原博鏈接:https://my.oschina.net/zbnb/blog/3032005

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