Jak uniknąć "impasu mysql znalezionego podczas próby uzyskania blokady; spróbuj ponownie uruchomić transakcję"

Mam tabelę innoDB, która rejestruje użytkowników online. Jest aktualizowany przy każdym odświeżaniu strony przez użytkownika, aby śledzić, na których stronach SIĘ ZNAJDUJĄ i ich ostatnią datę dostępu do witryny. Następnie mam cron, który działa co 15 minut, aby usunąć stare rekordy.

Znalazłem "impas znaleziony podczas próby uzyskania blokady; spróbuj ponownie uruchomić transakcję" przez około 5 minut wczoraj wieczorem i wydaje się, że jest to podczas uruchamiania wstawek do tej tabeli. Czy ktoś może zasugerować jak tego uniknąć błąd?

=== EDIT = = =

Oto zapytania, które są uruchomione:

Pierwsza wizyta na stronie:

INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3

Na każdej stronie odśwież:

UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888

Cron co 15 minut:

DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND

Następnie robi kilka liczeń, aby zarejestrować niektóre statystyki(np.: Członkowie online, Goście online).

Author: BenMorel, 2010-02-25

8 answers

Jedną z łatwych sztuczek, która może pomóc w większości impasów, jest sortowanie operacji w określonej kolejności.

Pojawia się impas, gdy dwie transakcje próbują zablokować dwie blokady przy przeciwnych zleceniach, tj.:

  • Połączenie 1: locks key(1), locks key (2);
  • połączenie 2: locks key(2), locks key (1);

Jeśli oba uruchomią się w tym samym czasie, Połączenie 1 zablokuje key(1), Połączenie 2 zablokuje key (2) i każde połączenie będzie czekać, aż drugie zwolni klucz - > impas.

Teraz, jeśli zmieniłeś swoje zapytania tak, że połączenia będą blokować klucze w tej samej kolejności, czyli:

  • Połączenie 1: locks key(1), locks key (2);
  • połączenie 2: Klucz do zamków(1), klucz do zamków(2);
Nie da się uzyskać impasu.

Więc to jest to, co proponuję:

  1. Upewnij się, że nie masz innych zapytań, które blokują dostęp do więcej niż jednego klucza na raz, z wyjątkiem instrukcji delete. jeśli tak (i podejrzewam, że tak), kolejność ich gdzie w (k1, k2,..kn) w porządku rosnącym.

  2. Napraw polecenie delete, aby działało w kolejności rosnącej:

Zmień

DELETE FROM onlineusers 
WHERE datetime <= now() - INTERVAL 900 SECOND

Do

DELETE FROM onlineusers 
WHERE id IN (
    SELECT id FROM onlineusers
    WHERE datetime <= now() - INTERVAL 900 SECOND 
    ORDER BY id
) u;

Kolejną rzeczą, o której należy pamiętać, jest to, że dokumentacja mysql sugeruje, że w przypadku impasu klient powinien spróbować ponownie automatycznie. możesz dodać tę logikę do kodu klienta. (Powiedzmy, 3 powtarza ten konkretny błąd przed poddaniem się).

 320
Author: Omry Yadan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-19 20:27:15

Impas ma miejsce, gdy dwie transakcje czekają na siebie, aby uzyskać blokadę. Przykład:

  • Tx 1: lock a, then B
  • Tx 2: lock B, then a

Istnieje wiele pytań i odpowiedzi na temat impas. Za każdym razem, gdy wstawiasz/aktualizujesz / lub usuwasz wiersz, uzyskuje się blokadę. Aby uniknąć impasu, należy upewnić się, że równoległe transakcje nie aktualizują wiersza w kolejności, która może spowodować impas. Ogólnie rzecz biorąc, spróbuj zdobyć lock zawsze w tym samym order nawet w różnych transakcjach (np. zawsze najpierw tabela a, potem tabela B).

Innym powodem blokady w bazie danych mogą być brakujące indeksy . Gdy wiersz jest wstawiany/aktualizowany/usuwany, baza danych musi sprawdzić ograniczenia relacyjne, to znaczy upewnić się, że relacje są spójne. W tym celu baza danych musi sprawdzić klucze obce w powiązanych tabelach. To Może spowodować uzyskanie innego Locka niż wiersz, który jest modyfikowany. Upewnij się, że zawsze mieć indeks na kluczach obcych (i oczywiście klucze podstawowe), w przeciwnym razie może to spowodować blokadę tabeli zamiast blokadę wiersza. Jeśli dojdzie do zablokowania stołu, wartość blokady jest wyższa, a prawdopodobieństwo zablokowania wzrasta.

 81
Author: ewernli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-07 12:17:53

Jest prawdopodobne, że polecenie delete wpłynie na dużą część wszystkich wierszy w tabeli. W końcu może to doprowadzić do uzyskania blokady tabeli podczas usuwania. Trzymanie się blokady (w tym przypadku blokada wiersza lub strony) i uzyskanie większej liczby blokad jest zawsze ryzykiem impasu. Jednak nie mogę wyjaśnić, dlaczego instrukcja insert prowadzi do eskalacji blokady - może to mieć związek z dzieleniem/dodawaniem stron, ale ktoś znający lepiej MySQL będzie musiał tam wypełnić.

Na początek warto spróbować od razu uzyskać blokadę tabeli dla instrukcji delete. Zobacz blokowanie tabeli problemy z blokowaniem tabel .

 12
Author: Anders Abel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-22 17:28:51

Na wypadek, gdyby ktoś nadal zmagał się z tym problemem:

Napotkałem podobny problem, w którym 2 żądania trafiały na serwer w tym samym czasie. Nie było takiej sytuacji jak poniżej:

T1:
    BEGIN TRANSACTION
    INSERT TABLE A
    INSERT TABLE B
    END TRANSACTION

T2:
    BEGIN TRANSACTION
    INSERT TABLE B
    INSERT TABLE A
    END TRANSACTION
/ Align = "left" /

Potem okazało się, że nie było relacji rodzic dziecko statek między 2 tabel z powodu obcego klucza. Kiedy wstawiałem rekord w tabeli potomnej, transakcja uzyskiwała blokadę w wierszu tabeli nadrzędnej. Natychmiast po tym byłem próba aktualizacji nadrzędnego wiersza, który wyzwalał elewację blokady na wyłączną. Ponieważ druga jednoczesna transakcja już posiadała współdzieloną blokadę, powodowała ona impas.

Zobacz: https://blog.tekenlight.com/2019/02/21/database-deadlock-mysql.html

 10
Author: chatsap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-26 13:41:54

Możesz spróbować, aby to zadanie delete działało, najpierw wstawiając klucz każdego wiersza do usunięcia do tabeli tymczasowej, takiej jak ten pseudokod

create temporary table deletetemp (userid int);

insert into deletetemp (userid)
  select userid from onlineusers where datetime <= now - interval 900 second;

delete from onlineusers where userid in (select userid from deletetemp);

Łamanie go w ten sposób jest mniej efektywne, ale pozwala uniknąć konieczności trzymania zamka na klucz podczas delete.

Również zmodyfikuj swoje zapytania select, aby dodać klauzulę where z wyłączeniem wierszy starszych niż 900 sekund. Pozwala to uniknąć zależności od zadania cron i pozwala przełożyć go na uruchamianie rzadziej.

Teoria co do impasów: nie mam zbyt wiele tła w MySQL, ale tutaj idzie... delete będzie trzymać blokadę zakresu klawiszy dla datetime, aby zapobiec dodaniu wierszy pasujących do klauzuli where w środku transakcji, a gdy znajdzie wiersze do usunięcia, spróbuje uzyskać blokadę na każdej stronie, którą modyfikuje. insert uzyska blokadę na stronie, do której się wstawia, a następnie spróbuje uzyskać blokadę klucza. Normalnie {[7] } będzie cierpliwie czekać na blokada klawisza zostanie zablokowana, jeśli delete spróbuje zablokować tę samą stronę, której używa insert, ponieważdelete potrzebuje tej blokady, a insert tej blokady klawisza. Nie wydaje się to jednak właściwe dla wstawek, delete i insert używają zakresów datetime, które nie nakładają się na siebie, więc może dzieje się coś innego.

Http://dev.mysql.com/doc/refman/5.1/en/innodb-next-key-locking.html

 6
Author: Brian Sandlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-11 16:26:47

Dla programistów Java używających Springa uniknąłem tego problemu, używając aspektu AOP, który automatycznie powtarza transakcje, które napotykają przejściowe impasy.

Zobacz @RetryTransaction Javadoc po więcej informacji.

 4
Author: Archie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-19 17:32:26

Mam metodę, której wewnętrzne elementy są zawinięte w MySqlTransaction.

Problem impasu pojawił się dla mnie, gdy uruchomiłem tę samą metodę równolegle z sobą.

Nie wystąpił problem z uruchomieniem pojedynczej instancji metody.

Kiedy usunąłem MySqlTransaction, byłem w stanie uruchomić metodę równolegle z samym sobą bez żadnych problemów.

Dzieląc się swoim doświadczeniem, niczego nie popieram.

 1
Author: BitsAndBytes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-04 13:29:44

cron to niebezpieczne. Jeśli jedna instancja cron nie zakończy się przed następną, prawdopodobnie będą walczyć ze sobą.

Lepiej byłoby mieć stale uruchomione zadanie, które usuwałoby niektóre wiersze, usypiało niektóre, a następnie powtarzało.

Również, INDEX(datetime) jest bardzo ważne, aby uniknąć impasu.

Ale jeśli test datetime zawiera więcej niż, powiedzmy, 20% tabeli, DELETE wykona skanowanie tabeli. Mniejsze kawałki usuwane częściej jest obejściem.

Inny powodem przechodzenia z mniejszymi kawałkami jest blokowanie mniejszej liczby rzędów.

Dolna linia:

  • INDEX(datetime)
  • ciągłe uruchamianie zadania -- delete, sleep a minute, repeat.
  • aby upewnić się, że powyższe zadanie nie umarło, wykonaj zadanie cron, którego jedynym celem jest ponowne uruchomienie go po niepowodzeniu.

Inne techniki usuwania: http://mysql.rjweb.org/doc.php/deletebig

 0
Author: Rick James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-05 21:27:26