SELECT是否像VACUUM一样删除死行?


9

我在摆弄,VACUUM并注意到一些意外的行为,其中SELECT从表中读取行似乎减少了VACUUM以后的工作量。

测试数据

注意:自动真空已禁用

CREATE TABLE numbers (num bigint);
ALTER TABLE numbers SET (
  autovacuum_enabled = 'f',
  toast.autovacuum_enabled = 'f'
);

INSERT INTO numbers SELECT generate_series(1, 5000);

试验1

现在,我们对所有行进行更新,

UPDATE numbers SET num = 0;

当我们跑步时,VACUUM (VERBOSE) numbers;我们得到了,

INFO:  vacuuming "public.numbers"
INFO:  "numbers": removed 5000 row versions in 23 pages
INFO:  "numbers": found 5000 removable, 5000 nonremovable row versions in 45 out of 45 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 6585
There were 0 unused item pointers.

试用2

现在我们发布另一个UPDATE,但是这次我们添加一个SELECT之后,

UPDATE numbers SET num = 1;
SELECT * FROM numbers;

当我们跑步时,VACUUM (VERBOSE) numbers;我们得到了,

INFO:  vacuuming "public.numbers"
INFO:  "numbers": removed 56 row versions in 22 pages
INFO:  "numbers": found 56 removable, 5000 nonremovable row versions in 45 out of 45 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 6586
There were 56 unused item pointers.

这里到底发生了什么?在SELECT从访问的页面中删除死元组之后,为什么我运行第二个版本,就像这样VACUUM做?

我在macOS 10.14.5上运行Postgres 11.3。


2
您使用哪个客户端来运行命令?是否启用了自动提交功能?
mustaccio

2
我要删除问题“ VACUUM表基本上只是SELECT * FROM表盖吗?” (不是)我认为这是一个很好的跟进,这里的答案很简单,就是SELECT可以删除死行,并且确实与VACUUM共享。它们之间的不同之处将是有关XID过渡的详尽讨论,以及大量其他内容。这个问题基本上是“除了清除死行之外,真空还有什么其他作用”。(有些含糊)
Evan Carroll

@mustaccio我使用ActiveRecord使用Ruby脚本对这些测试进行了测试,该脚本使用了引擎盖下的PG gem。我认为默认情况下会启用自动提交,因为除非明确使用了BEGIN,否则您无需发出任何COMMIT。
rafbm

Answers:


5

/ r / PostgreSQL上的这篇帖子Laurenz Albe回答,似乎只有Heap Only Tuples(HOT)更新可能是造成问题的原因。根据HOT更新中的描述src/backend/access/heap/README.HOT

实际上,在元组检索期间,当页面几乎已满(<10%空闲)并且可以获取缓冲区清除锁时,就会发生空间回收。这意味着UPDATEDELETESELECT可以触发空间回收,但往往没有中INSERT ... VALUES,因为它不检索行。

引号不在原始答案中,而其余部分是引号,

要支持或反驳此理论,请运行以下查询:

SELECT n_tup_upd, n_tup_hot_upd
FROM pg_stat_user_tables
WHERE schemaname = 'public' AND relname = 'TABLE_NAME';

如果n_tup_hot_upd大于零,我们有一个案例。


现在我们在说话。+1
mustaccio

HOT似乎是一个很好的解释。如果为I CREATE INDEX idx_numbers ON numbers USING btree (num),则VACUUM输出更改为INFO: "numbers": removed 5000 row versions in 45 pages。但是请注意,在无索引的情况下,n_tup_hot_upd在UPDATE和SELECT之间以及SELECT和VACUUM之间始终为0。我还确保SELECT pg_sleep(10)在每个语句之间运行,以便统计信息是最新的(我确实看到了seq_scan: 2,一个用于UPDATE,一个用于SELECT)。
rafbm

选择是否在这种情况下生成WAL?我的印象是选择根本不产生WAL。如果是,这意味着将删除死行传播到任何从站。如果否,则意味着从站仍需要进行清理。这也意味着主机和从机并不完全相同。嗯,也许我需要做一些研究,然后发表一个问题和/或答案。
Colin't Hart,

1

是的,在未索引表的特殊情况下,是的,SELECT可以完成与VACUUM相同的工作(就删除死行而言)。


3
你能补充一个解释吗?
Laurenz Albe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.