为什么在 table 中写入以防止在另一个中出现真空?

why writes in a table prevent vacuums in another?

具有 READ COMMITTED 隔离级别,已执行写操作的空闲事务将防止真空清理事务写入的 table 的死行。

对于 table 由仍在进行中的交易写入的数据来说,这一点很明显。 Here你可以找到一个很好的解释。

但我不清楚为什么这个限制也会影响 any 其他 tables.

例如:事务 T 启动并更新 table B,在 T 处于 "idle in transaction" 状态时为 table A 执行真空。这种场景下,为什么A中的死行无法去除?

这是我所做的:

# show default_transaction_isolation;
 default_transaction_isolation 
-------------------------------
 read committed
(1 row)
# create table a (v int);
CREATE TABLE
# create table b (v int);
CREATE TABLE

# insert into a values (generate_series(1,1000));
INSERT 0 1000

此时我进行更新以生成新的 1000 个死行

# update a set v = v + 1;
UPDATE 1000

Vacuuming 将按预期删除它们:

# vacuum verbose a;
INFO:  vacuuming "public.a"
INFO:  "a": removed 1000 row versions in 5 pages
INFO:  "a": found 1000 removable, 1000 nonremovable row versions in 9 out of 9 pages
DETAIL:  0 dead row versions cannot be removed yet.
There were 0 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM

我现在开始事务 T 写入 table b:

# begin;
BEGIN
# insert into b values (generate_series(1,1000));
INSERT 0 1000

我在 T:

之后启动的不同事务 T1 中再次生成更多死行
# begin;
# update a set v = v + 1;
# commit;

不同的交易中:

# vacuum verbose a;
INFO:  vacuuming "public.a"
INFO:  "a": found 0 removable, 2000 nonremovable row versions in 9 out of 9 pages
DETAIL:  1000 dead row versions cannot be removed yet.
There were 34 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM

这是相关部分:详细信息:尚无法删除 1000 个死行版本。

如果我提交事务 T 并再次执行 vacuum,我会按预期删除死行:

# vacuum verbose a;
INFO:  vacuuming "public.a"
INFO:  "a": removed 1000 row versions in 5 pages
INFO:  "a": found 1000 removable, 1000 nonremovable row versions in 9 out of 9 pages
DETAIL:  0 dead row versions cannot be removed yet.
There were 34 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM

无法重现:

第一个会话脚本:

-bash-4.2$ cat prim.sql
create table a (v int);
create table b (v int);
insert into a values (generate_series(1,1000));
update a set v = v + 1;
vacuum verbose a;
begin;
  insert into b values (generate_series(1,1000));
  select pg_sleep(9);
  select e'I\'m still open transaction'::text prim;

第二次会话并检查状态:

-bash-4.2$ cat 1.sh
(sleep 3; psql t -c "vacuum verbose a;") &
(sleep 5; psql t -c "select state,query from pg_stat_activity where state != 'idle' and pid <> pg_backend_pid()") &
psql t -f prim.sql

和运行:

-bash-4.2$ bash 1.sh
CREATE TABLE
CREATE TABLE
INSERT 0 1000
UPDATE 1000
psql:prim.sql:5: INFO:  vacuuming "public.a"
psql:prim.sql:5: INFO:  "a": removed 1000 row versions in 5 pages
psql:prim.sql:5: INFO:  "a": found 1000 removable, 1000 nonremovable row versions in 9 out of 9 pages
DETAIL:  0 dead row versions cannot be removed yet.
There were 0 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
BEGIN
INSERT 0 1000
INFO:  vacuuming "public.a"
INFO:  "a": found 0 removable, 1000 nonremovable row versions in 9 out of 9 pages
DETAIL:  0 dead row versions cannot be removed yet.
There were 1000 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
 state  |        query
--------+---------------------
 active | select pg_sleep(9);
(1 row)

 pg_sleep
----------

(1 row)

            prim
----------------------------
 I'm still open transaction
(1 row)

如您所见,第一个会话在不同会话中的真空发生之前、期间和之后都处于活动状态。

我试过的版本是:

t=# select version();
                                                   version
--------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.3.14 on x86_64-redhat-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
(1 row)

在保持打开的事务之后启动的事务中再次生成死行很重要。

我已经能够使用以下版本重现该问题:

  • PostgreSQL 9.3.19 on x86_64-unknown-linux-gnu,由 gcc 编译(Ubuntu 4.8.4-2ubuntu1~14.04.3)4.8.4,64 位

  • x86_64-pc-linux-gnu 上的 PostgreSQL 9.5.9,由 gcc 编译(Ubuntu 4.8.4-2ubuntu1~14.04.3)4.8.4,64 位'

通过 Twitter 关注这个问题。

当前(至少达到 PostgreSQL 9.6)行为是:

any table 中执行写入操作的实时事务将防止清除由在 any 中的第一个实时事务之后开始的已提交事务生成的死行其他 table.

从概念的角度来看,即使这个限制也不是必需的,但当前算法是如何实现的,以提高检查死行的性能。