почему записи в таблице предотвращают возникновение вакуума в другой?

Имея уровень изоляции READ COMMITTED, незанятые транзакции, выполнившие операцию записи, не позволят вакуумировать мертвые строки для таблиц, в которые была записана транзакция.

Это очевидно для таблиц, которые были написаны транзакциями, которые все еще выполняются. Здесь вы можете найти хорошее объяснение.

Но мне непонятно, почему это ограничение влияет и на любые другие таблицы.

Например: транзакция T запускается и обновляет таблицу B, вакуум выполняется для таблицы A, пока T находится в состоянии «простаивает в транзакции». Почему в этом случае мертвые строки в 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

Чистка пылесосом удалит их, как и ожидалось:

# 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 в таблице b:

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

Я снова генерирую больше мертвых строк в другой транзакции T1, которая началась после T:

# 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 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

person alostale    schedule 07.09.2017    source источник
comment
нет. транзакция содержит грязные страницы. если вы не изменяли таблицу b, ваш idel в транзакции не повлияет на что-либо в таблице b ... Я предполагаю :)   -  person Vao Tsun    schedule 07.09.2017
comment
@VaoTsun это то же самое, что я предполагал, но я хотел убедиться, потому что мне это непонятно.   -  person alostale    schedule 07.09.2017
comment
хорошо, тогда. Имея уровень изоляции READ COMMITTED, незанятые транзакции, которые выполнили операцию записи, НЕ будут препятствовать автоочистке для очистки мертвых строк для таблиц, в которые транзакция НЕ записывала. Я не могу найти, что это четко указано в документах, но это кажется естественным с Postgres MVCC   -  person Vao Tsun    schedule 07.09.2017
comment
Обратите внимание, что транзакция будет только удерживать вакуум от очистки новых мертвых кортежей с момента начала транзакции. Мертвые кортежи, созданные до начала этой транзакции, могут быть собраны и использованы повторно.   -  person Scott Marlowe    schedule 07.09.2017
comment
@VaoTsun Я сделал простой тестовый пример (описанный выше), и он выглядит так. Записывает в любую таблицу, предотвращает автоочистку во всех таблицах.   -  person alostale    schedule 08.09.2017
comment
@ScottMarlowe ясно, что необходимо предотвратить удаление старых мертвых строк, которые все еще могут быть видны текущим транзакциям, и это нормально для таблиц, написанных этими текущими транзакциями. Вопрос в том, почему нельзя очистить таблицу, если есть какая-либо текущая транзакция, которая произвела запись.   -  person alostale    schedule 08.09.2017
comment
@alostale: только в таблицах, которые используются этой транзакцией. Транзакции могут потребоваться старые версии строк. Если транзакция никогда не использует таблицу, эта таблица будет очищена.   -  person a_horse_with_no_name    schedule 08.09.2017
comment
@a_horse_with_no_name это ясно, и это то, что я изначально предполагал. Но если мой тестовый пример верен (см. Выше), похоже, что все таблицы будут затронуты, как только появится неактивная транзакция, записанная в любую таблицу.   -  person alostale    schedule 08.09.2017
comment
@alostale Мне понравился ваш подход, поэтому я решил воспроизвести удивительные явления - конечно, воспроизвести не могу. INFO: "a": found 0 removable, 0 nonremovable row versions in 0 out of 9 pages поделитесь своей версией и создайте сценарий в скрипте rexter или sql (извините - не уверен насчет имен сайтов - где угодно в общем env)   -  person Vao Tsun    schedule 08.09.2017
comment
@VaoTsun: я использую PostgreSQL 9.4.8 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010, 64-bit   -  person alostale    schedule 08.09.2017
comment
@VaoTsun: извините, но я не могу создать скрипку, потому что, похоже, она не позволяет выполнить вакуум   -  person alostale    schedule 08.09.2017
comment
@alostale да, и делиться db в разных транзакциях тоже не получится, я полагаю   -  person Vao Tsun    schedule 08.09.2017
comment
Но это была моя точка зрения, таблица МОЖЕТ быть очищена, к которой обращается текущая транзакция. НО кортежи, обновленные этой транзакцией, и все кортежи, которые были запущены после нее, не могут быть собраны во время выполнения транзакции.   -  person Scott Marlowe    schedule 08.09.2017


Ответы (3)


Ответьте на этот вопрос через Twitter.

Текущее (по крайней мере, до PostgreSQL 9.6) поведение:

Текущие транзакции, выполняющие операцию записи в любую таблицу, предотвратят очистку мертвых строк, созданных зафиксированными транзакциями, начавшимися после первой активной транзакции в любой другой таблице.

Даже это ограничение не требуется с концептуальной точки зрения, это то, как текущий алгоритм реализуется для повышения производительности при проверке причин мертвых строк.

person alostale    schedule 11.09.2017
comment
Когда вы говорите, что операция записи в любой таблице предотвратит очистку мертвых строк ..., связано ли это с какой-либо таблицей в этой конкретной схеме или с любой таблицей в базе данных. - person Nik; 29.08.2020

не могу воспроизвести:

сценарий первого сеанса:

-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)
person Vao Tsun    schedule 08.09.2017
comment
какую версию pg вы используете? - person alostale; 08.09.2017
comment
извините - смешно глупо с моей стороны прошу предоставить версию, а не сам :) - person Vao Tsun; 08.09.2017
comment
Я понял, что забыл включить в свой сценарий один шаг: перед запуском длинной транзакции нужно создать мертвые строки. Я отредактировал им свой исходный пост. - person alostale; 08.09.2017

Важно снова создать мертвые строки в транзакции, запущенной ПОСЛЕ транзакции, которая остается открытой.

Мне удалось воспроизвести проблему со следующими версиями:

  • PostgreSQL 9.3.19 на x86_64-unknown-linux-gnu, скомпилирован gcc (Ubuntu 4.8.4-2ubuntu1 ~ 14.04.3) 4.8.4, 64-разрядная версия

  • PostgreSQL 9.5.9 на x86_64-pc-linux-gnu, скомпилированный gcc (Ubuntu 4.8.4-2ubuntu1 ~ 14.04.3) 4.8.4, 64-разрядная версия

person caristu    schedule 08.09.2017