mysql學習 日誌系統 sql更新語句的執行過程

2022-09-20 20:18:12 字數 4215 閱讀 8549

前言

對於更新語句來說,流程是和查詢語句是一樣的,同樣要經過聯結器,分析器,優化器,執行器。

mysql>

update t set c=c+

1where id=

2;

不一樣的是,更新流程還涉及到兩個日誌模組:

walwal 的全稱是 write-ahead logging,它的關鍵點就是先寫日誌,再寫磁碟。

具體來說,當有一條記錄需要更新的時候,innodb 引擎就會先把記錄寫到 redo log裡面,並更新記憶體,這個時候更新就算完成了。同時,innodb 引擎會在適當的時候,將這個操作記錄更新到磁碟裡面,而這個更新往往是在系統比較空閒的時候做。

innodb 的 redo log 是固定大小的,比如可以配置為一組 4 個檔案,每個檔案的大小是 1gb,總共可以記錄 4gb 的操作。從頭開始寫,寫到末尾就又回到開頭迴圈寫,如下面這個圖所示。

write pos 是當前記錄的位置,一邊寫一邊後移,寫到第 3 號檔案末尾後就回到 0 號檔案開頭。

checkpoint 是當前要擦除的位置,也是往後推移並且迴圈的,擦除記錄前要把記錄更新到資料檔案。write pos 和 checkpoint 之間的是還空著的部分,可以用來記錄新的操作。

如果 write pos 追上 checkpoint,表示redo log滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把 checkpoint 推進一下.

有了 redo log,innodb 就可以保證即使資料庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為crash-safe

redo log 是 innodb 引擎特有的日誌,而 server 層也有自己的日誌,稱為 binlog(歸檔日誌)。

兩種日誌的不同點

redo log 是 innodb 引擎特有的;binlog 是 mysql 的 server 層實現的,所有引擎都可以使用。

redo log 是物理日誌,記錄的是「在某個資料頁上做了什麼修改」;binlog 是邏輯日誌,記錄的是這個語句的原始邏輯,比如「給 id=2 這一行的 c 欄位加 1 」。

redo log 是迴圈寫的,空間固定會用完;binlog 是可以追加寫入的。「追加寫」是指 binlog 檔案寫到一定大小後會切換到下乙個,並不會覆蓋以前的日誌。

執行器先找引擎取 id=2 這一行。id 是主鍵,引擎直接用樹搜尋找到這一行。如果 id=2 這一行所在的資料頁本來就在記憶體中,就直接返回給執行器;否則,需要先從磁碟讀入記憶體,然後再返回。

執行器拿到引擎給的行資料,把這個值加上 1,比如原來是 n,現在就是 n+1,得到新的一行資料,再呼叫引擎介面寫入這行新資料。

引擎將這行新資料更新到記憶體中,同時將這個更新操作記錄到 redo log 裡面,此時 redo log 處於 prepare 狀態。然後告知執行器執行完成了,隨時可以提交事務。

執行器生成這個操作的 binlog,並把 binlog 寫入磁碟。

執行器呼叫引擎的提交事務介面,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成。

這個 update 語句的執行流程圖,圖中淺色框表示是在 innodb 內部執行的,深色框表示是在執行器中執行的。

可能注意到了,最後三步看上去有點「繞」,將 redo log 的寫入拆成了兩個步驟:prepare 和 commit,這就是"兩階段提交"。 

由於 redo log 和 binlog 是兩個獨立的邏輯,如果不用兩階段提交,要麼就是先寫完 redo log 再寫 binlog,或者採用反過來的順序。我們看看這兩種方式會有什麼問題。

仍然用前面的 update 語句來做例子。假設當前 id=2 的行,字段 c 的值是 0,再假設執行 update 語句過程中在寫完第乙個日誌後,第二個日誌還沒有寫完期間發生了 crash,會出現什麼情況呢?

先寫 redo log 後寫 binlog。

假設在 redo log 寫完,binlog 還沒有寫完的時候,mysql 程序異常重啟。由於我們前面說過的,redo log 寫完之後,系統即使崩潰,仍然能夠把資料恢復回來,所以恢復後這一行 c 的值是 1。但是由於 binlog 沒寫完就 crash 了,這時候 binlog 裡面就沒有記錄這個語句。因此,之後備份日誌的時候,存起來的 binlog 裡面就沒有這條語句。然後你會發現,如果需要用這個 binlog 來恢復臨時庫的話,由於這個語句的 binlog 丟失,這個臨時庫就會少了這一次更新,恢復出來的這一行 c 的值就是 0,與原庫的值不同。

先寫 binlog 後寫 redo log。

如果在 binlog 寫完之後 crash,由於 redo log 還沒寫,崩潰恢復以後這個事務無效,所以這一行 c 的值是 0。但是 binlog 裡面已經記錄了「把 c 從 0 改成 1」這個日誌。所以,在之後用 binlog 來恢復的時候就多了乙個事務出來,恢復出來的這一行 c 的值就是 1,與原庫的值不同。

1、為什麼會有兩份日誌呢?

答:因為最開始 mysql 裡並沒有 innodb 引擎。mysql 自帶的引擎是 myisam,但是 myisam 沒有 crash-safe 的能力,binlog 日誌只能用於歸檔。而 innodb 是另乙個公司以外掛程式形式引入 mysql 的,既然只依靠 binlog 是沒有 crash-safe 能力的,所以 innodb 使用另外一套日誌系統——也就是 redo log 來實現 crash-safe 能力。

2、定期全量備份的週期「取決於系統重要性,有的是一天一備,有的是一周一備」。那麼在什麼場景下,一天一備會比一周一備更有優勢呢?或者說,它影響了這個資料庫系統的哪個指標?

答:首先,是恢復資料丟失的時間,既然需要恢復,肯定是資料丟失了。如果一天一備份的話,只要找到這天的全備,加入這天某段時間的binlog來恢復,如果一周一備份,假設是周一,而你要恢復的資料是週日某個時間點,那就,需要全備+周一到週日某個時間點的全部binlog用來恢復,時間相比前者需要增加很多;看業務能忍受的程度

其次,是資料庫丟失,如果一周一備份的話,需要確保整個一周的binlog都完好無損,否則將無法恢復;而一天一備,只要保證這天的binlog都完好無損;當然這個可以通過校驗,或者冗餘等技術來實現,相比之下,上面那點更重要

3、我感覺binlog很多餘,按理是不是只要redo log就夠了? redo log是innodb的要求,因為以plugin的形式加入到mysql中,此時binlog作為server層的日誌已然存在,所以便有了兩者共存的現狀。但我覺得這並不能解釋我們在只用inonodb引擎的時候還保留binlog這種設計的原因。

答:binlog還不能去掉。

乙個原因是,redolog只有innodb有,別的引擎沒有。

另乙個原因是,redolog是迴圈寫的,不持久儲存,binlog的「歸檔」這個功能,redolog是不具備的。

4、如果要檢查binlog,那prepare存在的意義是什麼,我直接檢驗binlog的完整性就可以了。任何涉及兩個系統提交的任務都會存在不一致的可能性,而且這種可能性無法完全消除,只能減少這種風險,prepare機制是把寫binlog時間段的風險降低到commit更低時間段,從而降低不一致的風險,分布式事務也是同理,不管怎麼設計,都是存在不一致風險的

答:binlog 只記錄邏輯操作,並無操作狀態,即無法確定該操作是否完成。redo log是有狀態的,所以沒辦法直接檢查binlog。只有在redo log狀態為prepare時,才會去檢查binlog是否存在,否則只校驗redo log是否是 commit就可以啦。

可以簡單理解為,,邏輯操作記錄了「做了什麼事兒,並無記錄有沒有做成功」,物理操作是「在哪兒做了這些事兒,有沒有成功」。

1 prepare階段 2 寫binlog 3 commit

當在2之前崩潰時

重啟恢復:後發現沒有commit,回滾。備份恢復:沒有binlog 。

一致當在3之前崩潰

重啟恢復:雖沒有commit,但滿足prepare和binlog完整,所以重啟後會自動commit。備份:有binlog. 一致

對於兩個日誌,它們有乙個共同的資料字段,叫xid。崩潰恢復的時候,會按照順序掃瞄redo log:

1.如果碰到既有prepare、又有commit的redo log,就直接提交

2.如果碰到只有prepare、而沒有commit的redo log,就拿著xid去binlog找對應的事務

mysql日誌系統 SQL 邏輯日誌 物理日誌

更新語句執行的流程和查詢語句執行的流程一樣 注意 在乙個表上有更新的操作的時候,和這個表相關的查詢快取就會被清空 在經歷分析器,優化器,和執行器儲存引擎的歷程中,還多了重要的日誌模組 redo log 重做日誌 bin log 歸檔日誌 是innodb 引擎獨有的日誌模組 它的關鍵點就是更新的時候先...

sql學習日誌

結果顯示為 null 再改正,employeeid int identity 1,1 結果顯示為 1,2,3 訊息顯示為 訊息 8111,級別 16,狀態 1,第 46 行 無法在表 orders 中可為 null 的列上定義 primary key 約束。更改為 alter table order...

MySQL日誌系統

從乙個更新操作開始 mysql create table t id int primary key,c int mysql update t set c c 1 where id 2 與查詢流程不一樣的是,更新流程還涉及兩個重要的日誌模組,它們正是我們今天要討論的主角 redo log 重做日誌 和...