pg_squeeze(表空间整理)

更新时间:
复制为 MD 格式

当表因频繁的UPDATEDELETE操作产生大量碎片(即表膨胀)时,会导致存储空间浪费和查询性能下降。pg_squeeze插件通过在线重组表的方式自动回收这些膨胀空间。它基于逻辑解码捕获数据变更,对业务写入性能影响很小,且完全在服务端自动运行,帮助您在不中断业务的前提下,持续优化表结构,提升数据库性能与空间利用率。

适用范围

支持的PolarDB PostgreSQL的版本如下:

  • PostgreSQL 18(内核小版本2.0.18.1.2.0及以上)

  • PostgreSQL 17(内核小版本2.0.17.7.5.0及以上)

  • PostgreSQL 16(内核小版本2.0.16.10.12.0及以上)

  • PostgreSQL 15(内核小版本2.0.15.15.7.0及以上)

  • PostgreSQL 14(内核小版本2.0.14.19.40.0及以上)

说明

您可在控制台查看内核小版本号,也可以通过SHOW polardb_version;语句查看。如未满足内核小版本要求,请升级内核小版本

pg_repack对比

pg_squeezepg_repack功能相似,都是用于处理表膨胀问题的常用扩展。pg_squeeze作为pg_repack的替代方案,在实现原理和运维方式上存在差异,可根据业务场景选择。

对比项

pg_squeeze

pg_repack

实现原理

逻辑解码(Replication Slot):通过复制槽异步捕获WAL中的数据变更。

触发器(Trigger):在源表上创建触发器,同步捕获DML操作。

对业务写入影响

较小。变更捕获是异步过程,对源表写入性能影响有限。

较大。每次DML操作都会额外触发一次写入,在高并发下可能成为性能瓶颈。

资源消耗

消耗一个复制槽。若压缩任务处理缓慢或失败,可能导致WAL日志堆积,占用存储空间。

触发器在高并发下会增加CPU开销。需要额外的日志表记录变更。

运维复杂度

需关注复制槽状态和WAL存储空间,防止因任务异常导致磁盘写满。

需关注触发器本身的管理,确保其在表结构变更或数据迁移后仍能正常工作。

快速入门

本节将引导您完成从安装配置到成功启用一个表的自动压缩任务的全过程。

步骤一:配置集群参数

  1. 登录PolarDB控制台,在集群列表页面找到目标集群,单击集群ID进入详情页。

  2. 在左侧导航栏选择配置与管理 > 参数配置

  3. 参数配置页面,修改以下参数:

    • wal_level:设置为logical。这是使用逻辑解码功能的前提,会适度增加WAL日志的生成量。

    • max_replication_slots:确保有足够余量。pg_squeeze在每个启用的数据库中至少需要1 + squeeze.workers_per_database个复制槽。建议设置为比当前值至少大2的值(例如,从10调整到12)。

    • shared_preload_libraries:在现有值的基础上追加,pg_squeeze。例如,如果原值为pg_stat_statements,则修改为pg_stat_statements,pg_squeeze

  4. 等待集群重启后参数配置生效。

步骤二:安装插件并启动后台进程

在需要进行表空间整理的目标数据库中启用pg_squeeze功能。

  1. 登录至目标数据库。

  2. 执行以下SQL语句,创建插件并启动后台调度工作进程(Scheduler Worker)。该进程会根据squeeze.tables中的配置,定时检查并创建压缩任务。

    -- 在目标数据库中创建扩展
    CREATE EXTENSION pg_squeeze;
    
    -- 启动后台调度进程
    -- 此操作只需在每个需要使用pg_squeeze的数据库中执行一次
    SELECT squeeze.start_worker();
  3. (可选)执行以下SQL语句,验证后台进程是否成功启动。如果看到application_namesqueeze scheduler的进程,则表示启动成功。

    SELECT * FROM pg_stat_activity WHERE application_name LIKE 'squeeze%';

步骤三:配置待处理的表

告知pg_squeeze需要管理哪些表,以及按何种策略进行检查和压缩。

  1. 确保表具备REPLICA IDENTITY(副本标识)pg_squeeze需要通过唯一标识来匹配新旧表中的行变更。通常,主键(Primary Key)会自动作为REPLICA IDENTITY

    • 检查:使用\d+ your_table_name命令查看表的Replica identity属性是否为DEFAULTFULL

    • 设置:如果表没有主键,但有一个唯一索引(例如your_table_uidx),可执行以下命令将其设置为REPLICA IDENTITY

      ALTER TABLE your_table_name REPLICA IDENTITY USING INDEX your_table_uidx;
  2. 将表注册到squeeze.tables将表信息及调度策略插入squeeze.tables配置表中。schedule字段用于定义检查时间,其格式类似于Cron表达式。

    -- 示例1:每天凌晨2:30检查public.foo表
    INSERT INTO squeeze.tables (tabschema, tabname, schedule)
    VALUES ('public', 'foo', ROW(ARRAY[30], ARRAY[2], NULL, NULL, NULL)::squeeze.schedule);
    
    -- 示例2:每周日凌晨4:00检查public.bar表,并指定压缩后按主键排序
    INSERT INTO squeeze.tables (tabschema, tabname, schedule, clustering_index)
    VALUES ('public', 'bar', ROW(ARRAY[0], ARRAY[4], NULL, NULL, ARRAY[0])::squeeze.schedule, 'bar_pkey');

    schedule字段的结构为(分钟, 小时, 日, 月, 星期)NULL表示任意时间。更多配置请参见附录:squeeze.tables配置表详解

步骤四:监控与验证

确认压缩任务是否按预期执行、成功或失败。您可以通过查询pg_squeeze提供的视图和表来监控任务状态。

-- 查看当前数据库中正在进行的压缩任务
SELECT * FROM squeeze.get_active_workers();

-- 查看最近10条成功的压缩历史记录
SELECT * FROM squeeze.log ORDER BY finished DESC LIMIT 10;

-- 查看失败的压缩记录及原因
SELECT * FROM squeeze.errors;

步骤五:(可选)取消定制压缩

如果特定表不再需要定期压缩,只需从squeeze.tables表中删除相应的行即可。

DELETE FROM squeeze.tables WHERE tabschema = 'public' AND tabname = 'foo';

手动触发即时压缩

除了按计划自动执行,您也可以手动触发对任意表的即时压缩,而无需将其注册到squeeze.tables中。对特定表进行一次性的、立即执行的压缩操作。

使用squeeze.squeeze_table()函数发起一个手动压缩任务。

-- 对 public.pgbench_accounts 表发起一次默认的压缩任务
SELECT squeeze.squeeze_table('public', 'pgbench_accounts');

-- 发起压缩任务,并指定压缩后按主键(pgbench_accounts_pkey)进行物理排序
SELECT squeeze.squeeze_table('public', 'pgbench_accounts', 'pgbench_accounts_pkey');
说明

squeeze.squeeze_table函数不是事务性的。它仅负责启动一个后台工作进程来执行压缩,然后立即返回。回滚调用该函数的事务,并不会中止已经启动的后台压缩任务。

配置高级选项

调整并发与锁行为

在不影响业务的前提下,更精细地控制压缩过程。通过设置squeeze.max_xlock_time参数,可以限制压缩最后阶段排他锁的最长持有时间,避免长时间阻塞业务。

-- 设置排他锁最长持有时间为100毫秒
SET squeeze.max_xlock_time TO 100;

如果锁持有时间超过此限制,pg_squeeze会释放锁,处理在此期间发生的数据变更,然后再次尝试获取锁。若多次尝试均超时,任务将报错中止。如果频繁发生锁超时失败,应优先考虑将任务调度到业务负载更低的时间窗口,其次再考虑适当增大此参数值。

配置并发工作进程数

当需要同时处理的表较多时,通过增加工作进程数来提高压缩效率。您可以将squeeze.workers_per_database参数设置为大于1的值,以允许在单个数据库中并行处理多个表的压缩任务。

-- 允许每个数据库最多同时运行2个压缩工作进程
SET squeeze.workers_per_database = 2;
说明

此设置会影响所有正在使用pg_squeeze插件的数据库。集群中所有Workers(包括Scheduler Worker)的总数不能超过参数max_worker_processes的数值。

附录:squeeze.tables配置表详解

squeeze.tablespg_squeeze的核心配置表,您通过向此表插入记录来注册需要管理的表并定义其处理策略。

字段

类型

描述

默认值

配置建议

tabschema

name

表所在的模式名。

-

-

tabname

name

表名。

-

-

schedule

squeeze.schedule

任务检查调度时间,格式为(分钟, 小时, 日, 月, 星期)

-

根据业务低峰期设置。例如,每天凌晨2:30执行:ROW(ARRAY[30], ARRAY[2], NULL, NULL, NULL)::squeeze.schedule

free_space_extra

integer

触发压缩所需的额外膨胀空间百分比(超出fillfactor预留空间之外的部分)。

50

对于写入频繁的OLTP表,可适当调低此值(如30),以更频繁、小批量地回收空间。

min_size

integer

表占用的最小存储空间(MB),小于此值的表不会被处理。

8

保持默认值即可,避免对小表进行不必要的操作。

vacuum_max_age

interval

自上次VACUUM后,空闲空间映射(FSM)被认为有效的最长时间。

'1 hour'

保持默认值。若超过此时间,pg_squeeze会进行更深入的分析来判断是否需要压缩。

max_retry

integer

当首次压缩失败时,允许的最大重试次数。

0

如果表经常因DDL变更等原因导致压缩失败,可适当增加此值(如2),以提高任务成功率。

clustering_index

name

指定一个现有索引,压缩完成后表内数据将按此索引的键值进行物理排序,效果类似CLUSTER命令。

NULL

对于范围查询频繁的表,建议将其主键或核心查询索引作为分片键,可显著提升查询性能。

rel_tablespace

name

指定压缩后表要迁移到的目标表空间。

NULL

用于表空间迁移场景。若为NULL,表将保留在原表空间。

ind_tablespaces

name[]

二维数组,用于指定索引的表空间映射。

NULL

用于索引的表空间迁移。

skip_analyze

boolean

是否在压缩完成后跳过ANALYZE命令。

false

建议保持false。压缩会改变表的数据分布,执行ANALYZE有助于优化器生成准确的执行计划。