想要優化寫的速度,我們首先會想到什麼?
随機IO轉為順序IO,單次寫轉為批次寫。
緩存
常見的寫邏輯優化,先寫緩存,然後刷盤。
邏輯:
- 如果頁在記憶體中,直接修改。如果不在,讀到記憶體,再修改。
- 定期刷盤。
資料一緻性
如果命中了緩存,讀到的是準确的。記憶體淘汰的時候,刷盤了,再讀磁盤的時候資料也是準确的。
問題
這樣有兩個問題:
- 寫多讀少,就會導緻命中緩存的機率比較低,每次都要先讀到記憶體。
- 如果崩潰了,記憶體的資料就丢了。
redolog
Innodb建立了redolog,即預寫日志。寫記憶體不變,添加了一步寫redolog的步驟。刷盤的時候就把日志清掉。如果崩潰了,就從日志讀取。
WAL技術,WAL的全稱是Write-Ahead Logging,它的關鍵點就是先寫日志,再寫磁盤。
redolog裡面存的是結果值。也就是将change buffer同步到日志了。(binlog寫的是流水日志)
這樣就轉化成了一次讀磁盤,一次寫記憶體操作和一次追加日志寫的操作,都挺快。
change buffer
還有個問題就是寫必須先讀。
我們将更新的資料存到change buffer中,有人讀則将讀到的資料和change buffer中的資料一起傳回。刷盤的時候将資料marge後刷盤。
這樣就優化成了一次寫記憶體+一次日志追加寫。
但是要注意:
不支援唯一索引頁。為什麼?我不讀盤怎麼知道你有沒有沖突呢?
什麼時候刷盤?
- 系統記憶體滿了。
- MySQL比較閑
- 正常關閉
這些都比較好了解。
還有個下面的幾種:
redolog寫滿了。
注意redolog不是無限寫的,而是一個圈。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjcmbw5CO5UmN2U2Y0MTY0M2YhFjZ4gDMjNGZxMTYkBTNlJjM58CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
- write pos是目前記錄的位置,一邊寫一邊後移,寫到第3号檔案末尾後就回到0号檔案開頭。
- checkpoint是目前要擦除的位置,也是往後推移并且循環的,擦除記錄前要把記錄更新到資料檔案。
- write pos和checkpoint之間的是還空着的部分,可以用來記錄新的操作。
- 如果write pos追上checkpoint,表示滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint推進一下。
定期重新整理髒頁或者LRU淘汰髒頁
記憶體和磁盤資料一緻的,稱為幹淨頁;反正,稱為髒頁。将記憶體資料寫入磁盤,則便一緻,稱為刷髒頁。
兩種坑:
- 某個查詢需要重新整理過多髒頁,則很慢。
- redolog寫滿,所有更新都會卡住。
那麼,怎麼提高刷髒頁的效率?
- 正确地設定innodb_io_capacity參數,判斷寫盤速度。
- 髒頁比例,innodb_max_dirty_pages_pct。
在準備刷一個髒頁的時候,如果這個資料頁旁 邊的資料頁剛好是髒頁,就會把這個“鄰居”也帶着一起刷掉;而且這個把“鄰居”拖下水的邏輯還 可以繼續蔓延。
innodb_flush_neighbors 參數,為0就是隻刷自己。
疑惑
很多人可能會問,到底是從redolog刷到磁盤,還是從記憶體刷到磁盤。
回到redolog建立的原因,是為了異常崩潰的時候。
是以正常刷盤是從記憶體刷到磁盤,redolog滿了也是從記憶體刷到磁盤然後清理redolog。異常崩潰呢?也是從redolog寫到記憶體中,先恢複現場,再走正常流程。
結語
- 如果有不對的地方歡迎指正。
- 如果有不了解的地方歡迎指出我來加栗子。
- 如果感覺OK可以點贊讓更多人看到它。
相關閱讀:
- 學MySQL的第一座大山—索引
- 學MySQL的第二座大山—鎖,事務
- 學習mysql的最後一座大山—表設計
- MySQL為什麼要用B+樹?
- MySQL怎麼緩解讀的壓力的?—buffer pool
- MySQL怎麼緩解寫的壓力?— change buffer
- MySQL架構概覽