髒頁刷入磁盤

髒頁刷入磁盤

學習檢測

  1. 什麼是髒頁?

  2. 什麼情況觸發刷髒頁行爲?

  3. 刷髒頁行爲對性能的消耗對比?

  4. InnoDB刷髒頁的策略?影響磁盤刷髒頁的因素?

  5. 刷新髒頁速度的策略?

  6. 爲什麼不直接淘汰內存,加載數據的時候,直接用redo log來校驗?

  7. 什麼事刷髒頁 連坐行爲?

  8. 髒頁發現標準?

總結

  1. 當內存中的數據和磁盤中的數據不一致的時候,就是”髒頁”

  2. 觸發刷髒頁的情況

    內存滿了

    redo log 滿了

    服務正常關閉

    mysql空閒時候自己刷新

  3. 內存滿了

當有新數據的查詢,不在buffer pool中的時候,就需要申請一個新的page頁,如果沒有未使用page和乾淨的page就需要將”髒頁”刷新到磁盤,如果查詢的數據較多,刷髒頁的行爲事件較長

redo log 滿了

redo log 滿了的時候系統會停止所有的寫操作,影響業務

​ redo log 滿了需要將checkpoint 指針向前推進,writepoint 和 checkpoint之間的就是可以寫的部分

服務關閉 和 mysql “空閒” 對性能沒什麼影響

  1. mysql 刷髒頁的策略 innodb_io_capacity 和磁盤IO能力有關,設置合理值(每秒系統IO能力),影響刷髒頁的因素有buffer pool最大髒頁比例 redo log 空閒空間(越大)刷的越快

  2. max(髒頁比例 or redo log 可寫空間的最大值的比例) 來刷髒頁

  3. 數據操作是在buffer pool ,第一種情況,如果在內存中,直接操作,返回結果,不在內存,讀取磁盤,加載到內存,返回結果,如果有redo log 的話,就要和redo log 進行校驗在返回,redo log 也是文件形式,並且是物理邏輯日誌,會更消耗性能

  4. 髒頁有可能有連坐,發現自己的鄰居有髒頁也會刷,控制參數是

    ​ innodb_flush_neighbors

  5. 每個數據頁都有頭部LSN,8字節,每次修改都會變大,對比LSN跟checkoutpoint中值小的,一定是乾淨頁,checkoutpoint 的變大說明有新事物增加了,page 小的話說明內存頁是操作
    在這裏插入圖片描述

buffer pool 參數

在這裏插入圖片描述

新數據寫入和flush示意圖

在這裏插入圖片描述

flush 觸發情況

redo log 滿了

InnoDB的redo log 寫滿了,這時候系統會停止所有的更新操作,把chekpoint往前推進(redo log 日誌寫入磁盤),redo log 留出空間繼續寫
在這裏插入圖片描述
checkpoint 可不是隨便往前修改一下位置就可以的。比如圖 2 中,把 checkpoint 位置從 CP 推進到 CP’,就需要將兩個點之間的日誌(淺綠色部分),對應的所有髒頁都 flush 到磁盤上。之後,圖中從 write pos 到 CP’之間就是可以再寫入的 redo log 的區域。

系統內存滿了

對應的就是系統內存不足。當需要新的內存頁,而內存不夠用的時候,就要淘汰一些數據頁,空出內存給別的數據頁使用。如果淘汰的是“髒頁”,就要先將髒頁寫到磁盤。

mysql 認爲系統空閒

當然,MySQL忙起來可是會很快就能把redo log 記滿的,所以要合理地安排時間,即使是忙的時候,也要見縫插針地找時間,只要有機會就刷一點“髒頁”。

mysql 正常關閉的情況

MySQL 正常關閉的情況。這時候,MySQL 會把內存的髒頁都 flush 到磁盤上,這樣下次 MySQL 啓動的時候,就可以直接從磁盤上讀數據,啓動速度會很快。

四種情況的性能對比

第三種,在mysql”空閒的時候”,這時系統沒什麼壓力,而第四種是數據庫本來就要關閉,這兩種情況下,你不會太關注”性能問題”,所以這裏,我們主要分析一下前兩種場景下的問題

第一種是redo log 寫滿,要”flush髒頁”,這種情況是InnoDB要儘量避免的,因爲出現這種情況,整個系統就不能接受更新了,所有的更新都必須堵住,如果你從監控上看,這時候更新數爲0

第二種是”內存不夠用了,先將髒頁寫到磁盤”,這種情況是常態,InnoDB用緩存池(buffer pool)管理內存,緩衝池中頁的狀態有三種

第一種,還沒使用的

第二種,使用了並且是乾淨的

第三種,使用了並且是髒頁

InnoDB的策略是儘量使用內存,因此對於一個長時間運行的庫來說,未被使用的頁面很少

而當要讀入的數據頁沒有在內存的時候,就必須到申請池中去申請一個數據頁,這時候只能把最久不使用的數據頁從內存中淘汰掉;如果要淘汰的是一個乾淨頁,就直接釋放出來複用;但如果是髒頁呢,就必須將髒頁先刷到磁盤,變成乾淨頁才能複用

所以,刷髒頁是常態,但是出現以下情況時候,都會明顯影響性能的

  1. 一個查詢要淘汰的髒頁個數較多,會導致查詢的響應時間明顯變長

  2. 日誌寫滿,更新全部堵住,寫性能跌爲0,這種情況對敏感業務來說,是補鞥被接受的

所以InnoDB需要有控制髒頁比例的機制,來避免上面情況

InnoDB 刷髒頁的控制策略

innodb_io_capacity flush 刷磁盤能力(系統每秒I/O數)

InnoDB IO 能力
在這裏插入圖片描述

查看系統磁盤的IOPS

fio工具

fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest

影響磁盤刷髒頁的因素

髒頁比例

redo log寫盤速度

刷新髒頁速度策略

計算髒頁比例和redo log 日誌序號和當前之日序號差值,取最大值%進行刷盤操作

innodb_max_dirty_pages_pct

​ 髒頁比例上限 默認值是75%

InnoDB會根據當前的髒頁比例(假設爲M),算出一個範圍在0–100之間的數字

僞代碼

F1(M)
{
 if M>=innodb_max_dirty_pages_pct then
   return 100;
 return 100*M/innodb_max_dirty_pages_pct;
}

InnoDB 每次寫入的日誌都有一個序號,當前寫入的序號跟 checkpoint 對應的序號之間的差值,我們假設爲 N。InnoDB 會根據這個 N 算出一個範圍在 0 到 100 之間的數字,這個計算公式可以記爲 F2(N)。F2(N) 算法比較複雜,你只要知道 N 越大,算出來的值越大就好了。

然後,根據上述算得的 F1(M) 和 F2(N) 兩個值,取其中較大的值記爲 R,之後引擎就可以按照 innodb_io_capacity 定義的能力乘以 R% 來控制刷髒頁的速度。

上述的計算流程比較抽象,不容易理解,所以我畫了一個簡單的流程圖。圖中的 F1、F2 就是上面我們通過髒頁比例和 redo log 寫入速度算出來的兩個值。
在這裏插入圖片描述
現在你知道了,InnoDB 會在後臺刷髒頁,而刷髒頁的過程是要將內存頁寫入磁盤。所以,無論是你的查詢語句在需要內存的時候可能要求淘汰一個髒頁,還是由於刷髒頁的邏輯會佔用 IO 資源並可能影響到了你的更新語句,都可能是造成你從業務端感知到 MySQL“抖”了一下的原因

要儘量避免這種情況,你就要合理地設置 innodb_io_capacity 的值,並且平時要多關注髒頁比例,不要讓它經常接近 75%。其中,髒頁比例是通過 Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total 得到的,具體的命令參考下面的代碼:

mysql> select VARIABLE_VALUE into @a from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty';
select VARIABLE_VALUE into @b from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';
select @a/@b;

爲什麼不直接淘汰內存,讀磁盤和redo log merge 操作

數據操作數據頁有兩種狀態 保證內存中或者磁盤中的數據某一個是最正確的

  • 數據在內存頁中,內存中就是正確的結果,直接返回

  • 數據不在內存頁,文件上的爲正確的,讀入內存在處理,

如果直接淘汰內部存,數據頁不在內存,讀磁盤到內存,然後在和redo log 進行merge過程,會增加性能消耗,不能保證內存是最新的

刷髒頁”連坐"控制

innodb_flush_neighbors

​ 值爲1標識連坐刷髒頁

​ 0位只刷自己的

一旦一個查詢請求需要在執行過程中先 flush 掉一個髒頁時,這個查詢就可能要比平時慢了。而 MySQL 中的一個機制,可能讓你的查詢會更慢:在準備刷一個髒頁的時候,如果這個數據頁旁邊的數據頁剛好是髒頁,就會把這個“鄰居”也帶着一起刷掉;而且這個把“鄰居”拖下水的邏輯還可以繼續蔓延,也就是對於每個鄰居數據頁,如果跟它相鄰的數據頁也還是髒頁的話,也會被放到一起刷。

在 InnoDB 中,innodb_flush_neighbors 參數就是用來控制這個行爲的,值爲 1 的時候會有上述的“連坐”機制,值爲 0 時表示不找鄰居,自己刷自己的。

找“鄰居”這個優化在機械硬盤時代是很有意義的,可以減少很多隨機 IO。機械硬盤的隨機 IOPS 一般只有幾百,相同的邏輯操作減少隨機 IO 就意味着系統性能的大幅度提升。而如果使用的是 SSD 這類 IOPS 比較高的設備的話,我就建議你把 innodb_flush_neighbors 的值設置成 0。因爲這時候 IOPS 往往不是瓶頸,而“只刷自己”,就能更快地執行完必要的刷髒頁操作,減少 SQL 語句響應時間。

在 MySQL 8.0 中,innodb_flush_neighbors 參數的默認值已經是 0 了

髒頁發現機制

每個數據頁頭部有LSN,8字節,每次修改都會變大。

對比這個LSN跟checkpoint 的LSN,比checkpoint小的一定是乾淨頁

因爲 事務都要寫redo log ,比redo log checkpoint值小的代表還沒有被寫入東西

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章