【Database】MySQL實戰45講


01 | 基礎架構:一條SQL查詢語句是如何執行的?

1. MySQL 的基本架構圖:

MySQL可以分成: Server層 和 存儲引擎層 兩部分。

Server層:包含連接器、查詢緩存、分析器、優化器、執行器等等,涵蓋 MySQL 的大多數核心服務功能,以及所有的內置函數(日期,時間,數學和加密函數等),所有跨存儲引擎的功能都在這一層實現,比如存儲過程,觸發器,視圖等。

存儲引擎層:負責數據的存儲和提取,架構模式是插件式的,支持 InnoDB(默認的存儲引擎), MyISAM, Memory 等多個存儲引擎。

【1】連接器

連接器負責和客戶端建立連接,獲取權限,維持和管理連接。

mysql -h$ip -P$port -u$user -p

連接器要和服務端建立連接。

(1)TCP握手

(2)認證user身份

(3)去權限表里面查詢user擁有的權限。(此后這個連接里面的權限判斷邏輯,都將依賴於此時讀到的權限)

這就意味着,一個用戶建立成功連接之后,即使你用管理員賬號對這個用戶的權限做了修改,也不會影響到已經存在連接的權限。修改完成之后,只有再新建的連接才會使用新的權限設置。

MySQL查看現在所有的連接:

mysql> show processlist;

command 列的 Sleep 狀態表示現在系統里面有一個空閑連接。

客戶端如果太長時間沒有動靜,連接器就會將它自動斷開。時間是由 wait_time 控制的。默認 8 小時。

數據庫里面,長連接是指連接成功之后,如果客戶端持續有請求,則一直使用同一個連接。短連接則是指每次執行完很少的幾次查詢就斷開連接,下次查詢再重新建立一個。

作者建議要盡量減少建立連接的動作,也就是盡量使用長連接。

但是這個時候可能會碰到一個問題:如果都使用長連接之后,你可能會發現,有些時候 MySQL 占用內存長得特別快。

這是因為 MySQL 在執行過程中臨時使用的內存是管理在連接對象里面的。這些資源會在連接斷開的時候才釋放。

所以如果長連接積累下來,可能導致內存占用大,被系統強行殺掉 (OOM),從現象來看就是 MySQL 異常重啟了。

如何解決這個問題有下面兩個方法:

(1)定期斷開長連接。使用一段時間,或者程序里面判斷執行過一個占用內存的大查詢后,斷開連接,之后要查詢再重連。

(2)對於MySQL 5.7以上版本,可以在每次執行一個比較大的操作之后,通過執行 mysql_reset_connection 來重新初始化連接資源。這個過程不需要重連和重新做權限驗證,但是會將連接恢復到剛剛創建完的狀態。

 

【2】查詢緩存

select 語句在查詢之前可以查詢緩存。

但是大多數情況下我會建議你不要使用查詢緩存,為什么呢?因為查詢緩存往往弊大於利。

MySQL 8.0 以上已經把查詢緩存這個功能干掉了。

 

【3】分析器

如果沒有命中查詢緩存,就要開始真正執行語句了。首先,MySQL 需要知道你要做什么,因此需要對 SQL 語句做解析。

(1)分析器會先做“詞法分析”。將我們輸入的一條 SQL 語句(多個字符串和空格組成),MySQL 需要識別出里面的字符串分別是什么,代表什么。

比如說識別關鍵詞 select,表名稱, 列名稱。

(2)“語法分析”。根據詞法分析的結果,語法分析器會根據語法規則,判斷你輸入的這個 SQL 語句是否滿足 MySQL 的語法。

此時如果語句不對,就會收到" You have an error in your SQL syntax" 的提示。

一般語法錯誤會提示第一個出現錯誤的位置,所以需要關注 "use near" 的內容。

 

【4】優化器

優化器是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連接順序。

比如你執行下面這樣的語句,

mysql> select * from t1 join t2 using(ID)  where t1.c=10 and t2.d=20;

既可以先從表 t1 里面取出 c = 10 的記錄的 ID 的值,在根據 ID 值關聯到表 t2,再判斷 t2 里面 d 的值是否等於 20。

也可以先從表 t2 里面取出 d = 20 的記錄的 ID 的值,再根據 ID 的值關聯到 t1, 再判斷 t1 里面的 c 是否等於 10。

這兩種執行方法的邏輯結果是一樣的,但是執行效率會非常不同,而優化器的作用就是決定選擇使用哪一種方案。

 

【5】執行器

開始執行的時候,要先判斷一下你對這個表 T 是否有執行查詢的權限,如果沒有,就會返回沒有權限的錯誤,如下所示(在工程實現上,如果命中查詢緩存,會在查詢緩存返回結果的時候做權限驗證。查詢也會在優化器之前調用 precheck 驗證權限。)

mysql> select * from T where ID=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

如果有權限,就會打開表開始執行。打開表的時候,執行器就會根據表的引擎定義,取使用這個引擎提供的接口。

如果在 T 表中 id 這個字段沒有索引,那么執行器的執行流程是這樣的。

1. 調用 InnoDB 引擎接口取這個表的第一行,判斷 ID 值是不是 10,如果不是則跳過,如果是,就將這行存在結果集當中。

2.調用引擎接口取“下一行”,重復相同的判斷邏輯,直到取到這個表的最后一行。

3. 執行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結果集合返回給客戶端。

到這里,這個語句就執行完成了。

對於有索引的表,執行的邏輯也差不多。第一次調用的是"取滿足條件的第一行"這個接口,之后循環取"滿足條件的下一行"這個接口。這些接口都是引擎中定義好的。 

在數據庫的慢查詢日志中看到一個 rows_examined 的字段,表示這個語句執行過程中掃描了多少行。這個值就是在執行器每次調用引擎獲取數據行的時候累加的。

在有些場景下,執行器調用一次,在引擎內部則掃描了很多行,因此,引擎掃描行數跟 rows_examined 並不是完全相同的。

 

問題:如果表 T 中沒有字段 k,而你執行了這個語句 select * from T where k=1, 那肯定是會報“不存在這個列”的錯誤:“Unknown column ‘k’ in ‘where clause'"。 你覺得這個錯誤是從我們上面的哪個階段報出來的呢?

題解:優化器。分析器會分析語法,但是不會判斷這個表里面是否有這個列。到了優化器才會決定執行的過程。

也不是執行器,執行器的功能是打開表獲取數據,但是表的字段不是數據,是預先定義好的,可以直接讀取,不需要打開表。

 

02 | 日志系統:一條SQL更新語句是如何執行的?

03 | 事務隔離:為什么你改了我還看不見?

04 | 深入淺出索引(上)

05 | 深入淺出索引(下)

06 | 全局鎖和表鎖:給表加個字段怎么有這么多阻礙?

07 | 行鎖功過:怎么減少行鎖對性能的影響?

08 | 事務到底是隔離的還是不隔離的?

09 | 普通索引和唯一索引,應該怎么選擇?

10 | MySQL 為什么有時候會選錯索引?

11 | 怎么給字符串字段加索引?

12 | 為什么我的MySQL會“抖”一下?

13 | 為什么表數據刪掉一半,表文件大小不變?

14 | count(*)這么慢,我該怎么辦?

15 | 答疑文章(一):日志和索引的相關問題

16 | “order by” 是怎么工作的?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM