一條更新SQL語句的執行過程
提出問題
UPDATE student SET score = score + 1 WHERE uid = 666;
以上就是一條最簡單的SQL更新語句,想要知道上面這句SQL語句是怎麼執行的先要了解MySQL資料庫的邏輯架構。
具體到上面的UPDATE語句,先取出uid=666的所有行,然後將這些行的score欄位的值加1,並寫入記憶體中。接下來的過程與查詢語句的查詢流程就不一樣了,查詢語句只需要返回查詢結果即可,但是更新語句需要去真的修改資料庫中的資料,所以更新語句相對來講要複雜一些。
說到SQL的更新語句就不得不提到重做日誌(redo log) 和歸檔日誌(binlog),這兩個日誌在MySQL中起到了巨大的作用,這兩個日誌的相互配合也是很有意思的設計,接下來就要詳細給大家講下這兩種日誌的作用、它們是如何工作的、以及它們之間的相互配合。
redo log
redo log是為了解決crash-safe問題而產生的,是一種物理日誌,我們知道資料庫是用來儲存資料的,crash-safe問題對於資料庫來說是非常重要的,在開啟redo log之後MySQL的異常重啟之前提交的資料都不會丟失,這樣就能保證異常crash後資料不會丟失。
redo log是InnoDB引擎層的一種日誌,是用來記錄這個頁"做了什麼改動"。在MySQL中經常會說道WAL技術,WAL的全稱是Write Ahead Logging,WAL的核心思想就是日誌先行,舉個例子,執行一條更新語句,InnoDB就會先把記錄寫到redo log裡面,然後更新到記憶體,等到系統比較空閒的時候再寫入磁碟。redo log的檔案大小是固定的,是通過迴圈寫的 實現的。
有了redo log就能保證InnoDB即使發生異常重啟也不會丟失資料,這種能力也叫做crash-safe的能力
binlog
binlog是一種邏輯日誌,是Server層的一種日誌,記錄了所有的sql語句,主要是用來配合備份來恢復資料庫的,只要我們有最近一次的備份和這期間完整的binlog就能夠恢復資料庫了。
下面我們來簡單看下binlog檔案,我是ubuntu系統,這個檔案是放在/var/log/mysql/
資料夾下面的,
從上面的圖片我們能看到檔名字是依次增加的,與redo log的迴圈寫不同,binlog是追加寫的。 我們執行下面的命令列就能看到binlog記錄的sql語句是什麼樣的,還有一些binlog檔案內容的參照 官方文件操作。
sudo mysqlbinlog /var/log/mysql/mysql-bin.000002 --base64-output=DECODE-ROWS --verbose —verbose
複製程式碼
執行的結果如下圖所示:
從上圖來看很清晰的能看懂這個update語句執行的含義。
上面講了這麼多這兩種日誌的含義,下面簡單總結下這兩種日誌的一些區別:
- redo log是一種物理日誌,記錄是這個頁做了什麼改動,而binlog是邏輯日誌,記錄是sql語句的原始邏輯。
- redo log的檔案大小是固定的,會迴圈寫入檔案,所以會覆蓋之前的日誌。而binlog是追加寫,不會覆蓋之前的日誌。
- redo log是InnoDB引擎層的日誌,而binlog是server層的日誌。
有同學會問,為什麼要搞兩個日誌呀?
我們知道MySQL最開始預設的引擎是MyASIM引擎,根本就不存在crash-safe的問題,binlog只是用來做歸檔的。在MySQL5.5.5之後將InnoDB作為預設的儲存引擎,這樣InnoDB就擁有了crash-safe的能力,在MySQL的架構中,引擎是以外掛的形式存在的,InnoDB引擎不是MySQL資料庫必須的,所以也就好理解redo log也不是MySQL資料庫必須的日誌。
這也就好理解為什麼要搞兩個日誌,一個是server層,一個是引擎層,他們負責不同的功能,相互合作。
那具體這兩個日誌是怎麼合作的呢?他們怎麼保證資料的一致性呢?
兩階段提交
先說下兩階段提交的具體過程:
- UPDATE語句的結果寫入記憶體,同時將這個操作寫入redo log,此時redo log處於prepare狀態,並告知執行器隨時可以提交事物。
- 執行器生成這個操作的binlog,並寫入binlog日誌。
- 執行器通知將之前處於prepare狀態改為commit狀態,更新完成。
兩個階段提交保證了redo log和binlog的一致性。 下面來分析下如果不是兩個階段提交會發生什麼?
先寫redo log後寫binlog
如果先寫redo log再寫binlog的話,當redo log寫完的時候發生了crash,此時binlog裡面是沒有記錄的。這時候發生重啟,redo log會恢復crash的語句,但是如果用這產生時的binlog去恢復資料庫就會丟失這條記錄,此時兩個日誌恢復的資料庫資料就產生了差異。
先寫binlog後寫redo log
如果是先寫binlog後寫redo log,當寫完binlog的時候發生了crash。這時候發生重啟,redo log中還沒寫,此時異常重啟後這個事務是無效的,所以無法恢復,但是binlog中有這條資料,當用此時的binlog檔案去恢復資料庫的時候,就會比當前的資料庫資料多一條記錄。
從上面就可以明白,如果不用兩階段提交就有可能出現兩個日誌狀態不一致。