我试图让 PostgreSQL 积极地自动清空我的数据库。我目前已按如下方式配置自动真空吸尘器:
我注意到自动真空仅在数据库未加载时才会启动,因此我遇到死元组比活元组多得多的情况。有关示例,请参阅随附的屏幕截图。其中一张表有 23 个活动元组,但有 16845 个死元组等待真空。这太疯狂了!
当测试运行完成并且数据库服务器空闲时,自动真空开始,这不是我想要的,因为我希望自动真空在死元组数量超过 20% 活元组 + 50 时启动,因为数据库已经配置。服务器空闲时的自动真空对我来说是无用的,因为生产服务器预计会在持续时间内达到 1000 次更新/秒,这就是为什么即使服务器负载不足我也需要自动真空运行。
有什么我想念的吗?如何在服务器负载较重时强制运行自动吸尘器?
更新
这可能是一个锁定问题吗?有问题的表是通过插入后触发器填充的汇总表。这些表以 SHARE ROW EXCLUSIVE 模式锁定,以防止并发写入同一行。
VACUUM
通常不会将磁盘空间返回给操作系统,除非在某些特殊情况下。
从文档:
VACUUM
删除表和索引中的死行版本并标记可用空间以供将来重用的标准形式。但是,它不会将空间返回给操作系统,除非在表末尾的一个或多个页面完全空闲并且可以轻松获得排他表锁的特殊情况下。相比之下,VACUUM FULL
通过编写一个没有死空间的完整新版本的表文件来主动压缩表。这最大限度地减少了表的大小,但可能需要很长时间。它还需要额外的磁盘空间用于表的新副本,直到操作完成。
问题是:如何实现这个数据库状态one or more pages at the end of a table become entirely free
?这可以通过 完成VACUUM FULL
,但我没有足够的空间来实现它。那么还有没有其他可能呢?
在另一个问题中,我了解到我应该从我的一个表中优化布局以节省空间并获得更好的性能。我这样做了,但最终得到了比以前更大的表,并且性能没有改变。当然我做了一个VACUUM ANALYZE
. 怎么会?
(我看到如果我只索引单列,索引大小不会改变。)
这是我来自的表(我添加了尺寸 + 填充):
Table "public.treenode"
Column | Type | Size | Modifiers
---------------+--------------------------+------+-------------------------------
id | bigint | 8 | not null default nextval( ...
user_id | integer | 4+4 | not null
creation_time | timestamp with time zone | 8 | not null default now()
edition_time | timestamp with time zone | 8 | not null default now()
project_id | integer | 4 | not null
location | real3d | 36 | …
Run Code Online (Sandbox Code Playgroud) 由于频繁的新记录和更新记录导致索引和存储碎片,我面临性能下降和存储使用量增加的问题。
VACUUM 没有多大帮助。
不幸的是,CLUSTER 不是一个选项,因为它会导致停机并且 pg_repack 不适用于 AWS RDS。
我正在寻找 CLUSTER 的 hacky 替代品。在我的本地测试中似乎可以正常工作的一个是:
begin;
create temp table tmp_target as select * from target;
delete from target;
insert into target select * from tmp_target order by field1 asc, field2 desc;
drop table tmp_target;
commit;
Run Code Online (Sandbox Code Playgroud)
ctid
看起来的顺序是正确的:
select ctid, field1, field2 from target order by ctid;
Run Code Online (Sandbox Code Playgroud)
问题是:这看起来好吗?是否会锁定target
表以SELECT
查找导致应用程序停机的查询?有没有办法列出事务中涉及的锁?
互联网上有一些消息来源坚称idle in transaction
连接可能会阻止真空清理死元组,以下是一些示例:
\n\n\n处于空闲事务状态的事务可以持有阻止其他查询的锁。它还可以防止 VACUUM(包括 autovacuum)清理死行,从而导致索引或表膨胀或事务 ID 环绕。
\n
\n\n长事务实际上不是问题 \xe2\x80\x93 如果必须存在长事务和许多小更改,问题就会开始。请记住:长事务可能会导致 VACUUM 无法清除死行。
\n
实际上,有很多,但从我的角度来看,这听起来绝对荒谬:在大多数情况下,事务隔离级别是读已提交,这反过来意味着不需要为此类事务保留死元组,而且,我找到了替代方案关于该主题的意见:
\n\n\n它并不是真正的长期事务,而是长期快照。当然,长时间运行的 select 或 insert 语句可以做到这一点。对于高于读提交的隔离级别,整个事务将保留快照直到其宕机,因此如果某些事务打开了一个可重复读事务,然后在没有提交的情况下休假,那将是一个问题。挂起的准备好的交易也会(如果您不知道什么是准备好的交易,那么您可能没有使用它们)。
\n
或Pavel Luzanov 在 Cybertec 博文下的评论:
\n\n\n我相信长事务的示例仅适用于可重复读取(或可序列化)隔离级别。但默认情况下 BEGIN 使用已提交读。因此,在第一个会话中的 SELECT 完成后,VACUUM 将在会话 2 中的后续 UPDATE、DELETE 命令之后删除表中的死行。
\n
@Bill Karwin在他的回答中实际上证实了这一点(谢谢!)
\n问题是:是否存在“有效”“非虚构”场景时idle in transaction …
postgresql ×5
vacuum ×3
disk-space ×2
clustering ×1
datatypes ×1
locking ×1
maintenance ×1
performance ×1