京東一面: MySQL寫入一條數據的流程是怎樣的?
1. MySQL 基本架構
圖片
總體來說,MySQL大體分為兩部分,分別是Server 層和存儲引擎層。
Server 層
它包括連接器、查詢緩存、分析器、優化器、執行器等。比如存儲過程,觸發器,視圖都是在這一層實現的。
- 連接器Connection Manager):負責處理客戶端與服務器之間的連接。它接受來自客戶端的請求,并進行身份驗證和權限檢查,建立和管理連接。
- 查詢緩存(Query Cache):在舊版 MySQL 中有,但在較新的版本中已不推薦使用。它能夠緩存查詢和對應的結果,以提高查詢性能。然而,在高并發和大型數據庫中,它反而可能成為性能瓶頸,因為它在某些情況下會引起鎖和不必要的開銷。
- 分析器(Parser):負責分析 SQL 查詢語句,驗證其語法和語義,確保查詢的正確性。它將 SQL 語句轉換成內部數據結構供優化器和執行器使用。
- 優化器(Optimizer):接收來自分析器的查詢請求,并決定如何最有效地執行查詢。優化器的目標是找到最佳的執行路徑,選擇合適的索引、連接順序和訪問方法,以提高查詢性能。
- 執行器(Executor):負責執行優化器生成的執行計劃,獲取存儲引擎返回的數據,并處理客戶端請求。它與存儲引擎交互,執行查詢并返回結果給用戶。
- 存儲引擎層: 它負責數據的存儲和提取。Mysql支持InnoDB、MyISAM、Memory 等多個存儲引擎。我們日常開發中,一般用的存儲引擎就是InnoDB。從 MySQL 5.5 版本開始,InnoDB 就成為了默認的存儲引擎。
介紹完MySQL基本架構,帶大家看一下,每個組件,一條寫入SQL,它主要做什么事~~
2.連接器
我們要執行寫入SQL,一般在MySQL客戶端, 需要輸入連接命令,連接到MySQL服務端。在MySQL服務端,就是連接器負責跟你的客戶端建立連接、獲取權限、維持和管理連接。
連接命令如下:
mysql -h(ip地址) -P(端口) -u(用戶名) -p
輸入完連接命令之后,我們接著輸入正確的密碼,經過經典的TCP握手之后,就可以成功連接到MySQL服務器啦,如下:
C:\MySQL\MySQL Server 8.0\bin>mysql -h 127.0.0.1 -P 3306 -u root -p
Enter password: ******
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 50
Server version: 8.0.31 MySQL Community Server - GPL
Copyright (c) 2000, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
如果輸入密碼錯誤,則會收到一個 Access denied的錯誤信息,如下:
C:\Program Files\MySQL\MySQL Server 8.0\bin>mysql -h 127.0.0.1 -P 3306 -u root -p
Enter password: *****
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
連接成功之后,大家就可以直接輸入寫入SQL,就可以看到結果啦。
mysql> insert into user_score_tab(user_id,score) values(888,10);
Query OK, 1 row affected (0.02 sec)
3. 查詢緩存(Query Cache)
在5.6 及更早的MySQL版本中,連接成功后,會提供查詢緩存,來優化查詢SQL。如果你查詢的表進行更新、插入的時候,會清空緩存的。因此,當你執行寫入SQL,會清空緩存。
其實,MySQL比較新的版本,如8.0 已經徹底廢棄了查詢緩存。因為在高并發和大型數據庫環境下,查詢緩存可能導致性能問題,并且在實際測試中發現,禁用查詢緩存可能會提高整體性能和可伸縮性。
4. 分析器
你扔個寫入SQL給MySQL服務器,它肯定需要先解析,才知道這個SQL是做什么的,對吧。
它會派出分析器,先做詞法分析。你提交過來的寫入SQL是由很多個字符串和空格組成的,MySQL會先解析出這些字符串表示什么意思。比如這個插入SQL:
insert into user_score_tab(user_id,score) values(888,10);
它會把關鍵字insert into解析出來,然后把user_score_tab解析成表,user_id、score解析成列名。做完詞法分析之后,開始做語法分析。語法分析主要就是判斷,你的SQL是否滿足MYSQL的語法。
如果你的SQL寫錯了,語法分析就會報錯誤提示:ERROR 1064 (42000): You have an error in your SQL syntax;
mysql> inser into user_score_tab(user_id,score) value(888,10);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'inser into user_score_tab(user_id,score) value(888,10)' at line 1
平時大家看到這個錯誤的時候,只需要,關注關鍵詞 syntax to use near 就可以快速知道哪里寫錯啦。比如這個例子,就是我的insert寫錯了,少了t。
做完詞法分析和語法分析解析,就知道這是一條插入SQL。
5. 優化器
對于簡單的插入SQL語句,優化器并不會執行復雜的查詢計劃生成工作。
優化器會處理索引的選擇與維護,但并不涉及復雜的查詢優化。如果表中存在主鍵或索引,優化器會確保索引的更新以保證數據一致性,并在插入數據時檢查約束條件。
INSERT語句也會生成執行計劃,它詳細描述了數據庫如何訪問數據、使用哪些索引、以及數據的處理順序等
6. 執行器
執行器負責執行具體的 SQL 操作,是數據庫系統的核心執行模塊。對于INSERT語句,執行器會負責實際的數據寫入過程。
- 確定插入位置:根據優化器的執行計劃,執行器會決定將數據插入表的具體位置,比如根據主鍵或唯一索引找到插入點。
- 加載數據頁:如果要插入的數據頁在內存(Buffer Pool)中,則直接使用;如果不在內存中,則需要從磁盤加載對應的數據頁到內存。
- 更新索引:如果表中有索引(如主鍵、唯一索引或其他索引),執行器也會相應更新這些索引。
7. Buffer Pool
Buffer Pool 是 MySQL InnoDB 存儲引擎中的一塊內存區域,專門用來緩存數據庫表的數據頁、索引頁等內容。它的主要目的是提高數據讀寫性能,減少磁盤 I/O 操作.
- 數據寫入內存中的數據頁:執行器將新數據插入到Buffer Pool中的相應數據頁。這是一個內存操作,而不是直接修改磁盤上的文件。
8. undo log
生成 Undo Log 在真正插入數據之前,InnoDB 會生成 undo log。對于插入操作,undo log 記錄的是如何刪除當前插入的記錄(這用于事務回滾時撤銷插入操作)。為什么生成 undo log?
在事務回滾時,MySQL 需要撤銷未提交的操作。通過 undo log,MySQL 能夠刪除已經插入但未提交的記錄,確保事務的原子性。
9. Redo Log
執行器在插入數據后,立即將這個操作記錄在 redo log 中。
- 寫入 redo log:為了確保數據的可靠性,MySQL采用了預寫日志(Write-Ahead Logging, WAL)機制。在數據真正寫入磁盤前,首先會將這個操作記錄在redo log中。
過程是怎樣的呢?
MySQL首先將操作寫入redo log,并標記為預提交(prepare)狀態。這意味著如果崩潰,MySQL可以通過redo log將操作重做,從而恢復數據。
10. 寫入 Binlog
- 寫入 binlog:在寫入 redo log 的同時,MySQL還會將這次操作寫入binlog,用于數據庫復制和災難恢復。
binlog是 MySQL 的邏輯日志,記錄了SQL操作細節,比如(INSERT INTO)。不同于redo log的物理日志。
11. 事務提交(兩階段提交)
在兩階段提交機制下,MySQL會在事務提交時更新 redo log 為commit狀態。
為什么需要兩階段提交?
確保 binlog 和 redo log 的一致性。如果系統崩潰,MySQL可以通過 redo log 重做操作,并通過 binlog 進行恢復。
12. 數據刷入磁盤
執行器不會立即將內存中的臟頁同步到磁盤。后臺線程會根據一定的策略(如定時刷新等),異步地將 Buffer Pool 中的臟頁刷到磁盤上的表空間文件中。- 這樣可以避免頻繁的磁盤 I/O 提高性能。