MySQL內存結構-緩沖區
MySQL的緩沖區中有數據頁,索引頁,插入緩沖等等,這個角度是從頁的功能來分類的。本小節從另一個視角關注這些頁,如果從 是否被修改過(和磁盤不一致) 這個角度來區分這些頁,那么頁可以被分為干凈的頁和臟頁。
- 干凈頁:內存中的數據和磁盤一致
- 臟頁:內存中的數據和磁盤不一致
本小節主要關注臟頁刷新到磁盤的機制,首先需要了解緩沖區的內存管理細節。
內存管理機制簡述
緩沖區中包含這三大類列表。分別為:LRUList、FreeList、FlushList。
在數據庫剛啟動時,LRUlist中並沒有數據頁,空閑頁都存放在FreeList中,當需要讀取某個頁時,會從free列表中獲取一個空閑頁,讀入數據后,放入LRU列表中;如果free中沒有空閑頁了,那么根據LRU算法淘汰Lru列表中末位的頁。
- LRU列表:管理已經被讀取的頁
- Free列表:管理空閑的頁
當LRU中的頁被修改后,頁就變成了臟頁,這個頁也會被加入Flush列表中。注意:這時這個頁既在LRU列表中,又在Flush列表中。
- Flush列表:管理臟頁
總結:LRUList和FreeList用來管理頁的可用性;Flush列表用來管理臟頁的刷新
臟頁的刷新機制-checkpoint機制
數據修改和讀取只依賴緩沖區行不行?
如果數據修改和讀取只依賴內存的緩沖區,那么一旦數據庫宕機,內存中的數據都會丟失。所以MySQL使用之前講過的redo log來實現異常重啟的數據恢復,redolog相關介紹可以看篇文章:MySQL-redo log 和 binlog
簡單來說,就是在更新緩沖區之前,先寫入redo log,保證異常重啟之后可以正常恢復緩沖區中的數據。
臟頁為什么一定要刷新?
考慮這種情況
- 緩沖區可以無限大
- redo log可以無限大或者無限拆分為多個文件
如果緩沖區無限大,可以裝下所有的磁盤數據,redo log也可以無限大,那么就算異常重啟,依靠redo log也可以恢復所有的更新。但現實中,首先,緩沖區依賴的內存空間不可能無限大,現實中有許多TB級別的數據庫,但是目前還沒有TB級別的內存;並且 redo log如果無限大或者有許多個文件,對運維和管理也是一個考驗;最后,如果系統中有大量的修改操作,一旦宕機,恢復的時間也會非常長。
所以自然而然,我們就一定需要把內存中的臟頁按照某種規則刷新到磁盤中,有了刷新這個操作,緩沖區的大小問題和redo log的大小問題都可以解決。
- 緩沖區不需要無限大了,因為可以持久化到磁盤
- redo log也不需要無限大了,因為一旦持久化到磁盤,redo log中對應的那部分數據就可以釋放。
如何刷新呢?
上部分討論的是臟頁刷新到磁盤的必要性。那具體應該如何刷新呢?MySQL中,刷新的規則叫checkpoint機制。
在InnoDB存儲引擎中,有兩種checkpoint:
- sharp checkpoint:在數據庫關閉時,刷新所有的臟頁到磁盤,這里有參數控制,默認是開啟的
- fuzzy checkpoint:刷新一部分臟頁到磁盤中。
關於Fuzzy checkpoint,InnoDB存儲引擎中可能包括如下幾種:
- 定時刷新 - master thread做
- Flush LRUlist checkpoint -page cleaner thread做
- async/sync checkpoint -page cleaner thread做
- dirty too much checkpoint -?誰做?
刷新機制用更通俗易懂的角度來分析,上面的四種類型可以對應下面的
- 無論如何,定時刷新
- 當LRU中列表中空閑頁不足時,強制LRU刪除一些末尾的頁,如果存在臟頁,那么需要checkpoint刷新
- 使用innodb_lru_scan_depth來控制最少空閑頁的數量
- 當重做日志不夠用時,從flush 列表中選擇一些頁,強制checkpoint刷新
- 重做日志有兩個水位:async水位 75% * innodb的總大小;sync水位:90* innodb大小。
- 當未刷新的數據大小 小於 低水位,不需要刷新
- 當未刷新的數據大小 大於 低水位,小於高水位,異步刷新,保證刷新后小於 低水位
- 當未刷新的數據大小 大於 高水位,同步阻塞刷新,保證刷新后小於 低水位。
- 關注系統中的整體臟頁比例,如果達到一定比例,強制刷新
- 使用 innodb_dirty_page_pct來控制這個比例數值,默認時75%
master thread中的定時刷新機制
1)InndoDB1.0.x版本之前的master thread。
每秒,會進行一次 dirty too much checkpoint。
每10秒
- 判斷過去10秒的IO操作是否小於200次,如果是,刷100個臟頁;
- 判斷系統當前臟頁比例,如果超過70%,刷新100個;如果小於70%,刷新臟頁的10%
2)InndoDB1.2.x版本之前的master thread。
在1.0.x存在硬編碼,每秒最多只會刷新100個臟頁到磁盤中,這種規定其實限制了性能更高的SSD磁盤。
在1.0.x版本,可以使用innodb_io_capacity來表示磁盤io的吞吐量。刷新臟頁的數量由innodb_io_capacity來控制,默認是200。
總結
了解臟頁刷新機制以及相應的參數是很有必要的,當數據庫系統某些性能問題時,要考慮是否是臟頁刷新相關的配置參數不合理導致的。
根據實際業務,考慮緩沖區的大小,redo log的大小,最少空閑頁,臟頁比例,io吞吐量相關參數是否配置合理,根據優化相關參數,解決系統問題。