1. 程式人生 > 資料庫 >【轉】2.1【MySQL】執行原理(一):查詢sql的執行過程及MySQL架構分析

【轉】2.1【MySQL】執行原理(一):查詢sql的執行過程及MySQL架構分析

MySQL的發展歷史和版本分支:

時間里程碑
1996 年MySQL1.0 釋出。它的歷史可以追溯到 1979 年,作者 Monty 用 BASIC 設計的一個報表工具。
1996 年 10 月3.11.1 釋出。MySQL 沒有 2.x 版本。
2000 年ISAM 升級成 MyISAM 引擎。MySQL 開源。
2003 年MySQL4.0 釋出,整合 InnoDB 儲存引擎
2005 年MySQL5.0 版本釋出,提供了檢視、儲存過程等功能。
2008 年MySQLAB 公司被 Sun 公司收購,進入 SunMySQL 時代。
2009 年Oracle 收購 Sun 公司,進入 OracleMySQL 時代。
2010 年MySQL5.5 釋出,InnoDB 成為預設的儲存引擎。
2016 年MySQL 釋出 8.0.0 版本。為什麼沒有 6、7?5.6 可以當成 6.x,5.7 可以當 成 7.x。

因為MySQL是開源的(也有收費版本),所以在MySQL穩定版本的基礎上也發展出來了很多的分支,就像Linux一樣,有Ubuntu、RedHat、CentOS、 Fedora 、Debian等等。大家最熟悉的應該是MariaDB,因為CentOS7裡面自帶了一個MariaDB。它是怎麼來的呢?Oracle收購MySQL之後,MySQL創始人之一Monty擔心MySQL資料庫的未來(開發緩慢,封閉,可能會被閉源),就建立了一個分支MariaDB,預設使用全新的Maria儲存引擎,它是原MyISAM儲存引擎的升級版本。

其他流行分支:

  • Percona Server 是MySQL重要的分支之一,它基於InnoDB儲存引擎的基礎上,提升了效能和易管理性,最後形成了增強版的XtraDB引擎,可以用來更好地發揮伺服器硬體上的效能。
  • 國內也有一些MySQL的分支或者自研的儲存引擎,比如網易的InnoSQL,極數雲舟的ArkDB。

我們操作資料庫有各種各樣的方式,比如Linux 系統中的命令列,比如資料庫工具Navicat,比如程式,例如Java語言的JDBC API或者ORM框架。但大家有沒有思考過,當我們的工具或者程式連線到資料庫之後,實際上發生了什麼事情?它的內部是怎麼工作的?


下面以資料查詢為例,來看下MySQL的工作流程是什麼樣的:

在這裡插入圖片描述

我們的程式或者工具要操作資料庫,第一步要做什麼事情?跟資料庫建立連線。

1.與資料庫建立連線

首先,MySQL必須要執行一個服務,監聽預設的3306埠。在我們開發系統跟第三方對接的時候,必須要弄清楚的有兩件事。

  • 第一個就是通訊協議,比如我們是用TCP/UDP還是HTTP還是WebService?
  • 第二個是訊息格式,比如我們用XML格式,還是JSON格式,還是定長格式?報文頭長度多少,包含什麼內容,每個欄位的詳細含義?

1.1 通訊型別和連線方式

MySQL是支援多種通訊協議的,可以使用同步/非同步的方式,支援長連線/短連線。這裡我們拆分來看。

通訊型別:同步或者非同步

  • 同步通訊的特點:

    • 同步通訊依賴於被呼叫方,受限於被呼叫方的效能。也就是說,應用操作資料庫,執行緒會阻塞,等待資料庫的返回
    • 一般只能做到一對一,很難做到一對多的通訊
  • 非同步跟同步相反:

    • 非同步可以避免應用阻塞等待,但是不能節省SQL執行的時間
    • 如果非同步存在併發,每一個SQL的執行都要單獨建立一個連線,避免資料混亂。但是這樣會給服務端帶來巨大的壓力(一個連線就會建立一個執行緒,執行緒間切換會佔用大量CPU資源)。另外非同步通訊還帶來了編碼的複雜度,所以一般不建議使用。如果要非同步,必須使用連線池,排隊從連線池獲取連線而不是建立新連線

    一般來說我們連線資料庫都是同步連線。

連線方式:長連線或者短連線

MySQL既支援短連線,也支援長連線。短連線就是操作完畢以後,馬上close 掉。長連線可以保持開啟,減少服務端建立和釋放連線的消耗,後面的程式訪問的時候還可以使用這個連線。一般我們會在連線池中使用長連線。

保持長連線會消耗記憶體。長時間不活動的連線,MySQL伺服器會斷開。那這個超時時間怎麼檢視呢?

show global variables like 'wait_timeout'; -- 非互動式超時時間,如 JDBC 程式 
show global variables like' interactive_timeout'; -- 互動式超時時間,如資料庫工具

執行結果如下圖。預設都是28800秒,8小時

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-TkdvrRHQ-1603815339151)(Untitled.assets/image-20201027132844956.png)]


我們怎麼檢視MySQL當前有多少個連線?可以用show status命令:

show global status like 'Thread%';

在這裡插入圖片描述

  • Threads_cached:快取中的執行緒連線數
  • Threads_connected:當前開啟的連線數
  • Threads_created:為處理連線建立的執行緒數
  • Threads_running:非睡眠狀態的連線數,通常指併發連線數

有了連線數,怎麼知道當前連線的狀態?可以使用show processlist命令,(root使用者)檢視SQL的執行狀態。

SHOW PROCESSLIST; 

在這裡插入圖片描述

從上面的Threads_connected可以看到當前有4個連線,所以這裡的顯示了4個連線狀態。那Command這一列是什麼意思呢?一些常見的狀態:

狀態含義
Sleep執行緒正在等待客戶端,以向它傳送一個新語句
Query執行緒正在執行查詢或往客戶端傳送資料
Locked該查詢被其它查詢鎖定
Copying to tmp tableondisk臨時結果集合大於 tmp_table_size。執行緒把臨時表從儲存器內部格式改 變為磁碟模式,以節約儲存器
Sendingdata執行緒正在為 SELECT 語句處理行,同時正在向客戶端傳送資料
Sortingforgroup執行緒正在進行分類,以滿足 GROUPBY 要求
Sortingfororder執行緒正在進行分類,以滿足 ORDERBY 要求

還有個問題,MySQL服務允許的最大連線數是多少呢?

show variables like 'max_connections';

在這裡插入圖片描述

在5.7版本中預設是151個,最大可以設定成16384(2^14)。

set global max_connections=1000;

在這裡插入圖片描述

1.2 MySQL支援哪些通訊協議

第一種是Unix Socket。比如我們在Linux伺服器上,如果沒有指定-h引數,它就用socket方式登入。如下圖(省略
了-S /var/lib/mysql/mysql.sock)

在這裡插入圖片描述

它不用通過網路協議,也可以連線到MySQL的伺服器,它需要用到伺服器上的一個物理檔案(/var/lib/mysql/mysql.sock)

select @@socket;

如果有引數-h指定主機,就會用第二種方式,TCP/IP協議。

mysql -h192.168.8.211 -uroot -p123456

我們的程式語言的連線模組都是用 TCP 協議連線到 MySQL 伺服器的,比如 mysql-connector-java-x.x.xx.jar。

在這裡插入圖片描述

1.3 MySQL採用什麼通訊方式

通訊方式分為以下三種:

在這裡插入圖片描述

  • 單工:在兩臺計算機通訊的時候,資料的傳輸是單向的。比如遙控器
  • 半雙工:在兩臺計算機之間,資料傳輸是雙向的,你可以給我傳送,我也可以給你傳送,但是在這個通訊連線裡面,同一時間只能有一臺伺服器在傳送資料,也就是你要給我發的話,也必須等我發給你完了之後才能給我發。比如對講機
  • 全雙工:資料的傳輸是雙向的,並且可以同時傳輸。比如打電話

那MySQL應該採用哪種通訊方式呢?半雙工的通訊方式,因為客戶端與服務端肯定是雙向通訊的,而且要麼是客戶端向服務端傳送資料,要麼是服務端向客戶端傳送資料,這兩個動作不能同時發生。

客戶端傳送SQL語句給服務端的時候,(在一次連線裡面)資料是不能分成小塊傳送的,不管你的SQL語句有多大,都是一次性發送。比如我們用MyBatis動態SQL生成了一個批量插入的語句, 插入10萬條資料, values後面跟了一長串的內容,或者where條件in裡面的值太多,會出現問題。這個時候我們必須要調整MySQL伺服器配置 max_allowed_packet 引數的值(預設是4M),把它調大,否則就會報錯。

在這裡插入圖片描述

另一方面,對於服務端來說,也是一次性發送所有的資料,不能因為你已經取到了想要的資料就中斷操作,這個時候會對網路和記憶體產生大量消耗。所以,我們一定要在程式裡面避免不帶limit 的這種操作,比如一次把所有滿足條件的資料全部查出來,一定要先count一下。如果資料量大的話,可以分批查詢。

執行一條查詢語句,客戶端跟服務端建立連線之後呢?下一步要做什麼?

2.查詢快取

MySQL內部自帶了一個快取模組。快取的作用我們應該很清楚了,把資料以KV的形式放到記憶體裡面,可以加快資料的讀取速度,也可以減少伺服器處理的時間。但是MySQL的快取我們好像比較陌生,從來沒有去配置過,也不知道它什麼時候生效?假如 t_user 表有500萬行資料,沒有索引。我們在沒有索引的欄位上執行同樣的查詢,大家覺得第二次會快嗎?

在這裡插入圖片描述

可以看到MySQL快取是預設關閉的。預設關閉的意思就是不推薦使用,為什麼MySQL不推薦使用它自帶的快取呢?

主要是因為MySQL自帶的快取的應用場景有限,需要滿足以下兩個要求:

  • 它要求SQL語句必須一模一樣,中間多一個空格,字母大小寫不同都被認為是不同的的SQL。
  • 表裡面任何一條資料發生變化的時候,這張表所有快取都會失效,所以對於有大量資料更新的應用,也不適合。

所以快取這一塊,我們還是交給ORM框架(比如MyBatis預設開啟了一級快取),或者獨立的快取服務,比如Redis來處理更合適。在MySQL 8.0中,查詢快取已經被移除了。

我們沒有使用快取的話,就會跳過快取的模組,下一步我們要做什麼呢?

3. 語法解析和預處理

這裡我會有一個疑問,為什麼我的一條SQL語句能夠被識別呢?假如我隨便執行一個字串penyuyan,伺服器報了一個1064的錯:

在這裡插入圖片描述

它是怎麼知道我輸入的內容是錯誤的?這個就是MySQL的Parser解析器和Preprocessor預處理模組。這一步主要做的事情是對語句基於SQL語法進行詞法和語法分析和語義的解析。

3.1 詞法分析

詞法分析就是把一個完整的SQL語句打碎成一個個的單詞。比如一個簡單的SQL語句:

select username from t_user where id = 1;

它會打碎成8個符號,每個符號是什麼型別,從哪裡開始到哪裡結束。

3.2 語法分析

第二步就是語法分析,語法分析會對SQL做一些語法檢查,比如單引號有沒有閉合,然後根據MySQL定義的語法規則,根據SQL語句生成一個數據結構。這個資料結構我們把它叫做解析樹(select_lex)。

在這裡插入圖片描述

任何資料庫的中介軟體,比如 Mycat,Sharding-JDBC(用到了Druid Parser),都必須要有詞法和語法分析功能,在市面上也有很多的開源的詞法解析的工具(比如LEX,Yacc)。

3.3 前處理器

問題:如果我寫了一個詞法和語法都正確的SQL,但是表名或者欄位不存在,會在哪裡報錯?是在資料庫的執行層還是解析器?比如:

select * from penyuyan;

解析器可以分析語法,但是它怎麼知道資料庫裡面有什麼表,表裡面有什麼欄位呢?實際上還是在解析的時候報錯,解析SQL的環節裡面有個前處理器。它會檢查生成的解析樹,解決解析器無法解析的語義。比如,它會檢查表和列名是否存在,檢查名字和別名,保證沒有歧義。預處理之後得到一個新的解析樹。

4.查詢優化得到執行計劃

得到解析樹之後,是不是執行SQL語句了呢?這裡我們有一個問題,一條SQL語句是不是隻有一種執行方式?或者說資料庫最終執行的SQL是不是就是我們傳送的SQL?

這個答案是否定的。一條SQL語句是可以有很多種執行方式的,最終返回相同的結果,他們是等價的。但是如果有這麼多種執行方式,這些執行方式怎麼得到的?最終選擇哪一種去執行?根據什麼判斷標準去選擇?

這個就是MySQL的查詢優化器的模組(Query Optimizer)。

4.1 什麼是優化器?

查詢優化器的目的就是根據解析樹生成不同的執行計劃(ExecutionPlan),然後選擇一種最優的執行計劃。MySQL裡面使用的是基於開銷(cost)的優化器,哪種執行計劃開銷最小,就用哪種。可以使用這個命令檢視查詢的開銷:

show status like 'Last_query_cost';

mysql官網關於這裡的,想了解更多的同學可以看看

4.2 優化器可以做什麼?

MySQL的優化器能處理哪些優化型別呢?舉兩個簡單的例子:

  1. 當我們對多張表進行關聯查詢的時候,以哪個表的資料作為基準表
  2. 有多個索引可以使用的時候,選擇哪個索引

實際上,對於每一種資料庫來說,優化器的模組都是必不可少的,他們通過複雜的演算法實現儘可能優化查詢效率的目標。如果對於優化器的細節感興趣,可以看看《資料庫查詢優化器的藝術-原理解析與SQL效能優化》。

在這裡插入圖片描述

但是優化器也不是萬能的,並不是再垃圾的SQL語句都能自動優化,也不是每次都能選擇到最優的執行計劃,大家在編寫SQL語句的時候還是要注意。

如果我們想知道優化器是怎麼工作的,它生成了幾種執行計劃,每種執行計劃的cost是多少,應該怎麼做?

4.3 優化器得到執行計劃的過程?

首先,我們要啟用優化器的追蹤(預設是關閉的):

SHOW VARIABLES LIKE 'optimizer_trace'; 
set optimizer_trace = 'enabled=on';

在這裡插入圖片描述

注意,開啟這開關是會消耗效能的,因為它要把優化分析的結果寫到表裡面,所以不要輕易開啟,或者檢視完之後關閉它(改成off)。接著我們執行一個SQL語句,優化器會生成執行計劃(下面是一個兩表聯查):

SELECT d.username,i.phone from user_info i,user_detail d WHERE i.id=d.user_id;

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-oSXC0mOj-1603815339177)(Untitled.assets/image-20201027153751514.png)]

這個時候優化器分析的過程已經記錄到系統表裡面了,我們可以查詢

select * from information_schema.optimizer_trace\G;

如果是直接在Navicat中執行,那麼得到結果只是短短一行資料,並沒有完整的執行計劃

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-Aps4PSkV-1603815339179)(Untitled.assets/image-20201027154026350.png)]

所以,此處應該是在命令列中執行:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-GjjuxeGM-1603815339180)(Untitled.assets/image-20201027164718018.png)]

得到的優化器分析的過程是一個JSON型別的資料,主要分成三部分,準備階段、優化階段和執行階段。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-lZdHzaii-1603815339181)(Untitled.assets/image-20201027165927031.png)]

那具體的優化計劃在哪呢?在優化階段(“join_optimization”)中的 considered_execution_plans

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-pz0FOjyN-1603815339183)(Untitled.assets/image-20201027170451709.png)]

最後,分析完記得關掉它:

set optimizer_trace="enabled=off"; 
SHOWVARIABLESLIKE'optimizer_trace';

優化完之後,得到一個什麼東西呢?

4.4 如何檢視最終執行計劃?

優化器最終會把解析樹變成一個查詢執行計劃,查詢執行計劃是一個數據結構。

當然,這個執行計劃是不是一定是最優的執行計劃呢?不一定,因為MySQL也有可能覆蓋不到所有的執行計劃。我們怎麼檢視MySQL的執行計劃呢?比如多張表關聯查詢,先查詢哪張表?在執行查詢的時候可能用到哪些索引,實際上用到了什麼索引?

MySQL提供了一個執行計劃的工具。我們在SQL語句前面加上EXPLAIN,就可以看到執行計劃的資訊。

5.儲存引擎,儲存資料

得到執行計劃以後,SQL語句是不是終於可以執行了?問題又來了:

  1. 從邏輯的角度來說,我們的資料是放在哪裡的,或者說放在一個什麼結構裡面?
  2. 執行計劃在哪裡執行?是誰去執行?

5.1 儲存引擎基本介紹

我們先回答第一個問題:在關係型資料庫裡面,資料是放在什麼結構裡面的?放在表Table裡面的,我們可以把這個表理解成Excel電子表格的形式。所以我們的表在儲存資料的同時,還要組織資料的儲存結構,這個儲存結構就是由我們的儲存引擎決定的,所以我們也可以把儲存引擎叫做表型別。

在MySQL裡面,支援多種儲存引擎,他們是可以替換的,所以叫做外掛式的儲存引擎。為什麼要搞這麼多儲存引擎呢?一種還不夠用嗎?這個問題先留著。

5.2 檢視儲存引擎

比如我們資料庫裡面已經存在的表,我們怎麼檢視它們的儲存引擎呢?

show table status from `forum`; --forum是指定資料庫名

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-InyXfWHT-1603815339184)(Untitled.assets/image-20201027171707820.png)]

另外,還通過DDL建表語句來檢視。

在MySQL裡面,我們建立的每一張表都可以指定它的儲存引擎,而不是一個數據庫只能使用一個儲存引擎。儲存引擎的使用是以表為單位的。而且,建立表之後還可以修改儲存引擎。

我們說一張表使用的儲存引擎決定我們儲存資料的結構,那在伺服器上它們是怎麼儲存的呢?我們先要找到資料庫存放資料的路徑:

show variables like 'datadir';

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-drfIrsP1-1603815339187)(Untitled.assets/image-20201027172157794.png)]

預設情況下,每個資料庫有一個自己資料夾,以forum資料庫為例。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-knZMp5Cv-1603815339189)(Untitled.assets/image-20201027172605747.png)]

任何一個儲存引擎都有一個frm檔案,這個是表結構定義檔案。不同的儲存引擎存放資料的方式不一樣,產生的檔案也不一樣,innodb 是 1 個,memory沒有,myisam是兩個。

這些儲存引擎的差別在哪呢?

5.3 儲存引擎比較

MyISAM 和InnoDB 是我們用得最多的兩個儲存引擎,在 MySQL 5.5 版本之前,預設的儲存引擎是MyISAM,它是MySQL自帶的。我們建立表的時候不指定儲存引擎,它就會使用MyISAM作為儲存引擎。MyISAM的前身是ISAM(IndexedSequentialAccessMethod:利用索引,順序存取資料的方法)。

5.5版本之後預設的儲存引擎改成了InnoDB,它是第三方公司為MySQL開發的。為什麼要改呢?最主要的原因還是InnoDB 支援事務,支援行級別的鎖,對於業務一致性要求高的場景來說更適合。

這個裡面又有Oracle和MySQL公司的一段恩怨情仇。


InnoDB本來是InnobaseOy公司開發的, 它和MySQLAB公司合作開源了InnoDB的程式碼。但是沒想到MySQL的競爭對手Oracle把InnobaseOy收購了。後來08年Sun公司(開發Java語言的Sun)收購了MySQLAB,09年Sun公司又被 Oracle 收購了,所以 MySQL,InnoDB 又是一家了。有人覺得 MySQL 越來越像Oracle,其實也是這個原因。


那麼除了這兩個我們最熟悉的儲存引擎,資料庫還支援其他哪些常用的儲存引擎呢?我們可以用這個命令檢視資料庫對儲存引擎的支援情況:

show engines;

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-Su09LV1i-1603815339190)(Untitled.assets/image-20201027173124782.png)]

其中有儲存引擎的描述和對事務、XA協議和Savepoints的支援。

  • XA協議用來實現分散式事務(分為本地資源管理器,事務管理器)。
  • Savepoints用來實現子事務(巢狀事務)。建立了一個Savepoints之後,事務就可以回滾到這個點,不會影響到建立Savepoints之前的操作。

這些資料庫支援的儲存引擎,分別有什麼特性呢?

  • MyISAM(3 個檔案)

    應用範圍比較小。表級鎖定限制了讀/寫的效能,因此在Web 和資料倉庫配置中,它通常用於只讀或以讀為主的工作。

    • 支援表級別的鎖(插入和更新會鎖表)。不支援事務

    • 擁有較高的插入(insert)和查詢(select)速度

    • 儲存了表的行數(count速度更快)。(怎麼快速向資料庫插入100萬條資料?我們有一種先用MyISAM插入資料,然後修改儲存引擎為InnoDB的操作)

    • 適用:只讀之類的資料分析的專案

  • InnoDB(2 個檔案)

    mysql5.7中的預設儲存引擎。 InnoDB是一個事務安全(與ACID相容)的MySQL儲存引擎,它具有提交、回滾和崩潰恢復功能來保護使用者資料。InnoDB行級鎖(不升級為更粗粒度的鎖)和Oracle風格的一致非鎖讀提高了多使用者併發性和效能。InnoDB將使用者資料儲存在聚集索引中,以減少基於主鍵的常見查詢的I/O。為了保持資料完整性,InnoDB還支援外來鍵引用完整性約束。

    • 支援事務,支援外來鍵,因此資料的完整性、一致性更高

    • 支援行級別的鎖和表級別的鎖

    • 支援讀寫併發,寫不阻塞讀(MVCC)

    • 特殊的索引存放方式,可以減少IO,提升查詢效率

    • 適用:經常更新的表,存在併發讀寫或者有事務處理的業務系統

  • Memory(1 個檔案)

    將所有資料儲存在RAM中,以便在需要快速查詢非關鍵資料的環境中快速訪問。這個引擎以前被稱為堆引擎。其使用案例正在減少; InnoDB及其緩衝池記憶體區域提供了一種通用、持久的方法來將大部分或所有資料儲存在記憶體中,而ndbcluster為大型分散式資料集提供了快速的鍵值查詢。

    • 把資料放在記憶體裡面,讀寫的速度很快,但是資料庫重啟或者崩潰,資料會全部消失。只適合做臨時表。
    • 將表中的資料儲存到記憶體中
  • CSV(3 個檔案)

    它的表實際上是帶有逗號分隔值的文字檔案。 csv表允許以csv格式匯入或轉儲資料,以便與讀寫相同格式的指令碼和應用程式交換資料。因為csv 表沒有索引,所以通常在正常操作期間將資料儲存在innodb表中,並且只在匯入或匯出階段使用csv表。

    不允許空行,不支援索引。格式通用,可以直接編輯,適合在不同資料庫之間匯入匯出。

  • Archive(2 個檔案)

    這些緊湊的未索引的表用於儲存和檢索大量很少引用的歷史、存檔或安全審計資訊。

    不支援索引,不支援update delete.

這是MySQL裡面常見的一些儲存引擎,我們看到了,不同的儲存引擎提供的特性都不一樣,它們有不同的儲存機制、索引方式、鎖定水平等功能。我們在不同的業務場景中對資料操作的要求不同,就可以選擇不同的儲存引擎來滿足我們的需求,這個就是MySQL支援這麼多儲存引擎的原因。

5.4 如何選擇儲存引擎?

  • 如果對資料一致性要求比較高,需要事務支援,可以選擇InnoDB。
  • 如果資料查詢多更新少,對查詢效能要求比較高,可以選擇MyISAM。
  • 如果需要一個用於查詢的臨時表,可以選擇Memory。
  • 如果所有的儲存引擎都不能滿足你的需求,並且技術能力足夠,可以根據官網內部用C語言開發一個儲存引擎

6.執行引擎,返回結果

OK,儲存引擎分析完了,它是我們儲存資料的形式,繼續第二個問題,是誰使用執行計劃去操作儲存引擎呢?這就是我們的執行引擎,它利用儲存引擎提供的相應的API來完成操作。為什麼我們修改了表的儲存引擎,操作方式不需要做任何改變?因為不同功能的儲存引擎實現的API是相同的。

最後把資料返回給客戶端,即使沒有結果也要返回。

==> MySQL架構

基於上面分析的流程,我們一起來梳理一下MySQL的內部模組。

1.模組詳解

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-X8355hj5-1603815339191)(MySQL執行一條SQL的流程.assets/image-20201027175312083.png)]

  1. Connector:用來支援各種語言和SQL的互動,比如PHP,Python,Java的 JDBC
  2. Management Serveices & Utilities:系統管理和控制工具,包括備份恢復、MySQL複製、叢集等等
  3. Connection Pool:連線池,管理需要緩衝的資源,包括使用者密碼許可權執行緒等等
  4. SQL Interface:用來接收使用者的SQL命令,返回使用者需要的查詢結果
  5. Parser:用來解析SQL語句
  6. Optimizer:查詢優化器
  7. CacheandBuffer:查詢快取,除了行記錄的快取之外,還有表快取,Key快取,許可權快取等等
  8. Pluggable Storage Engines:外掛式儲存引擎,它提供API給服務層使用,跟具體的檔案打交道

2.架構分層

總體上,我們可以把MySQL分成三層,跟客戶端對接的連線層,真正執行操作的服務層,和跟硬體打交道的儲存引擎層(參考MyBatis:介面、核心、基礎)。

  1. 連線層

    我們的客戶端要連線到MySQL伺服器3306埠,必須要跟服務端建立連線,那麼管理所有的連線,驗證客戶端的身份和許可權,這些功能就在連線層完成。

  2. 服務層

    連線層會把SQL語句交給服務層,這裡面又包含一系列的流程:比如查詢快取的判斷、根據SQL呼叫相應的介面,對我們的SQL語句進行詞法和語法的解析(比如關鍵字怎麼識別,別名怎麼識別,語法有沒有錯誤等等)。

    然後就是優化器,MySQL底層會根據一定的規則對我們的 SQL語句進行優化,最後再交給執行器去執行。

  3. 儲存引擎

    儲存引擎就是我們的資料真正存放的地方,在MySQL裡面支援不同的儲存引擎。再往下就是記憶體或者磁碟。