Repeatable read - czy chroni przed nadpisywaniem zmian dokonanych przez drugą transakcję?

0

Z tego co czytałem o izolacji Repearable Read to chroni ona daną transakcję przed niepowtarzalnymi odczytami czyli że raz czytane w transakcji rekordy przy ponownym ich czytaniu w tej transakcji będą zwracały te same dane. Ale czy ten mechanizm zabezpiecza też przed nadpisywaniem wyników działania drugiej transakcji (i jeżeli tak to w jaki sposób)?
Chodzi mi o znaną sytuację gdy wątek i transakcja t1 pobiera jakieś dane, drugi wątek i transakcja t2 pobiera te same dane, obie transakcje dokonują obliczeń, druga transakcja zapisuje swoje wyniki do tego rekordu z którego pobrała dane i commituje się, pierwsza transakcja t1 kończy obliczenie i też zapisuje swój wynik do tego samego rekordu nadpisując zmianę dokonaną przez transakcje t2.

1

W przypadku REPETABLE READS jeżeli w pierwszej transakcji zmodyfikujesz rekord, to w drugiej transakcji nie będziesz go mógł go odczytać, dopóki pierwsza transakcja się nie zakończy. Nie będziesz mógł również w drugiej transakcji zmodyfikować rekordu, który został odczytany w pierwszej transakcji, nim ta się nie zakończy. W przypadku takim jak opisałeś czyli dwie transakcje najpierw odczytują rekordy, a następnie próbują zmodyfikować dojdzie do zakleszczenia.

0

Dzięki.
Czyli samo w trybie Repeatable Reads samo przeczytanie rekordu przez transakcję t1 spowoduje że transakcja t2 nie będzie mogła go już zmodyfikować bo zostanie on zablokowany i będzie tylko do czytania dla innych transakcji?

0
Pierce111 napisał(a)

Dzięki.
Czyli samo w trybie Repeatable Reads samo przeczytanie rekordu przez transakcję t1 spowoduje że transakcja t2 nie będzie mogła go już zmodyfikować bo zostanie on zablokowany i będzie tylko do czytania dla innych transakcji?

Tak

0
AdamPL napisał(a)

W przypadku REPETABLE READS jeżeli w pierwszej transakcji zmodyfikujesz rekord, to w drugiej transakcji nie będziesz go mógł go odczytać, dopóki pierwsza transakcja się nie zakończy. Nie będziesz mógł również w drugiej transakcji zmodyfikować rekordu, który został odczytany w pierwszej transakcji, nim ta się nie zakończy. W przypadku takim jak opisałeś czyli dwie transakcje najpierw odczytują rekordy, a następnie próbują zmodyfikować dojdzie do zakleszczenia.

Hmmmm, jak zwykle "to zależy"

Weźmy na przykład popularnego mysql-a i taką sekwencję:

ses1 > set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

ses1 > set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

ses1 > select * from test;
+----+------+
| id | val  |
+----+------+
|  1 |   10 |
+----+------+
1 row in set (0.00 sec)

[code]
ses2 > set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

ses2 > set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 10 |
+----+------+
1 row in set (0.00 sec)
[/code]

Teraz w sesji 1 robimy obliczenia i update (bez commit):
[code]
ses1 > update test set val = 5 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
[/code]

A w sesji 2 - o dziwo - da się odczytać rekord ?
[code]
ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 10 |
+----+------+
1 row in set (0.00 sec)
[/code]

No to teraz commit w jedynce
[code]
ses1 > commit;
Query OK, 0 rows affected (0.07 sec)

ses1 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 5 |
+----+------+
1 row in set (0.00 sec)
[/code]

I sprawdźmy w dwójce
[code]
ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 10 |
+----+------+
1 row in set (0.00 sec)
[/code]

To teraz w dwójce zapiszmy wynik nazych skomplikowanych obliczeń (bez commit)
[code]
ses2 > update test set val = 25 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 25 |
+----+------+
1 row in set (0.00 sec)
[/code]

a teraz w jedynce odczytajmy, i zróbmy skomplikowane obliczenia
[code]
ses1 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 5 |
+----+------+
1 row in set (0.00 sec)

ses1 > update test set val = 125 where id =1;
[/code]

jak widać - update "zawisł" w oczekiwaniu na zdjęcie blokady

to w sesji 2 robimy commit
[code]
ses2 > commit;
Query OK, 0 rows affected (0.08 sec)

ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 25 |
+----+------+
1 row in set (0.00 sec)
[/code]

i wracamy do sesji 1 - naszym oczom ukazuje się to:
[code]
Query OK, 1 row affected (2.29 sec)
Rows matched: 1 Changed: 1 Warnings: 0

ses1 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 125 |
+----+------+
1 row in set (0.00 sec)
[/code]

Oczy nie wierzą - wracamy do 2
[code]
ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 25 |
+----+------+
1 row in set (0.00 sec)
[/code]

cuda Panie, cuda

[code]
ses2 > commit;
Query OK, 0 rows affected (0.00 sec)

ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 25 |
+----+------+
1 row in set (0.00 sec)
[/code]

i znowu 1
[code]
ses1 > commit;
Query OK, 0 rows affected (0.12 sec)

ses1 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 125 |
+----+------+
1 row in set (0.00 sec)
[/code]

[code]
ses2 > select * from test for update;
+----+------+
| id | val |
+----+------+
| 1 | 125 |
+----+------+
1 row in set (0.00 sec)

ses2 > commit;
Query OK, 0 rows affected (0.00 sec)

ses2 > select * from test;
+----+------+
| id | val |
+----+------+
| 1 | 125 |
+----+------+
1 row in set (0.00 sec)

[/code]

Wracając do źródeł:

AdamPL napisał(a)

W przypadku REPETABLE READS jeżeli w pierwszej transakcji zmodyfikujesz rekord, to w drugiej transakcji nie będziesz go mógł go odczytać, dopóki pierwsza transakcja się nie zakończy.

Udało się odczytać

AdamPL napisał(a)

W przypadku takim jak opisałeś czyli dwie transakcje najpierw odczytują rekordy, a następnie próbują zmodyfikować dojdzie do zakleszczenia.

Nie doszło do żadnego zakleszczenia.

Polecam to samo ćwiczenie zrobić w Oraclu, potem w MSSQL następnie w PostgreeSQL ..... wnioski mogą być ciekawe ;)

0

Cóż mogę powiedzieć. Tak kończy się praca w bazie danych o której nie ma się pojęcia. Powstaje frustracja, nerwy, wtf... a wszystko przez niewiedzę. Z tego też powodu bazą danych powinni się zajmować profesjonaliści, a nie amatorzy. Właściciele wielu firemek, powinni zainwestować w prawdziwych bazodanowców, a nie przymuszać do tego jakiś PHP-owców czy C#-owców. Później na forum 4programmers czytam jaki to "MySQL jest zjeb****", "porównajcie sobie z Oracle" itd.

@kordirko

  1. MySQL wykorzystuje domyślnie silnik InnoDB, który działa domyślnie w poziomie izolacji REPETABLE READ, nie trzeba go w ten sposób specjalnie ustawiać. W innych bazach danych domyślnym poziomem izolacji jest z kolei READ COMMITED.
  2. Polecenie SELECT czyta zawsze wartość z migawki utworzonej w momencie wejścia w transakcję, dzięki temu zwykły SELECT działa nawet wtedy gdy w innej transakcji są niezatwierdzone zmiany. To sprawia, że MySQL jest szybki i nie ma co chwila informacji o LOCK-ach.
  3. Jeżeli wchodzisz w MySQL w transakcję w której chcesz wyliczyć jakąś wartość na podstawie istniejącej już wartości oraz chcesz mieć gwarancję, że nic innego jej nie zmodyfikuje zamiast zwykłego SELECT robisz SELECT lock-ujący (FOR UPDATE lub LOCK IN SHARE MODE).
  4. Jeżeli w jednej transakcji masz zrobiony nie COMMIT-owany UPDATE, to w drugiej transakcji nie zrobisz SELECT-a lock-ującego, dojdzie do zakleszczenia.
  5. MySQL zachował się poprawnie w przedstawionym przez Ciebie przypadku. Zdania "oczom nie wierzę", "cuda panie cuda" dowodzą niestety Twojej niewiedzy i niezrozumienia tematu.

P.S. Nie odbieraj tego jako atak. Nie każdy musi się znać na wszystkim i to jest zrozumiałe. Nie atakuję Ciebie, tylko zwracam uwagę, że nie masz wystarczającej wiedzy w tym temacie.

0

Dzięki za pomoc : )
Mógłbyś mi tylko wyjaśnić to zakleszczenie:

  1. Jeżeli w jednej transakcji masz zrobiony nie COMMIT-owany UPDATE, to w drugiej transakcji nie zrobisz SELECT-a lock-ującego, dojdzie do zakleszczenia.

Z tego co wiem to gdy transakcja nie może dostać jakiegoś locka to po prostu czeka na niego tak długo aż nie zostanie on zwolniony. Czyli po commitowaniu transakcji T1, transakcja T2 ruszy dalej i wykona swojego Select'a.

1 użytkowników online, w tym zalogowanych: 0, gości: 1