目前物理復(fù)制到了ro 開始刷120s apply_lsn 不推進(jìn)的信息以后, 即使壓力停下來也無法恢復(fù), 為什么?
如下圖所示:
這里最極端的場景是如果rw 上面最老的page1, 也就是在flush list 上根據(jù) oldest_modification_lsn 排在最老的位置page_lsn 已經(jīng)大于ro 上面的apply_lsn 了, 那么刷臟是無法進(jìn)行的, 因?yàn)槲锢韽?fù)制需要保證page 已經(jīng)被解析到ro parse buffer才可以進(jìn)行刷臟. 另外想Page2 這樣的Page 雖然newest_modification 和 oldest_modification 沒有差很多也無法進(jìn)行刷臟了. 因?yàn)镻arse buffer 已經(jīng)滿了.
但是這個(gè)時(shí)候ro 節(jié)點(diǎn)的apply_lsn 已經(jīng)不推進(jìn)了, 因?yàn)樯厦娴膒arse buffer 已經(jīng)滿了, parse buffer 推進(jìn)需要等rw 節(jié)點(diǎn)把老的page 刷下去, 老的parse buffer chunk 才可以釋放. 但是由于上面rw 節(jié)點(diǎn)已經(jīng)最老的page 都無法刷臟, 那么parse buffer chunk 肯定就沒機(jī)會(huì)釋放了.
那么此時(shí)就形成了死循環(huán)了. 即使寫入壓力停下來, ro 也是無法恢復(fù)的.
所以只要rw 上面最老page 超過了 parse buffer 的大小, 也就是最老page newest_modification_page lsn > ro apply_lsn 之時(shí), 那么死鎖就已經(jīng)形成, 后續(xù)都無法避免了
這里copy_page 為何沒有生效?
目前copy_page 的機(jī)制是刷臟的時(shí)候進(jìn)行的, 在下圖中copy page copy 出來的page newest_modification 也是大于ro apply_lsn 的, 所以也是無法刷臟的, 所以這個(gè)時(shí)候其實(shí)這個(gè)copy_page 機(jī)制是無效的機(jī)制.
正確的做法是: 在發(fā)現(xiàn)Page newest_modification 有可能超過一定的大小, 那么就應(yīng)該讓該page 進(jìn)行copy page強(qiáng)行刷臟, 否則到后面在進(jìn)行刷臟就來不及了.
開啟了多版本LogIndex 版本為什么可以規(guī)避這個(gè)問題?
在因?yàn)閜arse buffer 滿導(dǎo)致的刷臟約束中, 如上圖所示, Page1, Page2 無法進(jìn)行刷臟, 但是其他的Page 如果newest_modification < ro apply_lsn 是可以刷臟的, 因此rw 節(jié)點(diǎn)buffer pool 里面臟頁其實(shí)不多.
開啟了LogIndex 以后, ro 就可以隨意丟棄自己的parse buffer 了, 當(dāng)然也就不會(huì)crash.
但是依然有一個(gè)問題是如果Page1 一直修改, 這個(gè)Page1 的newest_modification lsn 一直在更新, 那么即使開啟LogIndex 也無法將該P(yáng)age 刷下去, 帶來的問題是rw checkpoint 是無法推進(jìn), 但是由于有了LogIndex, 其他page 可以隨意刷臟, 所以不會(huì)出現(xiàn)rw 臟頁數(shù)不夠的問題. 那Page1 刷臟如何解決呢?
通過copy page 解決.
如果rw 開啟了copy page 以后, 雖然上圖中的Page1 剛剛被copy 出來的時(shí)候無法flush, 但是因?yàn)殚_啟LogIndex, ro apply_lsn 可以隨意推進(jìn), 隨著ro apply_lsn 的推進(jìn), 過一段時(shí)間一定可以刷這個(gè)copy page, 也就避免了這個(gè)問題了.
所以目前版本答案是 LogIndex + copy page 解決了幾乎所有問題
另外驗(yàn)證了刷臟約束兩種場景
大量寫入場景
有熱點(diǎn)頁場景
其實(shí)大量寫入場景即使導(dǎo)致了刷臟約束, 后面還是可以恢復(fù)的, 只有熱點(diǎn)頁場景才無法恢復(fù). 很多時(shí)候熱點(diǎn)頁不一定是用戶修改的page, 而是Btree 上面的一些其他page, 比如root page 等等, 我們很難發(fā)現(xiàn)的.
另外驗(yàn)證了如果page 以及 redo log 寫入延遲都升高, 是不會(huì)特別出現(xiàn)刷臟約束問題, 只有出現(xiàn)熱點(diǎn)頁的場景才會(huì)有問題.
上圖可以看到
ro parse buffer = ro appply_lsn - rw flush_lsn
apply_lsn 是ro 節(jié)點(diǎn)讀取redo 并應(yīng)用推進(jìn)的速度
flush_lsn 是rw 節(jié)點(diǎn)page 刷臟推進(jìn)的速度
由于IO 延遲同時(shí)影響了 redo 和 page, 從公式可以看到, 那么ro parse buffer 不會(huì)快速增長的.
從公式里面可以看到, 如果redo 推進(jìn)速度加快, page 刷臟速度減慢, 那么是最容易出現(xiàn)刷臟約束的. 也就是redo IO 速度不變, Page IO 速度變慢, 就容易出現(xiàn)把RO parse buffer 打滿的情況, 但是一樣需要出現(xiàn)熱點(diǎn)頁才能出現(xiàn)parse buffer 被打滿的死鎖.
如果沒有熱點(diǎn)頁, 這個(gè)時(shí)候由于parse buffer 還是再推進(jìn), 所以不會(huì)自動(dòng)crash, 反而會(huì)出現(xiàn)rw 由于被限制了刷臟, buffer pool 里面大量的臟頁, 最后找不到空閑Page 的情況. rw crash 的情況.
多版本或者Aurora 如何解決這個(gè)問題?
剛才上面的分析有兩個(gè)鏈條互相依賴
約束1: rw 的刷臟依賴ro 節(jié)點(diǎn)apply_lsn 的推進(jìn)
約束2: ro 節(jié)點(diǎn)釋放old parse buffer 依賴rw 節(jié)點(diǎn)刷臟
多版本/Aurora 都把約束2 給去掉了, ro 節(jié)點(diǎn)可以隨意釋放old parse buffer. 那么就不會(huì)有parse buffer 滿的問題, 那么如果ro 節(jié)點(diǎn)訪問到rw 還未刷下去page, 但是ro 節(jié)點(diǎn)已經(jīng)把Parse buffer 釋放了, 那么會(huì)通過磁盤上的 logIndex + 磁盤上page 生成想要的版本.
但是這里依然還要去解決約束1 的問題, rw 的刷臟會(huì)被ro 給限制. rw 刷臟時(shí)候判斷 page newest_modification_lsn > ro apply_lsn, 那么在Aurora 里面這個(gè)Page 也是無法進(jìn)行Apply 的, 但是Aurora 和我們區(qū)別在于Aurora 可以把這個(gè)Page 丟出buffer pool, 但是我們是無法把這樣的page 丟出Buffer Pool, 依然會(huì)造成Buffer Pool 里面大量的臟頁, 最后找不到空閑Page 的情況. 在多版本引擎里面支持把Page newest_modification_lsn > ro apply_lsn 這樣的Page 在Buffer Pool 中釋放也很重要.
-
節(jié)點(diǎn)
+關(guān)注
關(guān)注
0文章
220瀏覽量
24453 -
PAGE
+關(guān)注
關(guān)注
0文章
11瀏覽量
20191
原文標(biāo)題:PolarDB 物理復(fù)制刷臟約束問題和解決
文章出處:【微信號(hào):inf_storage,微信公眾號(hào):數(shù)據(jù)庫和存儲(chǔ)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評(píng)論請先 登錄
相關(guān)推薦
評(píng)論