天天看點

mysql設定執行順序_MySQL的sql執行步驟及優化

簡單來說MySQL的sql執行過程如下:用戶端發送一條查詢給伺服器;

伺服器先檢查查詢緩存,如果命中了緩存,則立刻傳回存儲在緩存中的結果。否則進入下一階段。

伺服器段進行SQL解析、預處理,在優化器生成對應的執行計劃;

MySQL根據優化器生成的執行計劃,調用存儲引擎的API來執行查詢。

将結果傳回給用戶端。

1、MySQL用戶端和伺服器通訊

MySQL用戶端和伺服器之間的通訊協定是“半雙工”的,這意味着,在任何一個時刻,要麼由伺服器向用戶端發送資料,要麼由用戶端向伺服器發送資料,這兩個動作不能同時發生。這種協定讓MySQL通信簡單快速,但也限制了MySQL。一個明顯的限制是,這意味着沒辦法進行流量限制。一旦一端開始發生消息,另一端要接收完整個消息才能響應他。

用戶端用一個單獨的資料包将查詢傳給伺服器。一旦用戶端發送了請求,他能做的事情就隻是等待結果了。

相反的,一般伺服器響應給使用者的資料通常很多,由多個資料包組成。當伺服器開始響應用戶端請求時,用戶端必須完整的接受整個傳回結果,而不是簡單的隻收取前面幾條結果,然後讓伺服器停止發送資料。

多數連接配接MySQL的庫函數都可以獲得全部結果并緩存到記憶體裡,還可以逐行擷取所需要的資料。預設一般是獲得全部結果并緩存到記憶體中。MySQL通常需要等所有的資料都已經發送給用戶端才能釋放這條查詢所占用的資源,是以接受全部結果并緩存通常可以減少伺服器的壓力,讓查詢能夠早點結束、早點釋放對應的資源。

2、查詢緩存

在解析一個查詢語句之前,如果查詢緩存是打開的,那麼MySQL會優先檢查這個查詢是否命中查詢緩存中的資料。這個檢查是通過一個對大小寫敏感的哈希查找實作的。查詢和緩存中的查詢即使隻有一個位元組不同,那也不會比對緩存結果,這種情況下查詢就會進入下一階段的處理。

如果目前的查詢恰好命中了查詢緩存,那麼在傳回查詢結果之前MySQL會檢查一次使用者權限。這仍然是無須解析查詢SQL語句的,因為在查詢緩存中已經存放了目前 查詢需要通路的表資訊。如果權限沒有問題,MySQL會跳過所有其他階段,直接從緩存中拿到結果并傳回給用戶端。這種情況下,查詢不會被解析,不用生成執行計劃,不會被執行。

3、查詢優化處理

查詢的生命周期的下一步是将一個SQL轉換成一個執行計劃,mysql在依照這個執行計劃和存儲引擎進行互動。這包含多個子階段:解析SQL、預處理、優化SQL執行計劃。這個過程中任何錯誤都可能終止查詢。文法解析器和預處理:首先MySQL通過關鍵字将SQL語句進行解析,并生成一顆對應的“解析樹”。MySQL解析器将使用MySQL文法規則驗證和解析查詢;預處理器則根據一些MySQL規則進一步檢查解析數是否合法。

查詢優化器:當文法樹被認為是合法的了,并且由優化器将其轉化成執行計劃。一條查詢可以有很多種執行方式,最後都傳回相同的結果。優化器的作用就是找到這其中最好的執行計劃。

執行計劃:MySQL不會生成查詢位元組碼來執行查詢,MySQL生成查詢的一棵指令樹,然後通過存儲引擎執行完成這棵指令樹并傳回結果。最終的執行計劃包含了重構查詢的全部資訊。

4、查詢執行引擎

在解析和優化階段,MySQL将生成查詢對應的執行計劃,MySQL的查詢執行引擎則根據這個執行計劃來完成整個查詢。這裡執行計劃是一個資料結構,而不是和很多其他的關系型資料庫那樣對應的位元組碼。

MySQL簡單的根據執行計劃給出的指令逐漸執行。在根據執行計劃逐漸執行的過程中,有大量的操作需要通過調用存儲引擎實作的接口來完成。為了執行查詢,MySQL隻需要重複執行計劃中的各個操作,知道完成所有的資料查詢。

5、傳回結果給用戶端

查詢執行的最後一個階段是将結果傳回給用戶端。即使查詢不需要傳回結果給用戶端,MySQL仍然會傳回這個查詢的一些資訊,如該查詢影響到的行數。如果查詢可以被緩存,那麼MySQL在這個階段也會将結果放到查詢緩存中。

MySQL将結果集傳回用戶端是一個增量、逐漸傳回的過程。這樣有兩個好處:伺服器端無須存儲太多的結果,也就不會因為傳回太多結果而消耗太多的記憶體;這樣處理也讓MySQL用戶端第一時間獲得傳回的結果。

結果集中的每一行都會以一個滿足MySQL用戶端/伺服器通信協定的包發送,再通過tcp協定進行傳輸,在tcp傳輸的過程中,可能對MySQL的封包進行緩存然後批量傳輸。

join 算法簡介

1)Nested Loop Join算法

NLJ 算法:将驅動表/外部表的結果集作為循環基礎資料,然後循環從該結果集每次一條擷取資料作為下一個表的過濾條件查詢資料,然後合并結果。如果有多表join,則将前面的表的結果集作為循環資料,取到每行再到聯接的下一個表中循環比對,擷取結果集傳回給用戶端。

Nested-Loop 的僞算法如下:

for each row in t1 matching range {

for each row in t2 matching reference key {

for each row in t3 {

if row satisfies join conditions,

send to client

}

}

}

因為普通Nested-Loop一次隻将一行傳入内層循環, 是以外層循環(的結果集)有多少行, 記憶體循環便要執行多少次.在内部表的連接配接上有索引的情況下,其掃描成本為O(Rn),若沒有索引,則掃描成本為O(Rn*Sn)。如果内部表S有很多記錄,則SimpleNested-Loops Join會掃描内部表很多次,執行效率非常差。

2)Index Nested-Loop Join算法

與NLJ算法類似其優化的思路 主要是為了減少内層表資料的比對次數;簡單來說Index Nested-Loop Join 就是通過外層表比對條件 直接與内層表索引進行比對,避免和内層表的每條記錄去進行比較, 這樣極大的減少了對内層表的比對次數;從原來的比對次數=外層表行數 * 内層表行數,變成了 外層表的行數 * 内層表索引的高度,極大的提升了 join的性能。

3)Block Nested-Loop Join算法

BNL 算法:将外層循環的行/結果集存入joinbuffer, 内層循環的每一行與整個buffer中的記錄做比較,進而減少内層循環的次數.

舉例來說,外層循環的結果集是100行,使用NLJ 算法需要掃描内部表100次,如果使用BNL算法,先把對Outer Loop表(外部表)每次讀取的10行記錄放到join buffer,然後在InnerLoop表(内部表)中直接比對這10行資料,記憶體循環就可以一次與這10行進行比較, 這樣隻需要比較10次,對内部表的掃描減少了9/10。是以BNL算法就能夠顯著減少内層循環表掃描的次數.

前面描述的query, 如果使用join buffer, 那麼實際join示意如下:

for each row in t1 matching range {

for each row in t2 matching reference key {

store used columns from t1, t2 in join buffer

if buffer is full {

for each row in t3 {

for each t1, t2 combination in join buffer {

if row satisfies join conditions,

send to client

}

}

empty buffer

}

}

}

if buffer is not empty {

for each row in t3 {

for each t1, t2 combination in join buffer {

if row satisfies join conditions,

send to client

}

}

}

如果t1, t2參與join的列長度隻和為s, c為二者組合數, 那麼t3表被掃描的次數為

(S * C)/join_buffer_size + 1

掃描t3的次數随着join_buffer_size的增大而減少, 直到join buffer能夠容納所有的t1, t2組合, 再增大join buffer size, query 的速度就不會再變快了。

join語句的優化

1. 用小結果集驅動大結果集,盡量減少join語句中的Nested Loop的循環總次數;

2. 優先優化Nested Loop的内層循環,因為内層循環是循環中執行次數最多的,每次循環提升很小的性能都能在整個循環中提升很大的性能;

3. 對被驅動表的join字段上建立索引;

4. 當被驅動表的join字段上無法建立索引的時候,設定足夠的Join Buffer Size。