摘要:本文次要介绍如何进行失常的VACUUM FULL 保护,及时开释磁盘存储。

1、背景

目前依据某我的项目状况,其DWS的磁盘IO性能低、库内数据量大、对象多、数据收缩重大。若毫无目的性的进行空间开释,一方面对IO压力很大,重大影响以后DWS工作运行,同时预计每次执行VACUUM FULL 工夫已超过运行距离,导致保护工作无奈发展;若根据脏页率进行磁盘空间保护,每次脏页统计破费1天之多且有极高概率出现异常,频繁进行脏页统计也肯定水平上影响DWS运行。

本文档次要介绍如何进行失常的VACUUM FULL 保护,及时开释磁盘存储。

2、阐明

2.1 VACUUM FULL介绍

VACUUM FULL一方面能够及时回收空间,一方面能够肯定水平上晋升数据库性能。

VACUUM FULL回收表中曾经删除的行所占据的存储空间。在个别的数据库操作里,那些曾经DELETE的行并没有从它们所属的表中物理删除,因而有必要周期地运行VACUUM FULL,特地是在常常更新的表上。

2.2 VACUUM FULL应用倡议

VACUUM FULL 对现有DWS工作运行具备肯定影响。倡议从以下几个角度思考:

零碎表

针对零碎表的操作比拟危险,往往随同着阻塞DWS失常工作或链接接入。附录的函数中已排除掉零碎表的脏页统计。

倡议:依据零碎表大小(参考附录5.3章节),半年~一年工夫进行统计,若发现收缩状况可协调窗口期做好业务暂停筹备并进行开释。这里不做特地阐明。

一般表

可单纯依据脏页率进行评估,决定是否须要进行开释;或通过脏页率+表大小配合形式评估,更有目的性进行开释。

倡议:

1、首先倡议确定零碎运行压力较低的时间段,在该时间段内进行脏页统计,并依据脏页统计成果进行VACUUM FULL 保护操作。

2、其次倡议依据零碎数据更新频度,选取1~2月进行一次脏页统计。而后依据统计后果对这些表进行VACUUM FULL 操作。

3、最初倡议获取零碎脏页时配合表大小,规定自行拟定。如:脏页率超过20%、表大小*脏页率开释空间达到20GB 等等。

4、补充倡议按照函数阐明(附录5.1章节),对视图数据进行固化(创立对应表)。这样可防止二次筛选时耗时过长,只须要对表进行筛选即可。

5、VACUUM FULL 操作倡议依据零碎压力进行调整,压力中等状况下可应用1~2个并发。无压力状况下可适当晋升并发度。

索引

针对索引须要进行重建,这里不做过多阐明。附录的函数中已排除掉索引统计。

2.3 新版脏页率函数应用阐明

1、创立函数及视图

DWS中依据附录脚本,创立funckang_get_dirty_tuples函数及v_get_dirty_tuples视图。须要留神视图中正文局部,自行决定是否保留。

2、对后果进行二次剖析

应用step3步骤,将视图内容映射成物理表。而后对物理表进行规定筛选,参考2.2章节倡议局部。

3、执行vacuum full

依据筛选出的schema名、table名 ,进行vacuum full 语句拼接,写入SQL文件。

4、执行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 新版脏页统计形式阐明

1、 funckang_get_dirty_tuples

注:函数本身只遍历DN上的表,同时去掉冗余信息 。通过v_get_dirty_tuples 视图计算表脏页信息,提供脏页率及表大小统计。

2、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) 最佳实际》,原文作者: 独孤求败马? 。

点击关注,第一工夫理解华为云陈腐技术~