若是這條sql是寫操做(insert、update、delete),那麼大體的過程以下,其中引擎層是屬於 InnoDB 存儲引擎的,由於InnoDB 是默認的存儲引擎,也是主流的,因此這裏只說明 InnoDB 的引擎層過程。因爲寫操做較查詢操做更爲複雜,因此先看一下寫操做的執行圖。方便後面解析。html
1)負責與客戶端的通訊,是半雙工模式,這就意味着某一固定時刻只能由客戶端向服務器請求或者服務器向客戶端發送數據,而不能同時進行。mysql
2)驗證用戶名和密碼是否正確(數據庫mysql的user表中進行驗證),若是錯誤返回錯誤通知(deAcess nied for user 'root'@'localhost'(using password:YES)),若是正確,則會去 mysql 的權限表(mysql中的 user、db、columns_priv、Host 表,分別存儲的是全局級別、數據庫級別、表級別、列級別、配合 db 的數據庫級別)查詢當前用戶的權限。linux
也稱爲查詢緩存,存儲的數據是以鍵值對的形式進行存儲,若是開啓了緩存,那麼在一條查詢sql語句進來時會先判斷緩存中是否包含當前的sql語句鍵值對,若是存在直接將其對應的結果返回,若是不存在再執行後面一系列操做。若是沒有開啓則直接跳過。sql
相關操做:數據庫
查看緩存配置:show variables like 'have_query_cache';windows
查看是否開啓:show variables like 'query_cache_type';緩存
查看緩存佔用大小:show variables like 'query_cache_size';安全
查看緩存狀態信息:show status like 'Qcache%';服務器
相關參數的含義:異步
緩存失效場景:
一、查詢語句不一致。先後兩條查詢SQL必須徹底一致。
二、查詢語句中含有一些不肯定的值時,則不會緩存。好比 now()、current_date()、curdate()、curtime()、rand()、uuid()等。
三、不使用任何表查詢。如 select 'A';
四、查詢 mysql、information_schema 或 performance_schema 數據庫中的表時,不會走查詢緩存。
五、在存儲的函數,觸發器或事件的主體內執行的查詢。
六、若是表更改,則使用該表的全部高速緩存查詢都變爲無效並從緩存中刪除,這包括使用 MERGE 映射到已更改表的表的查詢。一個表能夠被許多類型的語句改變,如 insert、update、delete、truncate rable、alter table、drop table、drop database。
經過上面的失效場景能夠看出緩存是很容易失效的,因此若是不是查詢次數遠大於修改次數的話,使用緩存不只不能提高查詢效率還會拉低效率(每次讀取後須要向緩存中保存一份,而緩存又容易被清除)。因此在 MYSQL5.6默認是關閉緩存的,而且在 8.0 直接被移除了。固然,若是場景須要用到,仍是可使用的。
開啓
在配置文件(linux下是安裝目錄的cnf文件,windows是安裝目錄下的ini文件)中,增長配置: query_cache_type = 1
關於 query_type_type 參數的說明:
指定 SQL_NO_CACHE:select SQL_NO_CACHE * from student where age >20; SQL_CACHE 同理。
對客戶端傳來的 sql 進行分析,這將包括預處理與解析過程,並進行關鍵詞的提取、解析,並組成一個解析樹。具體的解析詞包括但不侷限於 select/update/delete/or/in/where/group by/having/count/limit 等,若是分析到語法錯誤,會直接拋給客戶端異常:ERROR:You have an error in your SQL syntax.
好比:select * from user where userId =1234;
在分析器中就經過語義規則器將select from where這些關鍵詞提取和匹配出來,mysql會自動判斷關鍵詞和非關鍵詞,將用戶的匹配字段和自定義語句識別出來。這個階段也會作一些校驗:好比校驗當前數據庫是否存在user表,同時假如User表中不存在userId這個字段一樣會報錯:unknown column in field list.
進入優化器說明sql語句是符合標準語義規則而且能夠執行。優化器會根據執行計劃選擇最優的選擇,匹配合適的索引,選擇最佳的方案。好比一個典型的例子是這樣的:
表T,對A、B、C列創建聯合索引(A,B,C),在進行查詢的時候,當sql查詢條件是:select xx where B=x and A=x and C=x.不少人會覺得是用不到索引的,但其實會用到,雖然索引必須符合最左原則才能使用,可是本質上,優化器會自動將這條sql優化爲:where A=x and B=x and C=X,這種優化會爲了底層可以匹配到索引,同時在這個階段是自動按照執行計劃進行預處理,mysql會計算各個執行方法的最佳時間,最終肯定一條執行的sql交給最後的執行器
執行器會調用對應的存儲引擎執行 sql。主流的是MyISAM 和 Innodb。
undo log是 Innodb 引擎專屬的日誌,是記錄每行數據事務執行前的數據。主要做用是用於實現MVCC版本控制,保證事務隔離級別的讀已提交和讀未提交級別。而 MVCC 相關的能夠參考 MySQL中的事務原理和鎖機制。
InnoDB 內部維護了一個緩衝池,用於減小對磁盤數據的直接IO操做,並配合 redo log 來實現異步的落盤,保證程序的高效執行。redo log 大小固定,採用循環寫
write pos 表示當前正在記錄的位置,會向後記錄, checkpoint 表示數據落盤的邊界,也就是 checkpoint 與 write pos中間是已記錄的,當 write pos寫完 id_logfile_3後,會回到id_logfile_0循環寫,而追上 checkpomnit 後則須要先等數據進行落盤,等待 checkponit向後面移動一段距離再寫。redo log存儲的內容我的認爲當直接更新到數據頁緩存時記錄的就是數據頁邏輯,若是更新到 Change Buffer 那麼就是操做的 sql。
關於 Buffer Pool詳情可查看博客 InnoDB 中的緩衝池(Buffer Pool)。
redo log 由於大小固定,因此不能存儲過多的數據,它只能用於未更新的數據落盤,而數據操做的備份恢復、以及主從複製是靠 bin log(若是數據庫誤刪須要還原,那麼須要某個時間點的數據備份以及bin log)。5.7默認記錄的是修改後的行記錄。
在更新到數據頁緩存或者 Change Buffer 後,首先進行 redo log 的編寫,此時 redo log 處於 prepare 狀態,隨後再進行 bin log 的編寫,等到 bin log 也編寫完成後再將 redo log 設置爲 commit 狀態。這是爲了防止數據庫宕機致使 bin log 沒有將修改記錄寫入,後面數據恢復、主從複製時數據不一致。當數據庫啓動後若是發現 redo log 爲 prepare 狀態,那麼就會檢查 bin log 與 redo log 最近的記錄是否對的上,若是對的上就提交,對不上就進行事務回滾。
三種格式:
一、Row(5.7默認)。記錄被修改後的行記錄。缺點是佔空間大。優勢是能保證數據安全,不會發生遺漏。
二、Statement。記錄修改的 sql。缺點是在 mysql 集羣時可能會致使操做不一致從而使得數據不一致(好比在操做中加入了Now()函數,主從數據庫操做的時間不一樣結果也不一樣)。優勢是佔空間小,執行快。
三、Mixed。會針對於操做的 sql 選擇使用Row 仍是 Statement。缺點是仍是可能發生主從不一致的狀況。
一、undo log是用於事務的回滾、保證事務隔離級別讀已提交、可重複讀實現的。redo log是用於對暫不更新到磁盤上的操做進行記錄,使得其能夠延遲落盤,保證程序的效率。bin log是對數據操做進行備份恢復(並不能依靠 bin log 直接完成數據恢復)。
二、undo log 與 redo log 是存儲引擎層的日誌,只能在 InnoDB 下使用;而bin log 是 Server 層的日誌,能夠在任何引擎下使用。
三、redo log 大小有限,超事後會循環寫;另外兩個大小不會。
四、undo log 記錄的是行記錄變化前的數據;redo log 記錄的是 sql 或者是數據頁修改邏輯或 sql(我的理解);bin log記錄的是修改後的行記錄(5.7默認)或者sql語句。
經過上面的分析,能夠很容易地瞭解開始的更新執行圖。這裏就不過多闡述了。
查詢的過程和更新比較類似,可是有些不一樣,主要是來源於他們在查找篩選時的不一樣,更新由於在查找後會進行更新操做,因此查詢這一行爲至始至終都在緩衝池中(使用到索引且緩衝池中包含數據對應的數據頁)。而查詢則更復雜一些。
在 MySQL 5.6開始,引入了一種索引優化策略——索引下推,其本質優化的就是 Where 條件的提取。Where 提取過程是怎樣的?用一個例子來講明,首先進行建表,插入記錄。
create table tbl_test (a int primary key, b int, c int, d int, e varchar(50)); create index idx_bcd on tbl_test(b, c, d); insert into tbl_test values (4,3,1,1,'a'); insert into tbl_test values (1,1,1,2,'d'); insert into tbl_test values (8,8,7,8,'h'); insert into tbl_test values (2,2,1,2,'g'); insert into tbl_test values (5,2,2,5,'e'); insert into tbl_test values (3,3,2,1,'c'); insert into tbl_test values (7,4,0,5,'b'); insert into tbl_test values (6,5,2,4,'f');
那麼執行 select * from tbl_test where b >= 2 and b < 7 and c > 0 and d != 2 and e != 'a'; 在提取時,會將 Where 條件拆分爲 Index Key(First Key & Last Key)、Index Filter 與 Table Filter。
一、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:select * from tbl_test where b >= 2 and b < 7 and c > 0 and d != 2 and e != 'a',應用這個提取規則,提取出來的 Index First Key 爲 b >= 2, c > 0 ,因爲 c 的條件爲 >,提取結束
Index Last Key
用於肯定索引查詢範圍的終止點,與 Index First Key 正好相反;提取規則:從索引的第一個鍵值開始,檢查其在 where 條件中是否存在,若存在而且條件是 =、<=,則將對應條件加入到 Index Last Key 中,繼續提取索引的下一個鍵值,使用一樣的提取規則;若存在而且條件是 < ,則將條件加入到 Index Last Key 中,同時終止提取;若不存在,一樣終止Index Last Key的提取
針對 SQL:select * from tbl_test where b >= 2 and b < 7 and c > 0 and d != 2 and e != 'a',應用這個提取規則,提取出來的 Index Last Key爲 b < 7 ,因爲是 < 符號,提取結束
二、Index Filter
在完成 Index Key 的提取以後,咱們根據 where 條件固定了索引的查詢範圍,那麼是否是在範圍內的每個索引項都知足 WHERE 條件了 ? 很明顯 4,0,5 , 2,1,2 均屬於範圍中,可是又均不知足SQL 的查詢條件
因此 Index Filter 用於索引範圍肯定後,肯定 SQL 中還有哪些條件可使用索引來過濾;提取規則:從索引列的第一列開始,檢查其在 where 條件中是否存在,若存在而且 where 條件僅爲 =,則跳過第一列繼續檢查索引下一列,下一索引列採起與索引第一列一樣的提取規則;若 where 條件爲 >=、>、<、<= 其中的幾種,則跳過索引第一列,將其他 where 條件中索引相關列所有加入到 Index Filter 之中;若索引第一列的 where 條件包含 =、>=、>、<、<= 以外的條件,則將此條件以及其他 where 條件中索引相關列所有加入到 Index Filter 之中;若第一列不包含查詢條件,則將全部索引相關條件均加入到 Index Filter之中
針對 SQL:select * from tbl_test where b >= 2 and b < 7 and c > 0 and d != 2 and e != 'a',應用這個提取規則,提取出來的 Index Filter 爲 c > 0 and d != 2 ,由於索引第一列只包含 >=、< 兩個條件,所以第一列跳過,將餘下的 c、d 兩列加入到 Index Filter 中,提取結束
三、Table Filter
這個就比較簡單了,where 中不能被索引過濾的條件都歸爲此中;提取規則:全部不屬於索引列的查詢條件,均歸爲 Table Filter 之中
針對 SQL:select * from tbl_test where b >= 2 and b < 7 and c > 0 and d != 2 and e != 'a',應用這個提取規則,那麼 Table Filter 就爲 e != 'a'
在5.6 以前,是不分 Table Filter 與 Index Filter 的,這兩個條件都直接分配到 Server 層進行篩選。篩選過程是先根據 Index Key 的條件先在引擎層進行初步篩選,而後獲得對應的主鍵值進行回表查詢獲得初篩的行記錄,傳入 Server 層進行後續的篩選,在 Server 層的篩選由於沒有用到索引因此會進行全表掃描。而索引下推的優化就是將 Index Filter 的條件下推到引擎層,在使用 Index First Key 與 Index Last Key 進行篩選時,就帶上 Index Filter 的條件再次篩選,以此來過濾掉不符合條件的記錄對應的主鍵值,減小回表的次數,同時發給 Server 層的記錄也會更少,全表掃描篩選的效率也會變高。下面是未使用索引下推和使用索引下推的示意圖。
從上面的分析來看,查詢的流程圖大體能夠用下面這張圖來歸納
這裏要注意的是若是在一開始沒有用到索引,會依次將磁盤上的數據頁讀取到緩衝池中進行查詢。
參考博客