
当再终端开启2个会话,其中会话1 执行
begin;
update t set ....;
模拟线上有写操作,但是没有完成
会话2 执行
SELECT squeeze.squeeze_table('public', 'test', null, null, null);
发现会话2 ,会因为锁等待而卡住,请问都说线上环境用这个工具不影响业务,请问这个咋理解呢?

在PostgreSQL中,pg_squeeze
是一个用于在线表重组的扩展工具,它可以有效减少表膨胀(bloat),从而提高性能。然而,当表有频繁的更新、删除或插入操作时,pg_squeeze.squeeze_table
可能会被阻塞或卡住,主要原因是它需要获取某些锁(如 ACCESS EXCLUSIVE
锁)来完成表的重组工作。
在实际的生产环境中,为了安全高效地使用 pg_squeeze
,需要采取以下策略来避免或最小化对业务的影响:
1. 使用 pg_squeeze
的基础原理
pg_squeeze
的工作机制通常分为以下几个阶段:
- 创建一个新的临时表。
- 将源表的数据逐行复制到临时表中(考虑并发事务)。
- 替换原始表的物理存储。
- 丢弃临时表。
其中,在某些阶段需要短暂获取较高级别的锁(如 ACCESS EXCLUSIVE
),这可能导致阻塞或等待。
2. 问题分析
当表有频繁的更新操作时,以下问题可能会导致 pg_squeeze.squeeze_table
被卡住:
- 锁冲突:
pg_squeeze
在进行表替换时需要获取ACCESS EXCLUSIVE
锁,而此锁会阻塞其他操作(如查询、更新、删除等)。如果表上有活跃事务,这个锁可能会一直等待。 - 长事务影响:如果有长时间运行的事务占用资源,会导致
pg_squeeze
无法获取所需锁。 - 表数据量大:对于非常大的表,
pg_squeeze
的数据复制阶段可能会耗时较长,从而增加锁定和冲突的可能性。
3. 线上环境的最佳实践
为了在生产环境中安全有效地使用 pg_squeeze
,可以采取以下措施:
3.1 降低锁冲突风险
-
使用
squeeze_freeze
参数:
设置squeeze_freeze
为TRUE
时,pg_squeeze
会尝试冻结表的元数据以避免长时间占用锁。SELECT squeeze_table('schema_name', 'table_name', squeeze_freeze := TRUE);
复制 -
限制锁等待时间:
通过设置lock_timeout
参数,限制ACCESS EXCLUSIVE
锁的等待时间。如果超时未能获取锁,pg_squeeze
将取消操作。SET lock_timeout = '10s'; SELECT squeeze_table('schema_name', 'table_name');
复制 -
使用
pg_try_advisory_lock
:
在运行之前检查是否可以安全地获取锁,避免直接将pg_squeeze
卡住。
3.2 避免高峰期执行
-
选择业务低峰期:
在业务访问量较低的时间段(如深夜)运行pg_squeeze
,以最小化对正常业务的影响。 -
分批处理:
对于数据量较大的表,可以按分区(如果有)或逐步迁移的方式进行重组,而不是一次性处理整个表。
3.3 配置合适的参数
-
调整
pg_squeeze
参数:
配置合适的参数以减少对业务的影响。例如:squeeze_triggered_by_table_bloat
: 触发表膨胀的阈值。squeeze_max_retry_count
: 遇到锁冲突时的最大重试次数。squeeze_vacuum_cost_delay
: 控制 VACUUM 操作的延迟,避免过多资源占用。
-
加速复制过程:
使用parallel_workers
参数(如果可用)来加快数据复制阶段,减少所需时间。SELECT squeeze_table('schema_name', 'table_name', parallel_workers := 4);
复制
3.4 监控与诊断
-
监控锁信息:
在执行pg_squeeze
的过程中,实时监控锁的状态,确保没有长时间的锁等待。SELECT * FROM pg_locks WHERE relation::regclass = 'schema_name.table_name'::regclass;
复制 -
查看活动事务:
检查当前的活跃事务,避免长时间事务阻塞pg_squeeze
。SELECT * FROM pg_stat_activity WHERE state = 'active';
复制 -
日志分析:
打开pg_squeeze
的日志记录功能,分析问题根源。
3.5 自动化调度
-
使用调度器:
配置pg_squeeze
与调度器(如cron
或pg_cron
)结合,定期清理膨胀,但是要确保调度任务的时间避开业务高峰期。示例:
INSERT INTO squeeze.tables (schemaname, tablename, enabled) VALUES ('schema_name', 'table_name', TRUE); -- pg_squeeze 将自动在后台运行
复制
4. 替代方案
如果表的更新频率非常高,或者表特别大,可以考虑以下替代方法:
- 逻辑重建:通过创建新表、复制数据,然后切换到新表的方式手动完成重组。
- 分区表:对大表进行分区,可以减少膨胀,并降低表重组的复杂性。
- VACUUM FULL:虽然会锁表,但对于一些极端膨胀的表,VACUUM FULL 可能是更快的解决方法。


