一條更新的SQL語句是如何執行的?

一條更新SQL語句的執行過程html

提出問題

UPDATE student SET score = score + 1 WHERE uid = 666; 以上就是一條最簡單的SQL更新語句,想要知道上面這句SQL語句是怎麼執行的先要了解MySQL數據庫的邏輯架構。mysql

UPDATE語句也不例外的執行這個流程,先鏈接數據庫(鏈接器),而後將SQL語句進行詞法分析,並檢測SQL語法(分析器),而後優化對應的查詢操做(優化器),最後真正的去執行這個語句(執行器)。

具體到上面的UPDATE語句,先取出uid=666的全部行,而後將這些行的score字段的值加1,並寫入內存中。接下來的過程與查詢語句的查詢流程就不同了,查詢語句只須要返回查詢結果便可,可是更新語句須要去真的修改數據庫中的數據,因此更新語句相對來說要複雜一些。sql

說到SQL的更新語句就不得不提到重作日誌(redo log)歸檔日誌(binlog),這兩個日誌在MySQL中起到了巨大的做用,這兩個日誌的相互配合也是頗有意思的設計,接下來就要詳細給你們講下這兩種日誌的做用、它們是如何工做的、以及它們之間的相互配合。shell

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的文件大小是固定的,是經過循環寫的 實現的。ubuntu

有了redo log就能保證InnoDB即便發生異常重啓也不會丟失數據,這種能力也叫作crash-safe的能力架構

binlog

binlog是一種邏輯日誌,是Server層的一種日誌,記錄了全部的sql語句,主要是用來配合備份來恢復數據庫的,只要咱們有最近一次的備份和這期間完整的binlog就可以恢復數據庫了。 下面咱們來簡單看下binlog文件,我是ubuntu系統,這個文件是放在/var/log/mysql/文件夾下面的,優化

從上面的圖片咱們能看到文件名字是依次增長的,與redo log的循環寫不一樣,binlog是追加寫的。 咱們執行下面的命令行就能看到binlog記錄的sql語句是什麼樣的,還有一些binlog文件內容的參照 官方文檔操做。ui

sudo mysqlbinlog /var/log/mysql/mysql-bin.000002 --base64-output=DECODE-ROWS --verbose —verbose
複製代碼

執行的結果以下圖所示:spa

從上圖來看很清晰的能看懂這個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文件去恢復數據庫的時候,就會比當前的數據庫數據多一條記錄。

從上面就能夠明白,若是不用兩階段提交就有可能出現兩個日誌狀態不一致。

相關文章
相關標籤/搜索