表中的数据被删除或更新后(UPDATE/DELETE),物理存储层面并不会直接删除数据,而是标记这些数据不可见,所以会在数据页中留下很多“空洞”,在读取数据时,这些“空洞”会随数据页一起加载,拖慢数据扫描速度,需要定期回收删除的空间。
空间回收方法
使用 VACUUM
命令,可以对表进行重新整理,回收空间,以便获取更好的数据读取性能。VACUUM 命令如下:
VACUUM [FULL] [FREEZE] [VERBOSE] [table];
VACUUM 会在页内进行整理,VACUUM FULL 会跨数据页移动数据。 VACUUM 执行速度更快, VACUUM FULL 执行地更彻底,但会请求排他锁。建议定期对系统表进行 VACUUM(每周一次)。
注:执行 VACUUM 命令后,由于系统采集信息需要一定时间,您需要在执行命令后稍微等待一段时间才能看到回收的存储空间。
使用建议
什么情况下做 VACUUM?
- 不锁表回收空间,只能回收部分空间。
- 频率:对于有较多实时更新的表,每天做一次。
- 如果更新是每天一次批量进行的,可以在每天批量更新后做一次。
- 对系统影响:不会锁表,表可以正常读写。会导致 CPU、I/O 使用率增加,可能影响查询的性能。
什么情况下做 VACUUM FULL?
- 锁表,通过重建表,回收所有空洞空间。对做了大量更新后的表,建议尽快执行 VACUUM FULL。
- 频率:至少每周执行一次。如果每天会更新几乎所有数据,需要每天做一次。
- 对系统影响:会对正在进行 VACUUM FULL 的表锁定,无法读写。会导致 CPU、I/O 使用率增加。建议在维护窗口进行操作。
查询需要执行 VACUUM 的表
AnalyticDB PostgreSQL 提供了一个 gp_bloat_diag 视图,统计当前页数和实际需要页数的比例。通过 analyze table
来收集统计信息之后,查看该视图。
gpadmin=# SELECT * FROM gp_toolkit.gp_bloat_diag;
其结果只包括发生了中度或者显著膨胀的表。当实际页面数和预期页面的比率超过4但小于10时,就会报告为中度膨胀。当该比率超过10时就会报告显著膨胀。对于这些表,可以考虑进行 VACUUM FULL 来回收空间。
VACUUM FREEZE 的使用
AnalyticDB PostgreSQL 执行的所有事务都有唯一的事务 ID(XID),XID 是单调递增的,上限是20亿。
随着数据库执行事务的增多,为防止 XID 超过极限,在 XID 接近 xid_stop_limit-xid_warn_limit(默认500000000)时,AnalyticDB PostgreSQL 会对执行事务的 sql 返回 warning 信息,提醒用户:
WARNING: database "database_name" must be vacuumed within number_of_transactions transactions
用户可通过手动执行 VACUUM FREEZE 当前 database 来缩小 XID。
如果忽略这个 warning 信息,在 XID 继续增长到超过xid_stop_limit(默认1000000000)时,AnalyticDB PostgreSQL会拒绝新的事务执行,并返回报错信息:
FATAL: database is not accepting commands to avoid wraparound data loss in database "database_name"
维护定期回收空间任务
当系统有更新操作(包括 INSERT VALUES、UPDATE、DELETE、ALTER TABLE ADD COLUMN 等),会在系统表和被更新的数据表中留存不再使用的垃圾数据,造成系统性能下降,并占用大量磁盘空间,因此需要定期进行垃圾回收。
AUTO VACUUM
AUTO VACUUM 可以自动执行 VACUUM 命令。AUTO VACUUM 将检查具有大量插入、更新或删除的表,并在需要的时候主动对表执行 VACUUM 来回收表中的垃圾数据,提升查询速度。默认情况下, 当表被删除行数超过一半时,AUTO VACUUM 会对表执行 VACUUM 操作来清除垃圾数据。
对于 MULTI MASTER 实例,当前暂时只能追踪主 MASTER 上发生的改动行为,辅助 MASTER 发生的改动行为将不会触发 AUTO VACUUM。
不锁表回收垃圾
在不锁表的情况下,可以回收部分垃圾。具体方式如下:
- 命令:连接每个数据库,以数据库的所有者身份登录,执行
VACUUM
命令。 - 频率:如果有大批量实时更新的情况(即不断执行INSERT VALUES、UPDATE、DELETE等操作),建议每天执行一次,或每周至少一次;如果更新是每天一次批量进行的,建议每周执行一次,或不要超过一个月执行一次。
- 对系统影响:不会锁表,表可以正常读写。会导致 CPU、I/O 使用率增加,可能影响查询的性能。
维护窗口回收垃圾
在业务暂停的维护窗口,可以回收所有垃圾。具体方式如下:
命令:连接每个数据库,以数据库的所有者身份登录(需要对所有操作对象有所有者权限)。
- 对每张数据表,执行
VACUUM FULL <table name>
,对列存表还需要执行REINDEX TABLE <table name>
- 对于系统表(包括 pg_class, pg_attribute, pg_index 等),当有频繁建删表,建删索引等操作时,也建议执行
VACUUM FULL <table name>
进行定期维护。注意:该操作需要业务停止访问数据库。 - 频率:至少每周执行一次。如果每天会更新几乎所有数据,需要每天做一次。
- 对系统影响:会对正在进行 VACUUM FULL 或 REINDEX 的表进行锁定,无法读写。会导致 CPU、I/O 使用率增加
创建资源
export db_ip="ip_address" export db_name="dbname" export db_port="3432" export adb_username="username" export adb_password=password # adb连接字符串 export pub_adb_params_ConStr="host=$db_ip port=$db_port dbname=$db_name user=$adb_username password=$adb_password"
创建 Shell 脚本
#!/bin/bash # Step1: 读取连接字符串 source ./pub_adb_params.sh # Step2: 获取数据库列表 dblist=`psql "$pub_adb_params_ConStr" -c "copy (select datname from pg_stat_database where datname='cjdmdb') to stdout"` # echo "$dblist" for db in $dblist ; do # skip system databases if [[ $db == template0 ]] || [[ $db == template1 ]] || [[ $db == postgres ]] || [[ $db == gpdb ]] || [[ $db == cjdm_adb_ower ]] ; then continue fi echo processing db "$db" tblist=`psql "$pub_adb_params_ConStr" -c "copy ( select table_name from ( select table_name, pg_table_size(table_name) as table_size, pg_indexes_size(table_name) as indexes_size, pg_total_relation_size(table_name) as total_size from ( select (table_schema || '.' || table_name) as table_name from information_schema.tables ) as all_tables order by total_size desc ) as pretty_sizes where table_name not like 'cjdm_invest.stg%' and table_name not like 'cjdm_invest.temp%' and table_name not like 'cjdm_invest.test%' and table_name not like 'cjdm_invest.v_%' and table_name like '%cjdm_invest%' and ( total_size > 100 * 1024 * 1024 or indexes_size > 100 * 1024 * 1024 ) ) to stdout"` # echo "$tblist" for tb in $tblist ; do echo processing table "$tb" # vacuum full this table, which will lock the table vacuum="vacuum full $tb" eval "psql \"$pub_adb_params_ConStr\" -c \"$vacuum\"" # reindex the table to reclaim index space reindex="reindex table $tb" eval "psql \"$pub_adb_params_ConStr\" -c \"$reindex\"" analyze="analyze $tb" eval "psql \"$pub_adb_params_ConStr\" -c \"$analyze\"" done done
参考资料
原创文章,转载请注明出处:http://www.opcoder.cn/article/64/