Понимание СЕРИАЛИЗУЕМОГО уровня изоляции Postgres

#postgresql

#postgresql

Вопрос:

У меня есть два эксперимента, которые не работают должным образом после прочтения документации postgres. Я использую postgres 12

Эксперимент 1

Подготовка данных:

 CREATE TABLE Test 
(
    id SERIAL primary key,
    level int,
    value int
);

INSERT INTO Test (
    level,
    value
)
SELECT 1, 10 UNION ALL
SELECT 1, 20 UNION all
SELECT 1, 30 UNION ALL
SELECT 2, 100;
  

Затем я открываю два окна запросов

Окно 1

 BEGIN TRANSACTION ISOLATION level SERIALIZABLE;

INSERT INTO Test(level, value)
SELECT 2, SUM(value)
FROM Test
WHERE level = 1
  

Окно 2

 BEGIN TRANSACTION ISOLATION level SERIALIZABLE;

INSERT INTO Test(level, value)
SELECT 3, SUM(value)
FROM Test
WHERE level = 2
  

Теперь, если я зафиксирую сначала окно 1, затем окно 2, произойдет сбой окна 2, потому что данные, которые он прочитал, устарели, что и ожидалось. Однако, если я сначала зафиксирую окно 2, а затем окно 1, произойдет сбой окна 1, но почему? Окно 2 уже зафиксировано, окно 1 не повлияло на его результат. Результат окна 1 также не был затронут. Поэтому я не понимаю, почему сбой фиксации окна 1 после фиксации окна 2

Эксперимент 2

Это очень похоже на эксперимент 1, но теперь разные уровни хранятся в разных таблицах.

Подготовка данных

 CREATE TABLE Level1 (
    id SERIAL primary key,
    value int
);

CREATE TABLE Level2 (
    id SERIAL primary key,
    value int
);

CREATE TABLE Level3 (
    id SERIAL primary key,
    value int
);

INSERT INTO Level1 (
    value
)
SELECT 10 UNION ALL
SELECT 20 UNION all
SELECT 30; 

INSERT INTO Level2 (
    value
)
SELECT 100;
  

Окно 1

 BEGIN TRANSACTION ISOLATION level SERIALIZABLE;

INSERT INTO Level2(value)
SELECT SUM(value)
FROM Level1
  

Окно 2

 BEGIN TRANSACTION ISOLATION level SERIALIZABLE;

INSERT INTO Level3(value)
SELECT SUM(value)
FROM Level2
  

Теперь оба окна успешно фиксируются в любом порядке, в котором я их фиксирую! Это меня полностью смущает. Логически, это то же самое, что и эксперимент 1, я бы понял, если бы это работало как в эксперименте 1, но здесь сериализуемая изоляция, похоже, вообще не работает!

Ответ №1:

В эксперименте 1 обе транзакции считывали и записывали одни и те же таблицы, поэтому существует вероятность конфликта.

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

Сравните, что говорится в документации:

Блокировки предикатов в PostgreSQL, как и в большинстве других систем баз данных, основаны на данных, к которым фактически обращается транзакция. Они будут отображаться в pg_locks системном представлении с mode of SIReadLock . Конкретные блокировки, полученные во время выполнения запроса, будут зависеть от плана, используемого запросом

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

Этого не происходит в вашем втором эксперименте. Две транзакции могут быть сериализованы (как в вашем первом эксперименте): сначала в окне 2, затем в окне 1. На этот раз нет ложноположительной ошибки сериализации.

Комментарии:

1. Итак, я понял, что «если две транзакции выполняются одновременно, и они будут давать разные результаты в зависимости от порядка выполнения, одна из транзакций должна быть прервана». Но на самом деле это «Если две транзакции выполняются одновременно и существует хотя бы один порядок выполнения, который привел бы к одинаковым результатам, транзакцию не нужно прерывать». С некоторыми ложными срабатываниями, конечно. Правильно ли это сейчас?

2. Да, это определение «сериализуемого». Цитируя стандарт: Сериализуемое выполнение определяется как выполнение операций одновременного выполнения SQL-транзакций, которое производит тот же эффект, что и некоторое последовательное выполнение тех же SQL-транзакций.