W moim rozumieniu PostgreSQL używa pewnego rodzaju monitorów do odgadnięcia, czy istnieje konflikt w izolowanym poziomie zserializowanym. Wiele przykładów dotyczy modyfikowania tego samego zasobu w transakcjach równoległych, a transakcja nadająca się do szeregowania działa świetnie. Ale chcę przetestować współbieżną kwestię w inny sposób.Dlaczego transakcja serializowana w PostgreSQL uważa to za konflikt?
Decyduję się przetestować 2 użytkowników modyfikujących ich własne saldo na koncie i życzę sobie, aby PostgreSQL był wystarczająco inteligentny, aby nie wykryć go jako konfliktu, ale wynik nie jest tym, czego chcę.
Poniżej znajduje się mój stół, istnieją 4 konta, które należą do 2 użytkowników, każdy użytkownik ma konto rozliczeniowe i konto oszczędnościowe.
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);
danych tabela jest tak:
id | user_id | type | balance
----+---------+----------+---------
1 | 1 | checking | 1000
2 | 1 | saving | 1000
3 | 2 | checking | 1000
4 | 2 | saving | 1000
Teraz biegnę 2 jednoczesnych transakcji dla 2 użytkowników. W każdej transakcji zmniejszam rachunek czekowy o pieniądze i sprawdzam całkowite saldo użytkownika. Jeśli jest większa niż 1000, to zatwierdz, w przeciwnym razie wycofaj.
Przykład Użytkownik 1 jest:
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;
Użytkownik 2 jest taka sama, z wyjątkiem user_id = 2
w 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;
ja najpierw zobowiązać użytkownika 1 za transakcję, to sukces bez wątpienia. Kiedy zatwierdzam transakcję użytkownika 2, nie powiedzie się.
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.
Moje pytania są następujące:
- Dlaczego PostgreSQL myśli tego 2 transakcje są sprzeczne? Dodałem warunek user_id dla wszystkich SQL i nie modyfikuję user_id, ale wszystkie te nie mają żadnego efektu.
- Czy to oznacza transakcję podlegającą kodowaniu, która nie zezwala na równoczesne transakcje na tej samej tabeli, nawet jeśli ich zapis/odczyt nie powoduje konfliktu?
- Czy coś na użytkownika jest bardzo powszechne, czy należy unikać stosowania transakcji serializowanych dla operacji, które zdarzają się bardzo często?
Możesz zapytać o to na liście mailingowej Postgres. –