一、假設要執行的SQL語句如下
update users set name = 'aaa' where id = 5;
把users表中字段id等於5的數據行的name字段的值修改為aaa,那么這條SQL語句是如何執行的呢?
從應用服務器和MySQL服務器的角度看,首先是應用服務器中的一個業務模塊需要更新數據庫數據,先通過一個數據庫連接將SQL語句發送到MySQL服務器上,然后經過SQL接口、解析器、優化器、執行器幾個環節,解析SQL語句,生成執行計划,執行器根據這個執行計划去調用存儲引擎的接口去執行語句。
這個過程看上一篇 https://www.cnblogs.com/a-candy/p/15652349.html
二、在存儲引擎中,這條更新語句是如何執行的
(存儲引擎有很多種,此處只記錄InnoDb存儲引擎)

執行update users set name = 'aaa' where id = 5;
步驟說明:
一、將包含id=5的數據頁加載進Buffer Pool中
Buffer Pool:是InnoDB存儲引擎中非常重要的組件,就是一個緩沖池,會緩存一些從磁盤加載進來的數據頁,在查詢的時候,如果是緩沖池里已經加載的數據,就不需要去查磁盤文件了,查詢內存的效率比查詢磁盤文件的效率要高的多。對數據的增刪改查其實都是基於buffer pool的。
所以在執行update語句的時候,首先是要先找到id=5的這行數據,因此先在緩沖池里查找,如果緩沖池中不存在的話,就會從磁盤文件加載到緩沖池。
二、將舊數據寫入undo log日志文件
假設這條數據原本的值name=‘xx’,現在要修改為‘aaa’,這里要先把舊值‘xx’寫入到undo日志文件中去。目的是為了事務的回滾和支持InnoDB的MVVC機制。
事務的回滾:InnoDB存儲引擎是支持事務的,在事務提交前,是可以對修改了的數據進行回滾的,就是把更新的'aaa'值回滾成之前的‘xxx’,所以為了回滾數據的需要,這里要記錄舊值。
MVVC機制:多版本並發控制。是InnoDB存儲引擎的一種處理並發讀寫的機制,其中redo log版本鏈是重要基礎。這里先不贅述,后面記錄到數據庫的並發時再詳細寫。
三、更新Buffer Pool中的緩存數據
在第一步中已經把id=5的這行數據加載到Buffer Pool中了,這個時候這行數據的name字段值還是‘xx’,要將其修改為新值‘aaa’。
此時,這行數據就成了臟數據,即這個時候,在Buffer Pool中的數據與在磁盤的數據是不一致的,磁盤的數據還是舊的‘xx’。
四、寫redo log buffer
redo log buffer:是內存中的一個緩沖區,記錄的是你對這行數據的操作。此時在redo log buffer中記錄,對id=5的這行數據修改了name字段的值為‘aaa’。
redo log buffer是用來在MySQL突然宕機的時候,用來恢復更新過的數據的。因為若MySQL數據庫宕機,Buffer Pool中緩存的數據會丟失,此時磁盤文件還未更新,那么之前的修改就丟失了。此時只需要重新執行redo log buffer中記錄過的操作,就可以恢復出宕機前的Buffer Pool。
但是redo log buffer也是在InnoDb存儲引擎內存中的,若MySQL宕機,則redo log buffer數據也是會丟失的,因此redo log buffer這個記錄是要進行持久化的,繼續看下一步。
五、提交事務的時候將redo log日志寫入os cache或者磁盤
為什么是提交事務的時候寫而不是其它的時候呢?假設這條update操作的事務還未提交,此時MySQL宕機了,會怎么樣?
Buffer Pool緩沖池的數據丟失,同時redo log buffer中記錄的操作也會丟失,但是此時數據丟失並不重要,因為事務還未提交,沒提交的事務就代表沒執行成功,磁盤上的數據還是原來舊的數據‘xx’,此時不會有任何影響。
如果事務提交了,就表示這個執行成功了。事務有四大特性,ACID,即原子性、一致性、隔離性和持久性。其中持久性就是說,一旦事務提交,則其所做的修改就要永久保存到數據庫中,此時即使系統崩潰,修改過的數據也不會丟失。
如果要事務提交,則應該將redo日志從redo log buffer中刷入到磁盤文件里。
這里InnoDB提供了幾種策略,這個策略是通過參數 innodb_flush_log_at_trx_commit來配置的。
值為0,表示提交事務的時候,不會把redo log buffer里的數據刷入到磁盤文件中。此時可能事務提交成功,但是MySQL宕機,導致內存中的數據和redo日志都丟失了。這是不符合事務的持久性的。
值為1,表示提交事務的時候,把redo log從內存刷入到數據庫的磁盤文件中去。此時只要事務提交成功,那么redo log就必然在磁盤文件里了,這個數據的修改也是不會丟失的。哪怕此時MySQL系統宕機,Buffer Pool中的數據還未刷新到磁盤中,磁盤中還是修改前的舊值‘xx’,只要MySQL重啟,就可以根據redo日志文件去恢復之前做過的修改,重做一次redo buffer,然后在合適的時機將臟數據刷回磁盤文件。
值為2,表示提交事務的時候,把redo log buffer中的數據寫入磁盤文件對應的os cache緩存里去,而不是直接寫入磁盤文件,可能1秒后才會把os cache里的數據寫入磁盤文件。os cache是系統緩存,這種情況下,如果MySQL服務器宕機,則數據不會丟失,但是如果機器宕機,那么os cache的數據就會丟失了,因此同樣會導致數據丟失。
這三種redo日志刷盤策略根據系統的特點和需要進行選擇,一般是設置為1,即提交事務的時候,redo日志必須刷入到磁盤文件里,這樣可以嚴格的保證,提交事務之后,數據絕對不會丟失,因為有redo 日志在磁盤文件里可以恢復出buffer pool。
六、將Buffer Pool緩沖池中的數據刷回磁盤
數據庫中的數據最終都是要存放到磁盤文件中去的,但是為了執行效率,在執行SQL語句的時候,是不可能直接將數據的修改寫入磁盤文件的,因此才有了redo日志。
使用redo 日志,存儲引擎在修改數據的時候,只需要修改這行數據在Buffer Pool中的值,即這個數據的內存拷貝,然后將這個行為記錄到redo日志文件中,記錄redo日志是采用追加的方式,即在磁盤上一小塊區域內的順序IO,這個操作是很快的。
而真實的數據在數據庫磁盤文件中,分布磁盤中不同的位置,在寫數據的時候,磁頭需要在磁盤的多個位置移動,屬於隨機IO,這個過程比順序IO效率低很多,速度相當慢,隨便一個大磁盤文件的隨機讀寫操作都可能要幾百毫秒。
因此,會有一個IO線程在后台運行,在數據庫空閑的時候或者Buffer Pool緩沖頁回收的時候,將數據慢慢寫回到數據庫磁盤文件中去。
