九、mysql問題錦集

目錄

 

1、在兩階段提交的不同瞬間,MySQL如果發生異常重啓,是怎麼保證數據完整性的?

事務兩階段提交

在兩階段提交的不同時刻,MySQL異常重啓會出現什麼現象

2、MySQL怎麼知道binlog是完整的?

3、redo log和binlog是怎麼關聯起來的?

4、處於prepare階段的redo log 加上完整binlog,重啓就能恢復,MySQL爲什麼要這麼設計?

5、myslq爲什麼需要兩階段提交?

6、不引入兩個日誌,也就沒有兩階段提交的必要了,只用binlog來支持崩潰恢復,又能支持歸檔,不就可以了?

7、能不能只用redo log,不要binlog?

8、redo log一般設置多大?

9、正常運行中的實例,數據寫入後的最終落盤,是從redo log更新的還是buffer pool更新過來的?

10、redo log buffer是什麼?是先修改內存,還是先寫redo log文件?


1、在兩階段提交的不同瞬間,MySQL如果發生異常重啓,是怎麼保證數據完整性的?

 

事務兩階段提交

在兩階段提交的不同時刻,MySQL異常重啓會出現什麼現象

1、如果在上圖時刻A的地方,也就是寫入redo log處於repare 階段之後、寫binlog之前,發生了崩潰(crash),由於此時binlog還沒寫,redo log也還沒有提交,所以崩潰恢復的時候,這個事務會回滾。這時候,binlog還沒寫,所以也不會傳到備庫;

2、如果在時刻B,也就是binlog寫完,redo log還沒有commit前發生crash,那崩潰恢復的時候MySQL會怎麼處理:

    2.1、如果redo log裏面的事務時完整的,也就是已經有了commit標識,則直接提交;

    2.2、如果redo log裏面的事務只有完整的prepare,則判斷對應的事務binlog是否存在並完整:

        a、如果是,則提交事務;

        b、否則,回滾事務。

這裏,時刻B發生crash對應的就是2.1(a)的情況,崩潰恢復過程中事務會被提交。

 


2、MySQL怎麼知道binlog是完整的?

一個事務的binlog是有完整格式的:

statement格式的binlog,最後會有COMMIT;

row格式的binlog,最後會有一個XID event。

另外,在MySQL5.6.2版本以後,還引入了binlog-checksum參數,用來驗證binlog內容的正確性。對於binlog日誌由於磁盤原因,可能會在日誌中間出錯的情況,MySQL可以通過校驗checksum的結果來發現。所以,MySQL還是有辦法驗證事務binlog的完整性的。


3、redo log和binlog是怎麼關聯起來的?

他們有一個共同的數據字段,叫XID。崩潰恢復的時候,會按順序掃描redo log:

如果碰到既有prepare、又有ccommit的redo log,就直接提交;

如果碰到只有prepare 、而沒有commit的redo log,就拿着XID去binlog找對應的事務。


4、處於prepare階段的redo log 加上完整binlog,重啓就能恢復,MySQL爲什麼要這麼設計?

在時刻B,也就是binlog寫完以後MySQL發生崩潰,這時候binlog已經寫入了,之後就會被從庫(或者用這個binlog恢復出來的庫)使用。所以,在主庫上也要提交這個事務。採用這個策略,主庫和備庫的數據就保證了一致性。


5、myslq爲什麼需要兩階段提交?

先redo log寫完,再寫binlog。崩潰恢復的時候,必須兩個日誌都完整纔可以。是不是一樣 的邏輯?

兩階段提交時經典的分佈式系統問題,並不是mysql獨有的。

如果必須要舉一個場景來說明這麼做的必要性的話,那就是事務的持久性問題。

對於InnoDB引擎來說,如果redo log提交完成了,事務就不能回滾(如果這還允許回滾,就可能覆蓋掉別的事務的更新)。而如果redo log直接提交,然後binlog寫入的時候失敗,InnoDB又回滾不了,數據和binlog日誌又不一致了。

兩階段提交就是爲了給所有人一個機會,當每個人都ok的時候,再一起提交。


6、不引入兩個日誌,也就沒有兩階段提交的必要了,只用binlog來支持崩潰恢復,又能支持歸檔,不就可以了?

這是不可以的,InnoDB在作爲MySQL的插件加入MySQL引擎家族之前,就已經是一個提供了崩潰恢復和事務支持的引擎了。InnoDB接入了MySQL後,發現既然binlog沒有崩潰恢復的能力,那就用InnoDB原有的redo log好了。

現在binlog能力還不能支持崩潰恢復。


7、能不能只用redo log,不要binlog?

如果只從崩潰恢復的角度來講是可以的。可以把binlog關掉,這樣就沒有兩階段提交了,但系統依然是crash-safe的。

但是binlog有着redo log無法替代的功能。

一個是歸檔。redo log是循環寫的,寫到末尾是要回到開頭繼續寫的。這樣歷史日誌沒法保留,redo log也就起不到歸檔的作用。

一個就是MySQL系統依賴binlog。binlog作爲MySQL一開始就有的功能,被用在了很多地方。其中,MySQL系統高可用的基礎,就是binlog複製。

還有很多公司的異構系統(一些數據分析系統),這些系統就是靠消費MySQL的binlog來更新自己的數據。關掉binlog的話,這些下游系統就沒法輸入了。

總之,由於現在包括Mysql高可用在內的很多系統機制都依賴於binlog,所以redo log還替代不到。


8、redo log一般設置多大?

redo log太小的話,會導致很快就被寫滿,然後不得不強行刷redo log,這樣WAL機制的能力就發揮不出來了。


9、正常運行中的實例,數據寫入後的最終落盤,是從redo log更新的還是buffer pool更新過來的?

redo log並沒有記錄數據頁的完整數據,所以它並沒有能力自己去更新磁盤數據頁,也就不存在”數據最終落盤,是由redo log更新過去“的情況。

1、如果是正常運行的實例的話,數據頁被修改以後,跟磁盤的數據頁不一致,稱爲髒頁。最終數據落盤,就是把內存中的數據頁寫盤。這個過程,甚至與redo log毫無關係。

2、在崩潰恢復場景中,InnoDB如果判斷到一個數據頁可能在崩潰恢復的時候丟失了更新,就會將它讀到內存,然後讓redo log更新內存內容。更新完成後,內存頁變成髒頁,就回到了第一種情況的狀態。


10、redo log buffer是什麼?是先修改內存,還是先寫redo log文件?

在一個事務的更新過程中,日誌是要寫多次的,如下:

begin;

insert into t1 ...

insert into t2 ...

commit;

這個事務要往兩個表中插入記錄,插入數據的過程中,生成的日誌都得先保存起來,但又不能在還沒有commit的時候就直接寫到redo log文件裏。

所以,redo log buffer就是一塊內存,用來先存redo日誌的。也就是說,在執行第一個insert的時候,數據的內存被修改了,redo log buffer也寫入了日誌。

但是,真正把日誌寫到redo log文件(文件名是ib_logfile+數字),實在執行commit語句的時候做的。

發佈了102 篇原創文章 · 獲贊 14 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章