MySQL select語句中where條件的提取過程


select語句中where條件的提取過程

孔個個
依然,在整理where條件提取過程時,發現中文互聯網上關於這一塊的知識要么是存在錯誤自相矛盾的,要么是版本過老,遂自己整理了一版。
在驗證這些內容的過程中走了很多彎路,而搞懂后便豁然開朗,這部分都寫在文章中了,多數以注解的形式存在着。

所有SQL的where條件,均可歸納為3大類:

  1. Index Key (First Key & Last Key):用於確定SQL查詢在索引中的連續范圍(起始范圍+結束范圍)的查詢條件,被稱之為Index Key。
  2. Index Filter:經過index key提取的結果里,並不都是滿足查詢條件的項,因此需要進一步對索引列進行篩選。
  3. Table Filter:所有不屬於索引列的查詢條件,均歸為Table Filter之中。

嘗試總結一下:

  • 首先,引擎層通過index key將連續范圍的索引列條件提取走。就是屬於索引列的查詢條件里,以=、>=的,以及第一個>的條件作為范圍開始(index first key),直到=、<=、第一個<的條件作為范圍結束(index last key)。

  • 其次,server層通過index filter將第一列滿足index key之外的所有屬於索引列查詢條件的,都提取走。

  • 最后,server層將剩下的所有不屬於索引列查詢條件的,提取走。

  1. Index key (First Key & Last Key)

    • 引擎層

    • 用於確定SQL查詢在索引中的連續范圍(起始范圍+結束范圍)的查詢條件,被稱之為Index Key

    • Index First Key

      • 用於確定索引查詢的起始范圍。

      • 提取規則:

        • 從索引的第一個column開始,檢查其在where條件中是否存在。
          • 若存在並且條件是=、>=,則將對應的條件加入Index First Key之中,繼續讀取索引的下一個鍵值,使用同樣的提取規則;
          • 若存在並且條件是>,則將對應的條件加入Index First Key中,同時終止Index First Key的提取;
          • 若不存在,同樣終止Index First Key的提取。

        =、>=一定加入index first key, 其次,第一個> 的條件會加入index first key, 然后不管有沒有第一個> 條件,都結束提取。

        也就是說,index first key包含着: 等值,包含自身的范圍起始值,以及[第一個]不包含自身的范圍起始值的條件

    • Index Last Key

      • 用於確定索引查詢的終止范圍。

      • 提取規則:

        • 從索引的第一個鍵值開始,檢查其在where條件中是否存在。
          • 若存在並且條件是=、<=,則將對應條件加入到Index Last Key中,繼續提取索引的下一個鍵值,使用同樣的提取規則;
          • 若存在並且條件是 < ,則將條件加入到Index Last Key中,同時終止提取;
          • 若不存在,同樣終止Index Last Key的提取。

        和index first key相對,也就是說,index last key包含着: 等值,包含自身的范圍結束值,以及[第一個]不包含自身的范圍結束值。

  2. Index Filter

    • Server層。(ICP特性作用時,Index Filter下降(推)到引擎層實現。)

    • index key之后,此范圍中的項並不都是滿足查詢條件的項,對范圍之內的數據進行再次條件限制,稱為 Index Filter。 其實就是,有索引的列除了被index key連續范圍條件提取了之外,其他的都給index filter。

    • Index Filter提取規則

      • 同樣從索引列的第一column開始,檢查其在where條件中是否存在。

      • 若存在並且where條件僅為 =,則跳過第一列繼續檢查索引下一列,下一索引列采取與索引第一列同樣的提取規則;

        索引第一列條件為等值則跳過這一列,因為這個被index key拿走啦

      • 若where條件為 >=、>、<、<= 其中的幾種,則跳過索引第一列將其余where條件中索引相關列全部加入到Index Filter之中(這句很重要,因為這一塊在index key提取環節中,非第一列的鍵也可能符合index key條件被提取,到了index filter又因為這個規則歸給inex filter了!);

        索引第一列條件為范圍,則跳過這一列,因為這個被index key拿走啦,然后把其他所有[有索引的]列的條件都拿過來

      • 若索引第一列的where條件包含 =、>=、>、<、<= 之外的條件,則將此條件以及其余where條件中索引相關列全部加入到Index Filter之中;

        索引第一列條件非范圍非等值,意味着沒有東西被index key拿走,自然就把所有[有索引的]列的條件都拿到這里了。

      • 若第一列不包含查詢條件,則將所有索引相關條件均加入到Index Filter之中。(同上)

  3. Table Filter

    • Server層
    • 無索引的條件,所有不屬於索引列的查詢條件,均歸為Table Filter之中。

Index Key/Index Filter/Table Filter小結

  • SQL語句中的where條件,使用以上的提取規則,最終都會被提取到Index Key (First Key & Last Key),Index Filter與Table Filter之中。
  • Index First Key,只是用來定位索引的起始范圍,因此只在索引第一次Search Path(沿着索引B+樹的根節點一直遍歷,到索引正確的葉節點位置)時使用,一次判斷即可;
  • Index Last Key,用來定位索引的終止范圍,因此對於起始范圍之后讀到的每一條索引記錄,均需要判斷是否已經超過了Index。
  • Table Filter,則是最后一道where條件的防線,用於過濾通過前面索引的層層考驗的記錄,此時的記錄已經滿足了Index First Key與Index Last Key構成的范圍,並且滿足Index Filter的條件,回表讀取了完整的記錄,判斷完整記錄是否滿足Table Filter中的查詢條件,同樣的,若不滿足,跳過當前記錄,繼續讀取索引的下一條記錄,若滿足,則返回記錄,此記錄滿足了where的所有條件,可以返回給前端用戶。

ICP特性:MySQL 5.6中引入的Index Condition Pushdown,將Index Filter環節 Push Down到索引層面進行過濾。

  • 在MySQL 5.6之前,並不區分Index Filter與Table Filter,統統將Index First Key與Index Last Key范圍內的索引記錄,回表讀取完整記錄,然后返回給MySQL Server層進行過濾。
  • 在MySQL 5.6之后,Index Filter與Table Filter分離,Index Filter下降到InnoDB的索引層面進行過濾,減少了回表與返回MySQL Server層的記錄交互開銷,提高了SQL的執行效率。

SQL的where條件提取的例子

考慮以下的一條SQL:

create table t1 (
    a int,
    b int,
    c int,
    d int,
    e varchar(20),
    primary key (a),
    key idx_t1_bcd (b,c,d)
);

mysql> select * from t1;
+---+------+------+------+------+
| a | b    | c    | d    | e    |
+---+------+------+------+------+
| 1 |    1 |    1 |    1 | a    |
| 3 |    3 |    2 |    2 | c    |
| 4 |    3 |    1 |    1 | d    |
| 5 |    2 |    3 |    5 | e    |
| 6 |    6 |    4 |    4 | f    |
| 7 |    4 |    5 |    5 | g    |
+---+------+------+------+------+

select * from t1 
	where b >= 2            // idx_t1_bcd 索引第一列b
	and b < 8               // idx_t1_bcd 索引第一列b
	and c > 1               // idx_t1_bcd 索引第二列c
	and d != 4              // idx_t1_bcd 索引第三列d
	and e != ‘a’;           // 不屬於索引列
  • 可以發現where條件使用到了[b,c,d,e]四個字段,而t1表的idx_t1_bcd索引,恰好使用了[b,c,d]這三個字段。走idx_t1_bcd索引進行條件過濾,應該是一個不錯的選擇。

    • 此SQL,覆蓋索引idx_t1_bcd上的哪個范圍?

      • 起始范圍:記錄[2,2,2]是第一個需要檢查的索引項。索引起始查找范圍由b >= 2,c > 1決定。

      • 終止范圍:記錄[8,8,8]是第一個不需要檢查的記錄,而之前的記錄均需要判斷。索引的終止查找范圍由b < 8決定;

        image-20200830180305137

    • 在確定了查詢的起始、終止范圍之后,SQL中還有可以通過索引idx_t1_bcd,使用c > 1 and d != 4條件進行索引記錄的過濾。

      • 根據SQL,固定了索引的查詢范圍[(2,2,2),(8,8,8))之后,此索引范圍中並不是每條記錄都是滿足where查詢條件的。例如:(3,1,1)不滿足c > 1的約束;(6,4,4)不滿足d != 4的約束。而c,d列,均可在索引idx_t1_bcd中過濾掉不滿足條件的索引記錄的。
      • 因此,SQL中還可以使用c > 1 and d != 4條件進行索引記錄的過濾。
    • 在確定了索引中最終能夠過濾掉的條件之后,e != ‘a’這個查詢條件,是無法在索引idx_t1_bcd上進行過濾的,因為索引並未包含e列。e列只在表上存在。為了過濾此查詢條件,必須將已經滿足索引查詢條件的記錄回表,取出表中的e列,然后使用e列的查詢條件e != ‘a’進行最終的過濾。

  • 剖析一下where條件提取流程:

    • Index Key (First Key & Last Key)

      • 應用index first key提取規則,提取出來的Index First Key為(b >= 2, c > 1)。由於c的條件為 >,提取結束,不包括d。
      • 應用Index Last Key提取規則,提取出來的Index Last Key為(b < 8),由於是 < 符號,因此提取b之后結束。
    • Index Filter

      在上面的SQL用例中,(3,1,1),(6,4,4)均屬於范圍中,但是又均不滿足SQL的查詢條件。

      • 針對上面的用例SQL,索引第一列只包含 >=、< 兩個條件(b>=2, b<8),因此第一列可跳過,將余下的c、d兩列加入到Index Filter中。因此獲得的Index Filter為 c > 1 and d != 4 。
    • Table Filter

      • 所有不屬於索引列的查詢條件,均歸為Table Filter之中。Table Filter就為 e != 'a'

SQL執行過程中的優化特性/算法

  • ICP(Index Condition Pushdown)

    ICP特性將Index Filter下降到InnoDB的索引層面進行過濾,減少了IO次數,減少了回表與返回MySQL Server層的記錄交互開銷,提高了SQL的執行效率。

  • MRR(Multi-Range Read)

    是基於輔助/第二索引的查詢,減少隨機IO,將隨機IO轉化為順序IO,提高查詢效率。

    查詢輔助索引時,首先把查詢結果按照主鍵進行排序,按照主鍵的順序進行書簽查找,避免頻繁發生離散讀操作導致緩沖區中的頁被替換出緩沖區,然后又不斷的被新的請求讀入緩沖區,減少緩沖池中頁被替換的次數。

  • Nested Loop Join算法

    將驅動表/外部表的結果集作為循環基礎數據,然后循環該結果集,每次獲取一條數據作為下一個表的過濾條件查詢數據,然后合並結果,獲取結果集返回給客戶端。

    Nested-Loop一次只將一行傳入內層循環, 所以外層循環(的結果集)有多少行, 內存循環便要執行多少次,效率非常差

  • Block Nested-Loop Join算法(BNL)

    主要用於當被join的表上無索引。

    將外層循環的行/結果集存入join buffer, 內層循環的每一行與整個buffer中的記錄做比較,從而減少內層循環的次數。

  • Batched Key Access算法(BKA)

    當被join的表能夠使用索引時,就先排好順序,然后再去檢索被join的表。對這些行按照索引字段進行排序,因此減少了隨機IO。

    如果被Join的表上沒有索引,則使用老版本的BNL策略(Block Nested-Loop)。


免責聲明!

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



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