【MySQL】MySQL中where條件的執行分析

一、問題描述

一條SQL,在數據庫中是如何執行的呢?相信不少人都會對這個問題比較感興趣。固然,要完整描述一條SQL在數據庫中的生命週期,這是一個很是巨大的問題,涵蓋了SQL的詞法解析、語法解析、權限檢查、查詢優化、SQL執行等一系列的步驟,簡短的篇幅是絕對無能爲力的。所以,本文挑選了其中的部份內容,也是我一直都想寫的一個內容,作重點介紹:前端

給定一條SQL,如何提取其中的where條件?where條件中的每一個子條件,在SQL執行的過程當中有分別起着什麼樣的做用?數據庫

經過本文的介紹,但願讀者可以更好地理解查詢條件對於SQL語句的影響;撰寫出更爲優質的SQL語句;更好地理解一些術語,例如:MySQL 5.6中一個重要的優化——Index Condition Pushdown,究竟push down了什麼?測試

本文接下來的內容,安排以下:優化

  1. 簡單介紹關係型數據庫中數據的組織形式;
  2. 給定一條SQL,如何提取其中的where條件;
  3. 最後作一個小的總結;

二、關係型數據庫中的數據組織

關係型數據庫中,數據組織涉及到兩個最基本的結構:表與索引。表中存儲的是完整記錄,通常有兩種組織形式:堆表(全部的記錄無序存儲),或者是聚簇索引表(全部的記錄,按照記錄主鍵進行排序存儲)。索引中存儲的是完整記錄的一個子集,用於加速記錄的查詢速度,索引的組織形式,通常均爲B+樹結構。blog

有了這些基本知識以後,接下來讓咱們建立一張測試表,爲表新增幾個索引,而後插入幾條記錄,最後看看錶的完整數據組織、存儲結構式怎麼樣的。(注意:下面的實例,使用的表的結構爲堆表形式,這也是Oracle/DB2/PostgreSQL等數據庫採用的表組織形式,而不是InnoDB引擎所採用的聚簇索引表。其實,表結構採用何種形式並不重要,最重要的是理解下面章節的核心,在任何表結構中均適用)排序

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

create index idx_t1_bcd on t1(b, c, d);生命週期

insert into t1 values (4,3,1,1,’d’);element

insert into t1 values (1,1,1,1,’a’);get

insert into t1 values (8,8,8,8,’h’):

insert into t1 values (2,2,2,2,’b’);

insert into t1 values (5,2,3,5,’e’);

insert into t1 values (3,3,2,2,’c’);

insert into t1 values (7,4,5,5,’g’);

insert into t1 values (6,6,4,4,’f’);

t1表的存儲結構以下圖所示(只畫出了idx_t1_bcd索引與t1表結構,沒有包括t1表的主鍵索引):

 

t1表的組織結構圖

 

簡單分析一下上圖,idx_t1_bcd索引上有[b,c,d]三個字段(注意:如果InnoDB類的聚簇索引表,idx_t1_bcd上還會包括主鍵a字段),不包括[a,e]字段。idx_t1_bcd索引,首先按照b字段排序,b字段相同,則按照c字段排序,以此類推。記錄在索引中按照[b,c,d]排序,可是在堆表上是亂序的,不按照任何字段排序。

三、SQL的where條件提取

在有了以上的t1表以後,接下來就能夠在此表上進行SQL查詢了,獲取本身想要的數據。例如,考慮如下的一條SQL:

select * from t1 where b >= 2 and b < 8 and c > 1 and d != 4 and e != ‘a’;

一條比較簡單的SQL,一目瞭然就能夠發現where條件使用到了[b,c,d,e]四個字段,而t1表的idx_t1_bcd索引,剛好使用了[b,c,d]這三個字段,那麼走idx_t1_bcd索引進行條件過濾,應該是一個不錯的選擇。接下來,讓咱們拋棄數據庫的思想,直接思考這條SQL的幾個關鍵性問題:

l.此SQL,覆蓋索引idx_t1_bcd上的哪一個範圍?

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

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

2.在肯定了查詢的起始、終止範圍以後,SQL中還有哪些條件可使用索引idx_t1_bcd過濾?

根據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條件進行索引記錄的過濾。

3.在肯定了索引中最終可以過濾掉的條件以後,還有哪些條件是索引沒法過濾的?

此問題的答案顯而易見,e != ‘a’這個查詢條件,沒法在索引idx_t1_bcd上進行過濾,由於索引並未包含e列。e列只在堆表上存在,爲了過濾此查詢條件,必須將已經知足索引查詢條件的記錄回表,取出表中的e列,而後使用e列的查詢條件e != ‘a’進行最終的過濾。

在理解以上的問題解答的基礎上,作一個抽象,可總結出一套放置於全部SQL語句而皆準的where查詢條件的提取規則:

全部SQLwhere條件,都可概括爲3大類:Index Key (First Key & LastKey)Index FilterTable Filter

接下來,讓咱們來詳細分析者3大類分別是如何定義,以及如何提取的。

l. Index Key

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

Index First Key

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

針對上面的SQL,應用這個提取規則,提取出來的Index First Key爲(b >= 2, c > 1)。因爲c的條件爲 >,提取結束,不包括d。

Index Last Key

Index Last Key的功能與Index First Key正好相反,用於肯定索引查詢的終止範圍。提取規則:從索引的第一個鍵值開始,檢查其在where條件中是否存在,若存在而且條件是=、<=,則將對應條件加入到Index Last Key中,繼續提取索引的下一個鍵值,使用一樣的提取規則;若存在而且條件是 < ,則將條件加入到Index Last Key中,同時終止提取;若不存在,一樣終止Index Last Key的提取。

針對上面的SQL,應用這個提取規則,提取出來的Index Last Key爲(b < 8),因爲是 < 符號,所以提取b以後結束。

2.Index Filter

在完成Index Key的提取以後,咱們根據where條件固定了索引的查詢範圍,可是此範圍中的項,並不都是知足查詢條件的項。在上面的SQL用例中,(3,1,1),(6,4,4)均屬於範圍中,可是又均不知足SQL的查詢條件。

Index Filter的提取規則:一樣從索引列的第一列開始,檢查其在where條件中是否存在:若存在而且where條件僅爲 =,則跳過第一列繼續檢查索引下一列,下一索引列採起與索引第一列一樣的提取規則;若where條件爲 >=、>、<、<= 其中的幾種,則跳過索引第一列,將其他where條件中索引相關列所有加入到Index Filter之中;若索引第一列的where條件包含 =、>=、>、<、<= 以外的條件,則將此條件以及其他where條件中索引相關列所有加入到Index Filter之中;若第一列不包含查詢條件,則將全部索引相關條件均加入到Index Filter之中。

針對上面的用例SQL,索引第一列只包含 >=、< 兩個條件,所以第一列可跳過,將餘下的c、d兩列加入到Index Filter中。所以得到的Index Filter爲 c > 1 and d != 4 。

3.Table Filter

Table Filter是最簡單,最易懂,也是提取最爲方便的。提取規則:全部不屬於索引列的查詢條件,均歸爲Table Filter之中。

一樣,針對上面的用例SQL,Table Filter就爲 e != ‘a’。

3.1 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 Last Key的範圍,若超過,則當前查詢結束;

Index Filter,用於過濾索引查詢範圍中不知足查詢條件的記錄,所以對於索引範圍中的每一條記錄,均須要與Index Filter進行對比,若不知足Index Filter則直接丟棄,繼續讀取索引下一條記錄;

Table Filter,則是最後一道where條件的防線,用於過濾經過前面索引的層層考驗的記錄,此時的記錄已經知足了Index First Key與Index Last Key構成的範圍,而且知足Index Filter的條件,回表讀取了完整的記錄,判斷完整記錄是否知足Table Filter中的查詢條件,一樣的,若不知足,跳過當前記錄,繼續讀取索引的下一條記錄,若知足,則返回記錄,此記錄知足了where的全部條件,能夠返回給前端用戶。

四、結語

在讀完、理解了以上內容以後,詳細你們對於數據庫如何提取where中的查詢條件,如何將where中的查詢條件提取爲Index Key,Index Filter,Table Filter有了深入的認識。之後在撰寫SQL語句時,能夠對照表的定義,嘗試本身提取對應的where條件,與最終的SQL執行計劃對比,逐步強化本身的理解。

同時,咱們也能夠回答文章開始提出的一個問題:MySQL 5.6中引入的Index Condition Pushdown,到底是將什麼Push Down到索引層面進行過濾呢?對了,答案是Index Filter。在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的執行效率。

相關文章
相關標籤/搜索