2013-03-14 10 views
11

Znalazłem bardzo kłopotliwą sytuację, w której potrzebuję pomocy, aby zrozumieć.Problem z impasem, gdy transakcja próbuje zdobyć blokadę, która już jest przechowywana.

Prowadzone są dwie transakcje:
(2) zawiera blokadę dla zapytania delete from myTable where id = NAME_CONST('p_id',10000). Jest to blokada PRIMARY KEY, chociaż nie jest to pełny klucz, ale zakres. Wygląda na to, że jest to pełna blokada zapisu, gdy jest napisane lock_mode X locks rec but not gap.
(1) czeka na tę samą blokadę, również dla zapytania delete from myTable where id = NAME_CONST('p_id',10000).
(2) próbuje także uzyskać tę blokadę, a MySQL wykryje zakleszczenie.

Co nie mogę zrozumieć, dlaczego (2) musi ponownie nabyć blokadę jak to już trzyma go i to jest blokada zapisu (lock_mode X) we wszystkich przypadkach.

Wygląda również na to samo zapytanie.

Oto definicja tabeli

create myTable (
    id int unsigned not null, 
    value1 char(8) not null, 
    value2 int unsigned, 
    primary key (id, value1) 
); 

i oto informacja z SHOW ENGINE INNODB STATUS\G

------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
130313 14:46:28 
*** (1) TRANSACTION: 
TRANSACTION 75ACB8A3, ACTIVE 0 sec, process no 6110, OS thread id 139973945382656 starting index read 
mysql tables in use 1, locked 1 
LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s) 
MySQL thread id 5154970, query id 5201313618 192.168.0.2 user updating 
delete from myTable where id = NAME_CONST('p_id',10000) 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB8A3 lock_mode X waiting 
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 
0: len 4; hex 0005af3a; asc :;; 
1: len 8; hex 2020202020202020; asc ;; 
2: len 6; hex 000075acb890; asc u ;; 
3: len 7; hex ea0000020d011e; asc ;; 
4: len 4; hex 00000065; asc e;; 

*** (2) TRANSACTION: 
TRANSACTION 75ACB890, ACTIVE 0 sec, process no 6110, OS thread id 139973957895936 starting index read 
mysql tables in use 1, locked 1 
7 lock struct(s), hea 
p size 1248, 6 row lock(s), undo log entries 4 
MySQL thread id 5155967, query id 5201313625 192.168.0.1 user updating 
delete from myTable where id = NAME_CONST('p_id',10000) 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB890 lock_mode X locks rec but not gap 
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 
0: len 4; hex 0005af3a; asc :;; 
1: len 8; hex 2020202020202020; asc ;; 
2: len 6; hex 000075acb890; asc u ;; 
3: len 7; hex ea0000020d011e; asc ;; 
4: len 4; hex 00000065; asc e;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB890 lock_mode X waiting 
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 
0: len 4; hex 0005af3a; asc :;; 
1: len 8; hex 2020202020202020; asc ;; 
2: len 6; hex 000075acb890; asc u ;; 
3: len 7; hex ea0000020d011e; asc ;; 
4: len 4; hex 00000065; asc e;; 

*** WE ROLL BACK TRANSACTION (1) 
+0

Czy próbowałeś odtworzyć błąd? Jeśli "tak" możesz pokazać nam scenariusz? – ravnur

+0

Próbowałem usunąć to tak szybko, jak tylko mogę, za pomocą 'rozpocznij transakcję; kasować...; rollback; 'z dwóch wątków symulacji tak szybko, jak bash może je przekazać do mysql, ale ani razu nie dostałem zakleszczenia. Nie mam pojęcia, jak to się może stać. –

+0

Zobacz to pytanie (opisuje, jak można złapać impas): http://stackoverflow.com/questions/2143873/how-to-explain-the-deadlock- better. Istnieje również niezły artykuł o wykrywaniu i odzyskiwaniu z impasu: http://dwachira.hubpages.com/hub/Process-Deadlock-Definition-Prevention-Detection-Recovery-and-Avoidance. Ogólnie rzecz biorąc, zakleszczenie jest kwestią architektury i projektowania. Powinieneś przejrzeć swoje procesy, które aktualizują dane. – ravnur

Odpowiedz

19

To nie jest ten sam zamek - transakcja 1 zamka ma się na (indeksu) tylko zapis i nie blokada luki.

Oto co się dzieje:

  1. transakcja 2 dostaje blokadę dla (indeks) rekord ale nie luki zanim rekordu („rec ale nie luki”), to znaczy, że ma tylko blokadę rekordu .
  2. transakcja 1 próbuje uzyskać blokadę zapisu i szczeliny przed (czyli obok-key lock), ale nie może, ponieważ transakcja 2 posiada blokadę rekordu (a więc transakcji 1 czeka).
  3. Transakcja 2 próbuje uzyskać blokadę rekordu i lukę przed (tj. Następną blokadę klucza) i nie może, ponieważ transakcja 1 już czeka na tę samą blokadę i wyprzedza ją w kolejce.
  4. Zakleszczenie.

Nie jestem do końca pewien, dlaczego transakcja 2 nie nabywa natychmiastowej blokady klucza - może proces uzyskiwania blokady rekordu, a następnie blokada luki nie jest atomowa (w sensie ogólnym słowo).

Myślę, że problem polega na tym, że masz złożony klucz podstawowy (identyfikator, wartość1), ale usuwasz z zakresu (określając tylko identyfikator) - wymaga to blokad luki. Zobacz http://dev.mysql.com/doc/refman/5.0/en/innodb-record-level-locks.html, w szczególności:

zamek Gap nie jest potrzebny do stwierdzenia, które wiersze zablokować za pomocą unikalnego indeksu szukać unikalnego rzędu. (Nie dotyczy to przypadek, że warunek wyszukiwania zawiera tylko niektóre kolumny wielokrotnej kolumnie unikatowy indeks, w tym przypadku, zamek luka nie występuje.)

można zmienić kod więc określić pełny klucz podstawowy podczas usuwania, tj. id i wartość 1?

Inne opcje:

  • ponowić kasowania kiedy jest impas, na przykład złap błąd w kodzie, a następnie spróbuj ponownie, jeśli został spowodowany przez zakleszczenie. Takie podejście jest często łatwiej powiedzieć niż zrobić, zwłaszcza w starszych aplikacji, ale zaleca się, by strony MySQL na how to cope with deadlocks:

być zawsze przygotowany do ponownego wydania transakcję, jeśli nie z powodu impasu. Zakleszczenia nie są niebezpieczne. Po prostu spróbuj ponownie.

  • zablokować całą tabelę z zamkiem na poziomie tabeli przed wydaniem polecenia delete. Może to jednak wpływać na wydajność i jest "młotem".
+0

Może być możliwe, jeśli dodaję wewnętrzny wybór wszystkich możliwych kluczy wewnątrz instrukcji IN lub join. Dziwne jest to, że pojedyncza instrukcja delete-a powinna: a) nie być atomistyczna i b) nie wystarczająco szybka, aby jakiekolwiek inne usuwanie zostało przez nią zablokowane. –

+0

Jak często dostajesz te zakleszczenia? Czy coś innego dzieje się w transakcji przed usunięciem? Ile współbieżnych wątków powoduje usunięcie? Czy możesz napisać kod? –

+0

Zdarza się to kilka razy każdego dnia, zawsze z dwóch różnych maszyn. Nic nie dzieje się przed usunięciem, które odnosi się do tej tabeli. Inne rzeczy dzieją się z innymi tabelami, ale to nie powinno mieć znaczenia. Nie mogę opublikować kodu Unscrambled. –

Powiązane problemy