【PostgreSQL里vacuum但是无法回收死元组的原因】
PostgreSQL数据库里的vacuum/autvacuum在我们日长的使用中可能会遇到很多问题,例如vacuum被阻塞,vacuum时间长,vacuum成功执行后,仍旧无法回收死元组等等。,本文主要介绍PostgreSQL的vacuum成功执行后,仍旧无法回收死元组的几种原因。
1.失效复制槽
select * from pg_replication_slots;
//删除失效的复制槽
SELECT pg_drop_replication_slot('slot_name');
2.长事务导致
如果数据库中存在长时间运行的事务,VACUUM 操作可能会被阻塞。这是因为 VACUUM 需要确保没有任何事务仍在访问那些它试图清理的行。如果有一个长事务存在,那长事务时间的其它表也没办法Vacuum,因为它不确认你是否会查其它表。
//根据事物开启时间排序
SELECT datname,
usename,
query,
xact_start,
now()-xact_start xact_duration,
query_start,
now()-query_start query_duration, state
FROM pg_stat_activity
WHERE state<>$$idle$$
AND (backend_xid is NOT null
OR backend_xmin is NOT null)
AND pid !=pg_backend_pid()
ORDER BY xact_start DESC;
//大于30min的长事务
SELECT datname,
usename,
query,
xact_start,
now()-xact_start xact_duration,
query_start,
now()-query_start query_duration, state
FROM pg_stat_activity
WHERE state<>$$idle$$
AND (backend_xid is NOT null
OR backend_xmin is NOT null)
AND now()-xact_start > interval $$30 min$$
ORDER BY xact_start;
3.存在未提交的prepare事务
SELECT gid, prepared, owner, database, transaction
FROM pg_prepared_xacts
ORDER BY age(transaction) DESC;
//大于30min的预处理语句
SELECT name,
statement,
prepare_time,
now()-prepare_time,
parameter_types, from_sql
FROM pg_prepared_statements
WHERE now()-prepare_time > interval $$30 min$$
ORDER BY prepare_time DESC;
4.idle in transaction状态的事务
select * from pg_stat_activity where state='idle in transaction';
VACUUM 依赖于事务的快照。如果某个事务正在运行并持有对某些数据的锁,VACUUM 可能会等待这个事务完成才能清理死元组。即使 VACUUM 执行完了,如果相关的事务没有提交,这些死元组仍然存在。
5.函数等内部结构涉及到表的访问
函数等内部结构涉及到表的访问,可以通过pg_locks去间接验证。
select * from pg_locks where relation='表名'::regclass ;
6.hot_standby_feedback参数问题
hot_standby_feedback参数打开,备库将向主库通报最旧的打开事务,并且主数据库上的 VACUUM 不会删除备用数据库上仍需要的旧行版本。
7.索引状态问题
VACUUM 处理表的死元组时也会扫描索引。如果索引存在问题或者不一致,可能导致死元组无法被清理。
解决方案: 确保索引状态,如不正常考虑重建索引。
select indexrelid::regclass,indrelid::regclass,indisvalid,indisready,indislive
from pg_index where indisvalid='f';
8.表和索引的并发访问
如果表和索引正在被其他并发操作访问,可能会导致 VACUUM 无法完全清理死元组。
select * from pg_stat_activity where query like '%表名%' AND pid !=pg_backend_pid();