來源:https://blog.csdn.net/wzyzzu/article/details/50426692
《PostgreSQL數據庫內核分析》第2章PostgreSQL的體系結構,本章從宏觀上對PostgreSQL的控制和處理流程進行了簡要介紹,說明了各個模塊之間是如何協同工作,以使得整個數據庫系統能夠穩定、正確地處理用戶的各種操作和請求的。至於每個模塊是如何各司其職,其內部具體是如何運作的,將會在后續的章節進行專門的介紹。本節為大家介紹AutoVacuum系統自動清理進程。
2.5.5 AutoVacuum系統自動清理進程
在PostgreSQL數據庫中,對表元組的UPDATE或DELETE操作並未立即刪除舊版本的數據,表中的舊元組只是被標識為刪除狀態,並未立即釋放空間。這種處理對於獲取多版本並發控制是必要的,如果一個元組的版本仍有可能被其他事務看到,那么就不能刪除元組的該版本。當事務提交后,過期元組版本將對事務不再有效,因而其占據的空間必須回收以供其他新元組使用,以避免對磁盤空間增長的無休止的需求,此時對數據庫的清理工作通過運行VACUUM來實現。從PostgreSQL 8.1開始,PostgreSQL數據庫引入一個額外的可選輔助進程AutoVacuum(系統自動清理進程),自動執行VACUUM和ANALYZE命令,回收被標識為刪除狀態記錄的空間,更新表的統計信息。
在PostgreSQL數據庫系統配置文件中,與系統自動清理相關的主要相關參數如下:
autovacuum:是否啟動系統自動清理功能,默認值為on。
autovacuum_max_workers:設置系統自動清理工作進程的最大數量。
autovacuum_naptime:設置兩次系統自動清理操作之間的間隔時間。
autovacuum_vacuum_threshold和autovacuum_analyze_threshold:設置當表上被更新的元組數的閾值超過這些閾值時分別需要執行vacuum和analyze。
autovacuum_vacuum_scale_factor和autovacuum_analyze_scale_factor:設置表大小的縮放系數。
autovacuum_freeze_max_age:設置需要強制對數據庫進行清理的XID上限值。
AutoVacuum系統自動清理進程中包含兩種不同的處理進程:AutoVacuum Launcher和AutoVacuum Worker。AutoVacuum Launcher進程為監控進程,用於收集數據庫運行信息,根據數據庫選擇規則選中一個數據庫,並調度一個AutoVacuum Worker進程執行清理操作。在AutoVacuum Launcher進程中,選擇數據庫的規則如下:首先由於數據庫事務XID是32位整數且遞增分配,當超過最大值時會從頭開始計數使用,而事務XID的大小表示事務開始的時間,事務XID重新計數使用會使數據庫中部分事務數據丟失,因此當XID超過配置的autovacuum_freeze_max_age時,強制對該數據庫進行清理並更新事務XID;其次,若無強制清理操作,則選擇數據庫列表中最早未執行過自動清理操作的數據庫。Launcher進程會定時(或者被信號驅動)選擇數據庫並調度Worker進程去執行清理工作。
AutoVacuum中的AutoVacuum Worker進程執行實際的清理任務,Launcher進程中維護有Worker進程列表。Worker進程列表由三種不同狀態的進程列表構成,即空閑的Worker進程列表、正在啟動的Worker進程、運行中的Worker進程列表。Launcher進程在不同狀態之間的切換實現了Worker進程的調度工作。首先,在初始化階段創建的運行內存上下文中,創建長度為autovacuum_max_workers的空閑Worker進程描述信息列表,而正在啟動Worker進程和運行中的Worker進程的列表被置為空。如果Launcher進程需要一個Worker進程,空閑Worker進程列表不為空且當前沒有正在啟動中的Worker進程,則開始一個啟動Worker進程的操作,即向Postmaster進程發送啟動消息,從空閑Worker進程列表中取出一個進程描述信息,設置為啟動中狀態。Launcher進程中只允許存在一個啟動中狀態的Worker進程,啟動中的Worker進程如果超時(超時時間由autovacuum_naptime設置)將被取消並重新開始啟動Worker進程的循環。如果Worker啟動成功,將啟動成功的Worker進程信息添加到運行中的Worker進程列表中。運行中的Worker進程即連接上根據規則選中的數據庫。
在啟動成功的Worker進程連接數據庫成功后,將遍歷該數據庫中的表,根據對表的清理規則選擇要執行的表和在該表上執行的操作。對表的操作分為VACUUM和ANALYZE兩種,對選中的表如果上次VACUUM之后的過期元組的數量超過了“清理閾值”(vacuum threshold),那么就清理該表,清理閾值是定義為:
清理閾值=清理基本閾值+清理縮放系數 *元組數
這里的清理基本閾值是autovacuum_vacuum_threshold,清理的縮放系數是autovacuum_vacuum_scale_factor,元組的數目可以從統計收集器里面獲取。這是一個部分精確的計數,由每次UPDATE和DELETE操作更新。
如果表上次被執行ANALYZE操作之后,其中過期元組的數量超過了“分析閾值”(analyze threshold),那么就分析該表更新表統計信息,分析閾值的定義與清理閾值相似,定義如下:
分析閾值=分析基本閾值+分析縮放系數 *元組數目
缺省的閾值和伸縮系數都是從postgresql.conf里面取得的。不過,我們可以以每個表獨立設置的方式覆蓋它,方法就是在系統表pg_autovacuum里輸入信息。pg_autovacuum表中一個元組可以用來記錄一個需要自動清理的表及其清理設置,AutoVacuum進程將使用其中的清理設置來清理該表。如果沒有特別設置該表的清理設置,AutoVacuum將使用全局設置。
1.AutoVacuum Launcher進程
通常,在入口函數StartAutoVacLauncher中執行fork操作創建Postmaster的子進程AutoVacuum Launcher,在新創建的子進程執行體中關閉從Postmaster進程中復制出的網絡連接端口,同時進入進程AutoVacuum Launcher的執行體函數AutoVacLauncherMain,其處理流程如圖2-12所示。
|
| 圖2-12 AutoVacuum Launcher處理流程 |
下面對其中幾個重要的步驟進行說明:
1)構建數據庫列表:調用函數rebuild_database_list完成,其步驟如下:
①建立一個Hash表,其中每一個元素代表一個數據庫,記錄了該數據庫的OID(adl_datid)、啟動worker的時間戳(adl_next_worker)以及一個評分值(adl_score)。初始時該Hash表中沒有元素。
②將pg_database平面文件(在PGDATA/global目錄下)中的數據庫構成一個鏈表,鏈表中的每一個節點代表一個數據庫,其中包括數據庫的OID、名稱、該數據庫的統計信息等。
③調用pgstat_fetch_stat_dbentry來填充每個節點的統計信息。
④對每一個統計信息不為空的數據庫,在Hash表中搜索該數據庫,如果沒有找到則將該數據庫加入到Hash表中,並且將該數據庫的adl_score設置為該數據庫被加入到Hash表中時的順序號。
⑤將Hash表中的數據庫按照adl_score值升序的順序依次加入到全局變量DatabaseList所指向的鏈表中,並設置每一個數據庫的adl_next_worker值。其中第一個數據庫的adl_next_worker值設為當前時間,之后的每一個數據庫的adl_next_worker的值都比前一個增加millis_increment。millis_increment的值由autovacuum_naptime參數值除以Hash表中數據庫的個數來設定。
為什么使用平面文件?
在PostgreSQL的數據集簇中,提供了兩個平面文件:pg_database和pg_auth。這兩個文件分別記錄了pg_database系統表和pg_authid系統表中的部分信息。如果有些還沒有啟動完畢的后台進程需要訪問這兩個系統表的內容,它們將會使用兩個平面文件來進行信息的獲取,這是由於進程還未完全啟動時是無法連接到數據庫並讀取相關系統表內容的。
在“構建數據庫列表”這一步驟由於並未連接到數據庫,因此只能用平面文件來替代系統表pg_database。
2)設置進程休眠時間:根據空閑Worker和數據庫列表來計算休眠的時間,當所有Worker進程都在運行時要設置一個較長的休眠時間。而當Worker進程退出時可以喚醒休眠,同時休眠也可以被其他信號中斷。
3)信號處理分支中got_SIGUSR1信號通知有Worker進程退出或者Postmaster通知有Worker啟動失敗。若是Postmaster通知Worker啟動失敗,則給Postmaster重新發送啟動Worker進程的消息。
4)啟動Worker進程:如果當前有一個Worker正在啟動中,則再休眠一會兒等待該Worker啟動完成。如果可以開始啟動一個新的Worker,則進行以下判斷:
①如果數據庫列表不為空,則檢查DatabaseList尾部數據庫的adl_next_worker參數,如果早於當前時間(表示該數據庫早就應該被處理)則啟動Worker進程。
②數據庫列表為空時,立即啟動Worker進程。
2.AutoVacuum Worker進程
AutoVacuum Worker進程的入口為launch_worker函數,在該入口處調用do_start_worker創建worker進程,並且返回連接數據庫的OID。如果返回的OID為有效的數據庫OID,則遍歷數據庫列表找到該OID對應的數據庫在數據庫列表中對應的節點,更新該節點的adl_next_worker域值,並將該節點移動到數據庫列表的頭部。如果遍歷數據庫列表沒有對應於該OID的節點,則調用rebuild_database_list重建數據庫列表。創建worker進程的函數體do_start_worker處理流程如圖2-13所示。
|
| 圖2-13 AutoVacuum Worker進程處理流程 |
AutoVacuum Worker進程的處理流程和AutoVacuum Launcher進程的處理流程基本類似,選擇要進行清理的數據庫的規則如前所述:選中數據庫后遍歷數據庫中的表,根據表的統計信息計算清理閾值和分析閾值,來確定是否要對表執行相應的操作。
在系統進行自動清理的同時,用戶可以使用安裝目錄bin文件夾下的vacuumdb或者vacuumlo工具對數據進行手動清理工作。vacuumdb工具清理數據庫並對數據庫執行分析操作,vacuumlo工具清理數據庫中無效的大對象。


