2017-02-17 58 views
6

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:

  1. 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.
  2. 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?
  3. Czy coś na użytkownika jest bardzo powszechne, czy należy unikać stosowania transakcji serializowanych dla operacji, które zdarzają się bardzo często?
+0

Możesz zapytać o to na liście mailingowej Postgres. –

Odpowiedz

5

Można rozwiązać ten problem z następującym index:

CREATE INDEX accounts_user_idx ON accounts(user_id); 

Ponieważ istnieje tak niewiele danych w przykładowej tabeli, trzeba będzie powiedzieć PostgreSQL używać skanowanie indeksu:

SET enable_seqscan=off; 

Teraz twój przykład zadziała!

Jeśli to wygląda na czarną magię, spójrz na plany wykonywania zapytań w swoich instrukcjach SELECT i UPDATE.

Bez indeksu oba będą używać sekwencyjnego skanowania na stole, , tym samym odczytując wszystkie wiersze tabeli. Więc obie transakcje zakończą się na SIReadLock na całej tabeli.

Spowoduje to błąd serializacji.

+0

Kluczowym punktem jest zatem uniknięcie pełnego skanowania tabeli, a indeks nie zostanie wywołany, jeśli dane tabeli są zbyt małe, czy moje zrozumienie jest prawidłowe? – darkbaby123

+0

To jest dokładnie słuszne. –

-2

Według mojej wiedzy serializable ma najwyższy poziom izolacji, dlatego najniższy poziom współbieżności. Transakcje występują jedna po drugiej bez współbieżności.

+0

Dlaczego PostgreSQL uważa, że ​​te 2 transakcje są w konflikcie? Dodałem warunek user_id dla wszystkich SQL i nie modyfikuję user_id, ale wszystkie te nie mają żadnego efektu. -> nie wiesz, dlaczego masz tego kumpla, być może, gdy szuka rekordów, ponieważ ma wspólne pole typu "sprawdzanie" może być powodem. czy wypróbowałeś jeden z "oszczędnościami" i jeden z "sprawdzeniem"? – sivalingam

+0

Próbowałem, wciąż mam konflikt. W rzeczywistości jest to konflikt nawet wtedy, gdy wszystkie warunki są zredukowane do 'gdzie user_id = x'. – darkbaby123