表膨胀的处理
如果只是测试数据库,或者业务允许每天有很长的停机维护时间,那么简单地在数据库中执行VACUUM FULL
就可以了。但VACUUM FULL
需要表上的排它读写锁,但对于需要不间断运行的数据库,我们就需要用到pg_repack
来处理表的膨胀。
要求
PostgreSQL 版本
PostgreSQL 9.4、9.5、9.6、10、11、12、13、14、15
磁盘
执行全表重新打包需要大约两倍于目标表及其索引的可用磁盘空间。例如,如果要重组的表和索引的总大小为 1GB,则需要额外的 2GB 磁盘空间。
pg_repack安装
虽然pg_repack 可以使用 yum 进行安装,但我不推荐这种安装方式。因为不知道版本与 postgresql 版本之间的兼容性。我们老老实实去 github 下载就行了。https://github.com/reorg/pg_repack
export PGHOME=/opt/pgsql13.2
export PATH=$HOME/bin:$PGHOME/bin:$PATH
cd pg_repack-1.4.8/
make
make install
安装后,在你要处理的数据库中加载pg_repack
扩展。pg_repack
被打包为一个扩展,所以你可以执行:
CREATE EXTENSION pg_repack
使用pg_repack
的注意事项
目标表必须有一个
PRIMARY KEY
,或者至少有一个NOT NULL
列上的UNIQUE
索引。重整开始之前,最好取消掉所有正在进行的Vacuum任务。
对索引做重整之前,最好能手动清理掉可能正在使用该索引的查询。
如果出现异常的情况(警如中途强制退出),有可能会留下未清理的垃圾,需要手工清理。可能包括:
临时表与临时索引建立在与原表/索引同一个schema内
临时表的名称为:
${schema_name}.table_${table_oid}
临时索引的名称为:
${schema_name}.index_${table_oid}}
原始表上可能会残留相关的触发器,需要手动清理。
当完成重整,进行重命名替换时,会产生巨量的WAL,有可能会导致复制延迟,而且无法取消。
重整特别大的表时,需要预留至少与该表及其索引相同大小的磁盘空间,需要特别小心,手动检查。
如果遇到写入速度非常快的,最后阶段replylog的时候,只能等。
数据库的插件与服务端的版本要保持一致。
默认是使用套接字,如果没有配置套接字就要使用 TCP(IP 和 port 写上) 进行连接。
pg_repack
的原理
pg_repack
的原理相当简单,它会为待重建的表创建一份副本。首先取一份全量快照,将所有活元组写入新表,并通过触发器将所有针对原表的变更同步至新表,最后通过重命名,使用新的紧实副本替换老表。而对于索引,则是通过PostgreSQL的CREATE(DROP) INDEX CONCURRENTLY
完成的。
重整表
创建一张与原表模式相同,但不带索引的空表。
创建一张与原始表对应的日志表,用于记录
pg_repack
工作期间该表上发生的变更。为原始表添加一个行触发器,在相应日志表中记录所有
INSERT
,DELETE
,UPDATE
操作。将老表中的数据复制到新的空表中。
在新表上创建同样的索引
将日志表中的增量变更应用到新表上
通过重命名的方式切换新旧表
将旧的,已经被重命名掉的表
DROP
掉。
重整索引
使用
CREATE INDEX CONCURRENTLY
在原表上创建新索引,保持与旧索引相同的定义。Analyze
新索引,并将旧索引设置为无效,在数据目录中将新旧索引交换。删除旧索引。
使用参数详解
pg_repack [选项] ... [数据库名称]
可以在OPTIONS中指定以下选项。
选项:
-a, --all 重新打包所有数据库
-t, --table=TABLE 仅重新打包特定表
-I, --parent-table=TABLE 重新打包特定的父表及其继承者
-c, --schema=SCHEMA 仅在特定模式中重新打包表
-s, --tablespace=TBLSPC 将重新打包的表移动到新的表空间
-S, --moveidx 将重新打包的索引也移动到TBLSPC
-o, --order-by=COLUMNS 按列而不是簇键排序
-n, --no-order 做 vacuum full 而不是 cluster
-N, --dry-run 打印那些表支持
-j, --jobs=NUM 为每个表使用这么多并行作业
-i, --index=INDEX 只移动指定的索引
-x, --only-indexes 只移动指定表的索引
-T, --wait-timeout=SECS 超时以取消冲突的其他后端
-D, --no-kill-backend 超时时不要杀死其他后端
-Z, --no-analyze 最后不分析
-k, --no-superuser-check 跳过客户端中的超级用户检查
-C, --exclude-extension 不要重新打包属于特定扩展名的表
连接选项:
-d, --dbname=DBNAME 要连接的数据库
-h, --host=HOSTNAME 数据库服务器主机或套接字目录
-p, --port=PORT 数据库服务器端口
-U, --username=USERNAME 连接的用户名
-w, --no-password 从不提示输入密码
-W, --password 强制密码提示
通用选项:
-e, --echo 回显查询
-E, --elevel=LEVEL 设置输出消息级别
–help 显示此帮助,然后退出
–version 输出版本信息,然后退出
使用案例
# 完全清理整个数据库,开5个并发任务,超时等待10秒
pg_repack -d <database> -j 5 -T 10
# 清理mydb中一张特定的表mytable,超时等待10秒
pg_repack mydb -t public.mytable -T 10
# 清理某个特定的索引 myschema.myindex,注意必须使用带模式的全名
pg_repack mydb -i myschema.myindex
# 俺一般喜欢补全
./pg_repack --dbname tpch --dry-run --username postgres --host 192.168.31.170 --port 5432 --password -j 2
参考链接https://pigsty.cc/blog/admin/bloat/#%E8%86%A8%E8%83%80%E7%8E%87%E7%9A%84%E4%BC%B0%E7%AE%97
评论