髒頁刷入磁盤
學習檢測
-
什麼是髒頁?
-
什麼情況觸發刷髒頁行爲?
-
刷髒頁行爲對性能的消耗對比?
-
InnoDB刷髒頁的策略?影響磁盤刷髒頁的因素?
-
刷新髒頁速度的策略?
-
爲什麼不直接淘汰內存,加載數據的時候,直接用redo log來校驗?
-
什麼事刷髒頁 連坐行爲?
-
髒頁發現標準?
總結
-
當內存中的數據和磁盤中的數據不一致的時候,就是”髒頁”
-
觸發刷髒頁的情況
內存滿了
redo log 滿了
服務正常關閉
mysql空閒時候自己刷新
-
內存滿了
當有新數據的查詢,不在buffer pool中的時候,就需要申請一個新的page頁,如果沒有未使用page和乾淨的page就需要將”髒頁”刷新到磁盤,如果查詢的數據較多,刷髒頁的行爲事件較長
redo log 滿了
redo log 滿了的時候系統會停止所有的寫操作,影響業務
redo log 滿了需要將checkpoint 指針向前推進,writepoint 和 checkpoint之間的就是可以寫的部分
服務關閉 和 mysql “空閒” 對性能沒什麼影響
-
mysql 刷髒頁的策略 innodb_io_capacity 和磁盤IO能力有關,設置合理值(每秒系統IO能力),影響刷髒頁的因素有buffer pool最大髒頁比例 redo log 空閒空間(越大)刷的越快
-
max(髒頁比例 or redo log 可寫空間的最大值的比例) 來刷髒頁
-
數據操作是在buffer pool ,第一種情況,如果在內存中,直接操作,返回結果,不在內存,讀取磁盤,加載到內存,返回結果,如果有redo log 的話,就要和redo log 進行校驗在返回,redo log 也是文件形式,並且是物理邏輯日誌,會更消耗性能
-
髒頁有可能有連坐,發現自己的鄰居有髒頁也會刷,控制參數是
innodb_flush_neighbors
-
每個數據頁都有頭部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的策略是儘量使用內存,因此對於一個長時間運行的庫來說,未被使用的頁面很少
而當要讀入的數據頁沒有在內存的時候,就必須到申請池中去申請一個數據頁,這時候只能把最久不使用的數據頁從內存中淘汰掉;如果要淘汰的是一個乾淨頁,就直接釋放出來複用;但如果是髒頁呢,就必須將髒頁先刷到磁盤,變成乾淨頁才能複用
所以,刷髒頁是常態,但是出現以下情況時候,都會明顯影響性能的
-
一個查詢要淘汰的髒頁個數較多,會導致查詢的響應時間明顯變長
-
日誌寫滿,更新全部堵住,寫性能跌爲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值小的代表還沒有被寫入東西