摘要:本文主要介紹如何進行正常的VACUUM FULL 維護,及時釋放磁盤存儲。
一、背景
目前根據某項目狀況,其DWS的磁盤IO性能低、庫內數據量大、對象多、數據膨脹嚴重。若毫無目的性的進行空間釋放,一方面對IO壓力很大,嚴重影響當前DWS任務運行,同時預計每次執行VACUUM FULL 時間已超過運行間隔,致使維護任務沒法開展;若依據髒頁率進行磁盤空間維護,每次髒頁統計花費1天之多且有極高几率出現異常,頻繁進行髒頁統計也必定程度上影響DWS運行。node
本文檔主要介紹如何進行正常的VACUUM FULL 維護,及時釋放磁盤存儲。sql
二、說明
2.1 VACUUM FULL介紹
VACUUM FULL一方面能夠及時回收空間,一方面能夠必定程度上提高數據庫性能。數據庫
VACUUM FULL回收表中已經刪除的行所佔據的存儲空間。在通常的數據庫操做裏,那些已經DELETE的行並無從它們所屬的表中物理刪除,所以有必要週期地運行VACUUM FULL,特別是在常常更新的表上。併發
2.2 VACUUM FULL使用建議
VACUUM FULL 對現有DWS任務運行具備必定影響。建議從如下幾個角度考慮:函數
系統表
針對系統表的操做比較危險,每每伴隨着阻塞DWS正常任務或連接接入。附錄的函數中已排除掉系統表的髒頁統計。性能
建議:根據系統表大小(參考附錄5.3章節),半年~一年時間進行統計,若發現膨脹狀況可協調窗口期作好業務暫停準備並進行釋放。這裏不作特別說明。spa
普通表
可單純根據髒頁率進行評估,決定是否須要進行釋放;或經過髒頁率+表大小配合方式評估,更有目的性進行釋放。3d
建議:code
一、首先建議肯定系統運行壓力較低的時間段,在該時間段內進行髒頁統計,並根據髒頁統計效果進行VACUUM FULL 維護操做。orm
二、其次建議根據系統數據更新頻度,選取1~2月進行一次髒頁統計。而後根據統計結果對這些表進行VACUUM FULL 操做。
三、最後建議獲取系統髒頁時配合表大小,規則自行擬定。如:髒頁率超過20%、表大小*髒頁率釋放空間達到20GB 等等。
四、補充建議依照函數說明(附錄5.1章節),對視圖數據進行固化(建立對應表)。這樣可避免二次篩選時耗時過長,只須要對錶進行篩選便可。
五、VACUUM FULL 操做建議根據系統壓力進行調整,壓力中等狀況下可以使用1~2個併發。無壓力狀況下可適當提高併發度。
索引
針對索引須要進行重建,這裏不作過多說明。附錄的函數中已排除掉索引統計。
2.3 新版髒頁率函數使用說明
一、建立函數及視圖
DWS中根據附錄腳本,建立funckang_get_dirty_tuples函數及v_get_dirty_tuples視圖。須要注意視圖中註釋部分,自行決定是否保留。
二、對結果進行二次分析
使用step3步驟,將視圖內容映射成物理表。而後對物理表進行規則篩選,參考2.2章節建議部分。
三、執行vacuum full
根據篩選出的schema名、table名 ,進行vacuum full 語句拼接,寫入SQL文件。
四、執行vacuum full
肯定時間時間段與併發度,經過 \parallel on ${number} 方式利用客戶端併發執行。
2.4 改進後髒頁統計方式比較
3 原有髒頁統計方式說明
1. 查詢 pgxc_get_stat_all_tables (viw1)
注:視圖能夠獲取髒頁率。但其中包括插入、更新刪除等許多統計信息,同時還須要與pg_namespace 關聯。
2. pgxc_stat_all_tables(func1) 函數
注:函數自身循環遍歷各個CN與DN上的信息,是個沒法下推函數。
3. pg_catalog.pg_stat_all_tables(view2)
注:試圖自身須要三個系統表關聯,統計了不少無用信息。
4 新版髒頁統計方式說明
一、 funckang_get_dirty_tuples
注:函數自身只遍歷DN上的表,同時去掉冗餘信息 。經過v_get_dirty_tuples 視圖計算表髒頁信息,提供髒頁率及表大小統計。
二、funckang_get_dirty_tuples_from_name
注:提供根據具體schemaname、tablename 方式返回具體的表的髒頁信息。
可根據提供的SQL進行查詢。
5 附錄
5.1 統計全庫表髒頁率
step1 :建立獲取髒頁的函數
CREATE OR REPLACE function public.funckang_get_dirty_tuples(out v_oid oid,out v_nspname text ,out v_relname text ,out v_livetup float8 ,out v_deadtup float8) returns setof record LANGUAGE plpgsql NOT FENCED NOT SHIPPABLE AS $function$ DECLARE /* -- ============================================================================= -- Program Name: 獲取數據髒頁率 -- Program ID: funckang_get_dirty_tuples -- Revision:1.0 -- Author: by kanghaifeng -- Create date: 2020/11/04 -- ============================================================================= */ row_data record; dn_name record; query_str text; query_str_nodes text; BEGIN --Get all the node names query_str_nodes := 'SELECT node_name FROM pgxc_node WHERE node_type = ''D'''; FOR dn_name IN EXECUTE(query_str_nodes) LOOP query_str := 'EXECUTE DIRECT ON (' || dn_name.node_name || ') ''SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_live_tuples(c.oid) AS n_live_tup,pg_stat_get_dead_tuples(c.oid) AS n_dead_tup FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname not in (''''pg_catalog'''',''''information_schema'''',''''sys'''',''''cstore'''',''''pmk'''') and n.nspname not like ''''pg_temp%'''' AND n.nspname !~ ''''^pg_toast'''' and c.relkind=''''r'''' GROUP BY c.oid, n.nspname, c.relname'' '; FOR row_data IN EXECUTE(query_str) LOOP --insert into kang_tup values(row_data.relid,row_data.schemaname,row_data.relname,row_data.n_live_tup,row_data.n_dead_tup); v_oid :=row_data.relid; v_nspname:=row_data.schemaname; v_relname:=row_data.relname; v_livetup:=row_data.n_live_tup; v_deadtup:=row_data.n_dead_tup; return next ; END LOOP; END LOOP; return; END; $function$ /
step2: 建立獲取髒頁信息的視圖,註釋部分爲表大小信息,可根據須要決定是否須要。
drop view if exists public.v_get_dirty_tuples; create view public.v_get_dirty_tuples as SELECT funckang_get_dirty_tuples.nspname, funckang_get_dirty_tuples.relname, -- pg_table_size(funckang_get_dirty_tuples.nspname||'.'||funckang_get_dirty_tuples.relname), sum(funckang_get_dirty_tuples.n_live_tup) AS n_live_tup, sum(funckang_get_dirty_tuples.n_dead_tup) AS n_dead_tup, (sum(funckang_get_dirty_tuples.n_dead_tup) / sum((funckang_get_dirty_tuples.n_dead_tup + funckang_get_dirty_tuples.n_live_tup)::numeric + .0001) * 100::numeric)::numeric(5,2) AS dirty_page_rate FROM public.funckang_get_dirty_tuples() funckang_get_dirty_tuples(oid,nspname,relname,n_live_tup,n_dead_tup) GROUP BY funckang_get_dirty_tuples.nspname,funckang_get_dirty_tuples.relname;
step3: 因視圖查詢耗時,建議建立一個表將視圖內容固話下來作進一步分析。
create table public.zangye as select * from public.v_get_dirty_tuples;
5.2 根據給定表返回髒頁率
step1 :建立獲取髒頁的函數
CREATE OR REPLACE function public.funckang_get_dirty_tuples_from_name(in out schemaname text,in out tablename text ,out v_livetup float8 ,out v_deadtup float8) returns setof record LANGUAGE plpgsql NOT FENCED NOT SHIPPABLE AS $function$ DECLARE /* -- ============================================================================= -- Program Name: 根據schemaname,tablename獲取數據髒頁率 -- Program ID: funckang_get_dirty_tuples_from_name -- Revision:1.0 -- Author: by kanghaifeng -- Create date: 2020/11/04 -- ============================================================================= */ row_data record; dn_name record; query_str text; query_str_nodes text; BEGIN --Get all the node names query_str_nodes := 'SELECT node_name FROM pgxc_node WHERE node_type = ''D'''; FOR dn_name IN EXECUTE(query_str_nodes) LOOP query_str := 'EXECUTE DIRECT ON (' || dn_name.node_name || ') ''SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_live_tuples(c.oid) AS n_live_tup,pg_stat_get_dead_tuples(c.oid) AS n_dead_tup FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname not in (''''pg_catalog'''',''''information_schema'''',''''sys'''',''''cstore'''',''''pmk'''') and n.nspname not like ''''pg_temp%'''' AND n.nspname !~ ''''^pg_toast'''' and c.relkind=''''r'''' and n.nspname='''''||schemaname||''''' and c.relname='''''||tablename||''''' GROUP BY c.oid, n.nspname, c.relname'' '; DBMS_OUTPUT.PUT_LINE(query_str); FOR row_data IN EXECUTE(query_str) LOOP --insert into kang_tup values(row_data.relid,row_data.schemaname,row_data.relname,row_data.n_live_tup,row_data.n_dead_tup); --v_oid :=row_data.relid; schemaname:=row_data.schemaname; tablename:=row_data.relname; v_livetup:=row_data.n_live_tup; v_deadtup:=row_data.n_dead_tup; return next ; END LOOP; END LOOP; return; END; $function$ /
step2 :查詢給出表的髒頁信息。下面爲dbadmin.hedi2 示例。註釋部分爲大小信息,可根據須要決定是否使用
SELECT funckang_get_dirty_tuples_from_name.schemaname, funckang_get_dirty_tuples_from_name.tablename, -- pg_table_size(funckang_get_dirty_tuples_from_name.schemaname||'.'||funckang_get_dirty_tuples_from_name.tablename), sum(funckang_get_dirty_tuples_from_name.n_live_tup) AS n_live_tup, sum(funckang_get_dirty_tuples_from_name.n_dead_tup) AS n_dead_tup, (sum(funckang_get_dirty_tuples_from_name.n_dead_tup) / sum((funckang_get_dirty_tuples_from_name.n_dead_tup + funckang_get_dirty_tuples_from_name.n_live_tup)::numeric + .0001) * 100::numeric)::numeric(5,2) AS dirty_page_rate FROM public.funckang_get_dirty_tuples_from_name('dbadmin','hedi2') funckang_get_dirty_tuples_from_name(schemaname,tablename,n_live_tup,n_dead_tup) GROUP BY funckang_get_dirty_tuples_from_name.schemaname,funckang_get_dirty_tuples_from_name.tablename;
5.3 系統表大小統計
select pt.schemaname ,pt.tablename ,getdistributekey(pt.schemaname||'."'||pt.tablename||'"') as distribute_key ,pg_size_pretty(pg_relation_size(pt.schemaname||'."'||pt.tablename||'"')) as tablesize ,case when pt.hasindexes = 't' then pg_size_pretty(pg_indexes_size(pt.schemaname||'."'||pt.tablename||'"')) else '' end as indexsize ,pc.reloptions ,pg_stat_get_last_analyze_time(pc.oid) as lastanalyze ,pg_stat_get_last_vacuum_time(pc.oid) as lastvacuum ,pc.parttype from pg_tables pt ,pg_class pc where (pt.schemaname||'."'||pt.tablename||'"')::regclass::oid=pc.oid and pt.schemaname not in ('mppdbpermission','information_schema','cstore','pg_catalog','pmk') order by pg_relation_size((pt.schemaname||'."'||pt.tablename||'"')) desc;
本文分享自華爲雲社區《關於DWS 空間釋放(vacuum full) 最佳實踐》,原文做者: 獨孤求敗馬? 。