作者介紹
朱賢文,成都文武資訊技術有限公司創始人,PostgreSQL中國使用者會核心組成員,熟悉資料庫,存儲和叢集技術;
成都文武資訊技術有限公司是PostgreSQL和GreenPlum資料庫服務的專業廠商,主要産品是ECOX叢集管理系統和Hunghu Cloud,專門運作資料庫的私有雲系統,帶高端存儲功能。公司總部位于天府軟體園。公司網站:w3.ww-it.cn
寫本文的原因
這兩天有兩篇專門介紹PostgreSQL的vacuum機制的技術文章,得到了比較熱烈和正面的回報,讓使用者可以比較清楚地了解和使用這個特性。
我個人覺得有點小遺憾:這兩篇文章沒有跳出技術的角度,分析為什麼會有這樣的機制和實作。是以我打算寫點文字補充一下,跳出技術角度,來了解和分析為啥會有這兩種機制,以及PostgreSQL選擇這樣的機制又啥好處。最後再總結一下如何用好這種機制,讓資料庫更快更好地服務我們的生産系統。
如果有不合妥當的地方,歡迎斧正 !
多版本并發控制機制
要說清楚這個事情之前,我們先看看幾種基本的多版本并發控制機制的實作方式。實作MVCC的資料庫管理系統,當它需要更改某塊資料的時候,它不會直接去更改,而是會建立這份資料的新版本,在新版本進行更改,是以會存儲多份版本,每個事務能看見哪一份版本的資料,要看隔離級别的實作方式,通常的辦法是資料塊的快照。
正是因為這個原因,引入了另一個問題,如何消除老舊的、沒有使用的無用資料(版本),目前主流上有3種處理實作方式:
第一種,以Oracle為代表的,把舊版本資料放入UNDO,新資料放入REDO,然後更改資料。這種方式,舊版本的資料放入了UNDO,是以可以有效避免膨脹。
第二種,以SQL Server為代表的,把舊版本的資料寫入專門的臨時表空間,新資料寫入日志,然後去更改資料。這種方式,舊版本的資料放入了專門的臨時表空間,是以也可以有效地避免膨脹。
第三種,以PostgreSQL為代表的,把舊版本标示為無效,新資料寫入日志,成功後把新版本的資料寫入新的位置。這種實作機制是導緻資料膨脹嚴重的一個重要原因,因為舊版本的資料雖然表示為無效狀态,但是沒被回收前還是占據存儲空間。
各種機制的比較
對于第一種、第二種實作方式,好處是更改資料的時候不會導緻明顯的膨脹,把資料膨脹限制在專門的存儲空間内。不好的地方也比較明顯,因為改寫資料的時候,會先把舊版本資料寫入到UNDO或者臨時表空間,然後再寫日志,是以IOPS的消耗會大一倍,IOPS沒有被有效地用于事務處理,這是其一;其二以Oracle為例,undu/redo設定的大小跟業務系統的負載特征相關性很強,設定不好容易導緻問題,有經驗的DBA一定遇到過snapshot too old這樣的錯誤,就是undo空間不合适導緻的一種問題;其三,在資料庫崩潰的時候,重新啟動資料庫會有一個恢複的過程,簡單地說Oracle這種實作方式,繁忙的資料庫恢複的過程長,啟動很慢,因為正确的資料版本需要通過undo,redo的資料去做比較和計算,對于生産系統可能不允許(當然也跟checkpoint本身的設定有關系)。
對于第三種實作方式,好處是IOPS可以充分地用于事務處理,提高業務系統的性能,系統恢複的時候,復原操作隻需要變更指針,所有的資料都在系統能,系統可以快速完成復原和恢複,讓
手機遊戲賬号資料庫系統盡快投入生産工作。這種實作方式也會有明顯的副作用,就是容易産生資料膨脹,資料庫需要經常做回收的工作;
再論實作機制的取舍
站在另一個角度,要弄清楚這個問題,還需要了解一下磁盤提供的基本功能。磁盤本身提供幾個特性:
對外提供存儲容量,儲存資料,比如600GB;
磁盤每秒向應用提供一定中斷響應次數,術語上叫IOPS (IO Per Sec),這個跟磁盤轉速有關系;現在的高速磁盤單盤都可以提供80~100 IOPS。
傳輸資料的的帶寬,比如ATA 33/66/100 SATA 3G/6G等。
磁盤驅動器在90年代前是很昂貴的外部裝置,提供的存儲空間和IOPS都非常有限;它作為應用系統永久存儲資料的重要儲存設備,在使用它的時候需要仔細規劃,合理取舍,需要仔細權衡如何使用好這種資源。
Oracle為代表的資料庫,明顯是傾向将磁盤的空間有效利用起來,避免資料膨脹,雖然會犧牲一些性能,但是磁盤空間的利用效率高一些,90年代以前的應用系統大多數是單機系統,沒那麼并發使用者,是以将磁盤空間合理利用起來,這種設計思想也是合理的。
PostgreSQL為代表的資料庫,明顯傾向于有效利用IOPS,将能利用的IOPS盡量用于支援應用系統,以提升應用系統的性能;同時犧牲磁盤空間的使用率作為代價,因為90年代以前,磁盤的IOPS真的很昂貴,PostgreSQL的設計可以充分的用好硬體的IOPS,有資料膨脹的負面影響也是值得的,因為膨脹的空間可以放在夜間來回收。
是以設計MVCC的實作機制,為啥要選擇犧牲資料膨脹,或者犧牲IOPS,它們隻是站在不同的角度,選擇了不同的技術實作方式,做了合理的取舍的結果;不同的實作有好的方面,也有負面影響。
最佳實踐
既然現在PostgreSQL有比較大的潛在的資料膨脹的問題,如何利用好PostgreSQL,避免這種機制引起的資料膨脹和相關的負面影響,讓PostgreSQL更好地為業務系統服務,那麼解決方法不外乎從兩個方面入手:
一方面需要盡量減少資料膨脹,具體方法就是設定合适的fillfactor這個參數,讓每個資料塊預留一定的空間用于記錄更新;當有預留更新的空間,新記錄會在預留的空間内更新,因為舊資料跟更新後的資料在同一個資料塊中,是以索引本身不必更新;可以降低IO發生的次數,并且提高性能;一般來說設定為建議為80,這個值可以根據應用的特點進行調整,比如:如果沒有update,隻有insert/delete這樣的操作,可以用預設的100;如果update的幾率比較大,比如每一條資料都需要改,那麼50是一個好的開始。
另一方面是讓被占用的磁盤空間盡快回收,具體方法就是設定好vacuum相關的參數,讓資料庫内的垃圾資料及時和有效地得到回收。中心思想就是需要讓回收的動作在系統設定允許的時間内完成垃圾資料的回收清理,這裡面涉及到一些具體的參數,需要強調一下,系統預設的參數autovacuum_vacuum_scale_factor=0.2,以及autovacuum_analyze_scale_factor=0.1,這兩個系統預設設定對于大表明顯太大,建議對于大表可以有針對性的設定。具體的方法,使用者可以更加自己系統的硬體配置,負載特征以及表的實際情況,設定合适的值,主要是autovacuum*相關的設定;