digoal
2016-10-06
postgresql , 9.6 , 平滑 fsync , write , smooth fsync
汽車換擋是否平順,通常取決于檔位數,或者換擋技術。
檔位數越多,換擋時感覺會約平順,檔位數較少的情況下,換擋可能會有比較明顯的頓挫感覺。
資料庫也一樣,有些時候可能就會出現卡頓的現象,比如尖銳(堆積)的io需求時。
本文将給大家介紹9.6在fsync, write方面的平順性改進,減少尖銳的io需求。
資料庫為了保證資料可靠性,同時還要保證好的讀寫性能,以及讀寫的一緻性,經過多年的發展,redo日志,shared buffer基本已經成為資料庫的标配。
為了保證資料的可靠性,通常需要在将dirty page刷盤前,保證其redo先刷盤,然後再通過lru算法異步的老化shared buffer中的dirty page。
為了保證好的讀寫性能,通常會需要shared buffer,寫先落在shared buffer中,而不是直接同步修改資料頁,因為資料頁很離散。 資料庫會把把離散的io轉換成順序的redo io。
那麼問題來了,什麼時候會調用fsync,什麼時候會調用write呢?
為什麼9.6會需要優化平滑的fsync, write呢?
調用write的地方較多,我舉一些非常重度的write場景,也是9.6重點優化的地方。
1. shared buffer
bgwriter背景程序會将shared buffer中的髒頁根據設定的喚醒時間,老化算法,寫排程設定,将老化的dirty page寫到作業系統。 (調用系統的write接口) 。
backend process 程序,當請求shared buffer中的page時,如果沒有足夠的空閑page則會主動觸發與bgwriter一樣的操作,将老化的dirty page寫到作業系統。 (調用系統的write接口) 。
2. wal buffer
walwriter背景程序,将wal buffer中的髒頁根據設定的喚醒時間,将wal buffer中的dirty page寫到作業系統。 (調用系統的write接口) 。
注意walwriter的fsync接口是可配置的,有buffer writer也有direct io。 如果配置了direct io則直接落盤,不會寫到作業系統的dirty page。
因為write實際上是寫到了作業系統中,作業系統再排程将髒頁落盤。
作業系統排程刷髒頁涉及到幾個核心參數,同時還涉及到檔案系統。
如果系統記憶體非常大,當觸發背景線程刷髒頁時,可能需要刷很多髒頁,導緻尖銳的io需求。
是以,我們可以通過修改核心參數達到削尖的目的。
但是這樣設定可能還不夠,因為資料庫是可以并發操作的(wal writer, bgwriter, backend processes都可能并發的調用write),也就是說高峰時産生髒頁的速度可能遠遠大于作業系統背景線程flush的速度。
是以這種情況下os dirty page還是可能堆積,爆發尖銳的io需求。
1. create database
建立資料庫時,需要copy 模闆庫dir,每個檔案copy一部分後,會調用sync_file_range,最後調用fsync持久化。

如果模闆庫檔案數多,或者檔案很大,可能導緻産生較多的os dirty page。
2. checkpoint
2.1 首先标記shared buffer中的髒頁
2.2 對已标記為髒頁的page,調用write
如果shared buffer很大,并且業務形态導緻資料庫産生髒頁速度很快的話,檢查點會瞬間産生很多的os dirty page。
2.3 對相關的fd,調用fsync,持久化
資料庫檢查點程序調用fsync,如果os背景線程沒有将檢查點過程中write出去的髒頁落盤,資料庫檢查點程序fsync會産生大量的刷盤io。
3. wal writer
wal writer會根據配置的fsync系統調用方法、排程間隔,将wal buffer中的資料刷到xlog檔案。
如果wal buffer配置較大,同時資料庫高并發的産生大量的redo,則wal writer也會産生大量的寫盤io。
1. 如果模闆庫檔案數多,或者檔案很大,create database可能導緻瞬間産生較多的os dirty page,在檔案write完後,調用fsync導緻大量的寫盤io。
2. 如果shared buffer很大,并且業務形态導緻資料庫産生髒頁速度很快的話,檢查點會瞬間産生很多的os dirty page。
3. 資料庫檢查點程序調用fsync,如果os背景線程沒有将檢查點過程中write出去的髒頁落盤,資料庫檢查點程序fsync會産生大量的刷盤io。
4. 如果wal buffer配置較大,同時資料庫高并發的産生大量的redo,則wal writer也會産生大量的寫盤io。
前面分析了資料庫write, fsync在特定的場景(通常是寫非常重的場景)中,可能導緻大量的io需求。
這些需求其實是因為資料庫為了提高性能,大量的時候了buffer,并且沒有很好的處理buffer堆積,導緻蜂擁而至的寫盤io請求。
通過配置os的backend flush線程的排程參數,可以緩解,但是不能徹底根治(無法抵禦高并發的寫,有點像三英占呂布的感覺,呂布再強也幹不過高并發的寫操作産生的os dirty page)。
那麼9.6是如何改進的呢?
1. 新增 sync_file_range 異步寫的排程政策
通過以下4個參數,控制這4中程序的write操作
9.6以前的版本,我們可以認為他們是沒有克制的濫用write調用,高峰時比較容易出現os backend flush線程跟不上的節奏。
9.6改成了有節制的使用write,即每隔一段,會提醒背景線程刷髒頁。
但是由于使用的是sync_file_range的異步接口,問題可能不能完全解決,再改進一下,當os dirty page超過多少的時候,觸發sync_file_range的同步調用可能更好(起到抑制産生髒頁的速度的左右)。
2. 檢查點write屬于同一個fd的dirty page時,排序後再write,進而降低離散的io。
<a href="https://github.com/digoal/blog/blob/master/201609/20160928_01.md">《postgresql 9.6 檢查點柔性優化(sync_file_range) - 在單機多執行個體下的io hang問題淺析與優化》</a>
src/backend/storage/file/fd.c
1. backend_flush_after (integer)
2. bgwriter_flush_after (integer)
3. checkpoint_flush_after (integer)
4. wal_writer_flush_after (integer)
<a href="http://info.flagcounter.com/h9v1">count</a>