В моем понимании PostgreSQL использует какие-то мониторы, чтобы угадать, есть ли конфликт в сериализованном уровне изоляции. Многие примеры касаются изменения одного и того же ресурса в параллельных транзакциях, и сериализуемая транзакция отлично работает. Но я хочу проверить параллельную проблему по-другому.Почему сериализуемая транзакция PostgreSQL считает это конфликтом?
Я решил протестировать 2 пользователей, изменяющих собственный баланс своих аккаунтов, и желаю, чтобы PostgreSQL был достаточно умен, чтобы не обнаружить его как конфликт, но результат не тот, который я хочу.
Ниже приведена таблица, в которой 4 учетных записи, принадлежащих 2 пользователям, каждый пользователь имеет текущую учетную запись и сберегательную учетную запись.
create table accounts (
id serial primary key,
user_id int,
type varchar,
balance numeric
);
insert into accounts (user_id, type, balance) values
(1, 'checking', 1000),
(1, 'saving', 1000),
(2, 'checking', 1000),
(2, 'saving', 1000);
В таблице данные, как это:
id | user_id | type | balance
----+---------+----------+---------
1 | 1 | checking | 1000
2 | 1 | saving | 1000
3 | 2 | checking | 1000
4 | 2 | saving | 1000
Теперь я бег 2 одновременной транзакции для 2 пользователей. В каждой транзакции я уменьшаю текущую учетную запись с некоторыми деньгами и проверяю общий баланс пользователя. Если оно больше 1000, то совершите, в противном случае откат.
1 пользователя в пример:
begin;
-- Reduce checking account for user 1
update accounts set balance = balance - 200 where user_id = 1 and type = 'checking';
-- Make sure user 1's total balance > 1000, then commit
select sum(balance) from accounts where user_id = 1;
commit;
Пользователь 2 то же самое, за исключением user_id = 2
в where
:
begin;
update accounts set balance = balance - 200 where user_id = 2 and type = 'checking';
select sum(balance) from accounts where user_id = 2;
commit;
Я первый совершить пользователь 1 в сделку, ему успеха, без сомнения. Когда я совершаю транзакцию пользователя 2, она терпит неудачу.
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
Мои вопросы:
- Почему PostgreSQL считает, что это 2 транзакции конфликт? Я добавил условие user_id для всех SQL и не изменяет user_id, но все это не влияет.
- Означает ли это, что сериализуемая транзакция не позволяет выполнять параллельные транзакции в одной таблице, даже если их чтение/запись не конфликтуют?
- Сделать что-то для каждого пользователя очень распространенным я могу избежать использования сериализуемой транзакции для операций, которые происходят очень часто?
Возможно, вы захотите спросить об этом в списке рассылки Postgres –