mysql查詢底層原理及join的底層分析

   爲了弄清楚mysql查詢原理,我們得先了解mysql基礎架構,然後再分析原理,最後根據具體例子分析

 一、  mysql基礎架構

   

 

1、連接器管理
      首先是數據庫連接器,主要負責和客戶端建立連接、權限獲取、管理連接等,由於整個建連的過程比較複雜,所以儘量使用長連接。如果數據庫發生異常後爲了快速恢復,可重啓系統重新建立連接。
2、Mysql緩存
     mysql請求首先看緩存數據,key爲sql語句value爲查詢的結果,如果存在則直接返回。如果沒有則直接往下走。
注意:mysql緩存對於一些靜態數據比較適合,對於實時性高的數據最好不要使用。
3、分析器
    對你執行的sql語句進行解析,首先是詞法分析包括一些關鍵字識別,然後語法分析,查看這條語句是否符合mysql語句
4、優化器
    通過你的語句分析,發現那些查詢命中索引,還有表之間的連接順序等
5、執行器
     通過上面一系列的驗證,使用引擎提供的接口。經過不斷的執行將查詢的結果存放在結果集中,通過explain可以看到執行器具體掃描了多少行。


二、sql語句的執行流程


      首先要清楚redo log和binlog兩個日誌模塊
      1、redo log(InnoDB特有的日誌模塊) 重做日誌文件,用於記錄事務操作的變化,記錄修改後的值,不管事務是否提交。保證數據的完整性。其中redo log是固定大小的,是從頭開始寫,寫到末尾在從頭開始。同時會有兩個指針,一個記錄寫入的位置,一個標記,當前擦除的位置,不斷的循環。整個過程稱爲crash-safe。即時數據庫異常,也會有記錄
      2、binlog 歸檔日誌文件,用於記錄對mysql數據庫執行更改的所有操作。binlog是追加寫,不會覆蓋之前的。
      接下來介紹一下mysql更新一條語句的流程。
     update tb_area SET area_name = "beijing" WHERE area_id = 1
    1) 首先執行器通過id查到這條記錄(搜索樹或者查找數據頁) ,並加載到內存中。
    2)然後對這條記錄的area_name調用引擎寫入接口,進行修改。
    3)修改內存中的值,同時更新redolog告知執行器完成寫入(狀態置爲prepare),可以提交事務,執行器將這條操作記錄記錄在binlog,寫入磁盤
    4)完成上述一系列的操作,執行器調用事務提交接口(redolog狀態置爲commit),完成更新操作。

    注意:Mysql的redolog模塊寫入拆成2步走,prepare和commit,稱爲兩階段提交。 整個過程爲1、redolog的prepare狀態 2、binlog的寫入 3、redolog的commit狀態,保證Mysql的可靠性。
     如果binlog沒有寫入並沒有提交事務回滾
     如果binlog寫入事務沒提交,數據庫回覆後自動完成commit


三、Join的實現原理

  

5.5 版本之前,MySQL本身只支持一種表間關聯方式,就是嵌套循環(Nested Loop Join)。如果關聯表的數據量很大,則join關聯的執行時間會非常長。在5.5以後的版本中,MySQL通過引入BNLJ算法來優化嵌套執行。

mysql底層join實現只支持一種算法:嵌套循環連接(Nested-Loop Join),nested-Loop-Join有三種變種:

Simple Nested-Loop Join 簡單嵌套循環連接

Index Nested-Loop Join 索引嵌套循環連接

Block Nested-Loop Join  塊索引嵌套連接

1.Simple Nested-Loop Join:
如圖,A爲驅動表,B爲匹配表。 從A中取出數據1,遍歷B,將匹配到的數據放到result.. 以此類推,每條A表數據都會輪詢B表。

2.Index Nested-Loop Join(索引嵌套):

 


這個需要查詢時,關聯非驅動表(也就是匹配表)的索引,通過索引來減少比較,加速查詢。

在查詢時驅動表會根據關聯字段的索引 到非驅動表查找數據,找到對應的值,此時分爲兩種情況:

如果索引不是主鍵索引的話,需要進行回表查詢(根據索引攜帶的主鍵信息查詢數據) 不是主鍵時,要進行多次回表查詢,先關聯索引,再根據主鍵ID查詢,性能上要慢很多。

如果關聯字段是非驅動表的主鍵時,性能會非常高,直接就能定位到數據。

這裏不懂的可以看下我的這個博客中的Inoodb引擎下的索引與主鍵存儲圖:

3.Block Nested-Loop Join(塊嵌套):


如果關聯的是非驅動表的索引會走索引嵌套,但如果join的列不是索引,就會採用Block Nested-Loop Join。  首先將驅動表的結果集中 所有與join相關的列都先緩存到join buffer中(這樣當查找完成時,就可以將匹配到的記錄從內存與非驅動表放到result返回),然後批量與匹配表進行匹配,將第一種中的多次比較合併爲一次,降低了非驅動表的訪問頻率。 默認情況下join_buffer_size=256K(可以通過show variables like 'join_%' 查看大小)。

BNL 算法:將外層循環的行/結果集存入join buffer, 內層循環的每一行與整個buffer中的記錄做比較,從而減少內層循環的次數.
舉例來說,外層循環的結果集是100行,使用NLJ 算法需要掃描內部表100次,如果使用BNL算法,先把對Outer Loop表(外部表)每次讀取的10行記錄放到join buffer,然後在InnerLoop表(內部表)中直接匹配這10行數據,內存循環就可以一次與這10行進行比較, 這樣只需要比較10次,對內部表的掃描減少了9/10。所以BNL算法就能夠顯著減少內層循環表掃描的次數.

 

 

當有兩個表以上join時:


兩個表以上join時也會用到join buffer,會將前兩個表的結果集緩存下來,然後與第三個表比較,再返回result。建議把join buffer開大點,因爲當join buffer不夠用時會對數據進行分段(例如將後一千條數據放入硬盤)將內存存不下的數據放入硬盤,這樣讀寫會產生IO從而減緩速度。
 

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