Mysql
 sql >> Baza danych >  >> RDS >> Mysql

MySQL:Trwałe oczekiwanie na blokadę metadanych tabeli

Przyjęte rozwiązanie jest niestety złe . To prawda, o ile mówi,

To rzeczywiście (prawie pewno; patrz poniżej), co robić. Ale wtedy sugeruje,

...a 1398 nie połączenie z zamkiem. Jak to mogło się stać? 1398 to połączenie czekające do zamka. Oznacza to, że jeszcze nie ma zamek, a zatem jego zabicie nic nie daje. Proces utrzymujący blokadę nadal będzie blokować, a następny wątek próbujący coś zrobić będzie więc również zatrzymaj się i wpisz „Oczekiwanie na blokadę metadanych” w odpowiedniej kolejności.

Nie masz gwarancji, że procesy „oczekujące na blokadę metadanych” (WFML) również się nie zablokują, ale możesz być pewien, że zabicie samych procesów WFML nie da dokładnie nic .

Prawdziwą przyczyną jest to, że inny proces wstrzymuje blokadę , a co ważniejsze, SHOW FULL PROCESSLIST nie powie Ci bezpośrednio, który to jest .

BĘDZIE powiedzieć, czy proces działa coś, tak. Zwykle to działa. Tutaj proces trzymający blokadę nic nie robi i ukrywa się wśród innych wątków, również nic nie robiąc.

W tym przypadku winowajcą jest prawie na pewno proces 1396 , który rozpoczął się przed procesem 1398 i jest teraz w Sleep stan i trwa od 46 sekund. Od 1396 wyraźnie robił wszystko, co musiał (o czym świadczy fakt, że teraz śpi i robił to przez 46 sekund, jeśli chodzi o MySQL ), żaden wątek, który wcześniej poszedł spać, nie mógł utrzymać blokady (lub 1396 również by się zatrzymało).

WAŻNE :jeśli łączyłeś się z MySQL jako użytkownik z ograniczeniami, SHOW FULL PROCESSLIST nie pokaż wszystkie procesy. Więc blokada może być utrzymywana przez proces, którego nie widzisz.

Lepsza SHOW PROCESSLIST

SELECT ID, TIME, USER, HOST, DB, COMMAND, STATE, INFO
    FROM INFORMATION_SCHEMA.PROCESSLIST WHERE DB IS NOT NULL
    AND (`INFO` NOT LIKE '%INFORMATION_SCHEMA%' OR INFO IS NULL)
    ORDER BY `DB`, `TIME` DESC

Powyższe można dostroić tak, aby pokazywały tylko procesy w stanie SLEEP, a mimo to posortuje je według czasu malejąco, więc łatwiej jest znaleźć proces, który się zawiesza (zwykle jest to Sleep 'jeden bezpośrednio przed tymi, które "czekają na blokadę metadanych").

Ważna rzecz

Pozostaw każdy proces „oczekiwania na blokadę metadanych” w spokoju .

Szybkie i brudne rozwiązanie, niezbyt zalecane, ale szybkie

Zabij wszystkich procesy w stanie „Uśpienie” w tej samej bazie danych, które są starsze niż najstarsze wątek w stanie „oczekiwanie na blokadę metadanych”. To właśnie Arnaud Amaury zrobiłby:

  • dla każdej bazy danych, która ma co najmniej jeden wątek w WaitingForMetadataLock:
    • Okazuje się, że najstarsze połączenie w WFML w tej bazie danych ma Z sekund
    • WSZYSTKIE wątki „Sleep” w tym DB i starsze niż Z muszą zniknąć. Zacznij od najświeższych, na wszelki wypadek.
    • Jeżeli w tym DB istnieje jedno starsze i nieuśpione połączenie, to może to właśnie ono trzyma blokadę, ale coś robi . Możesz oczywiście go zabić, ale zwłaszcza jeśli jest to UPDATE/INSERT/DELETE, robisz to na własne ryzyko.

Dziewięćdziesiąt dziewięć razy na sto nić do zabicia jest najmłodsza wśród osób w stanie uśpienia, które są starsze niż starszy oczekujący na blokadę metadanych:

TIME     STATUS
319      Sleep
205      Sleep
 19      Sleep                      <--- one of these two "19"
 19      Sleep                      <--- and probably this one(*)
 15      Waiting for metadata lock  <--- oldest WFML
 15      Waiting for metadata lock
 14      Waiting for metadata lock

(*) kolejność TIME faktycznie ma milisekundy, a przynajmniej tak mi powiedziano, po prostu ich nie pokazuje. Tak więc, podczas gdy oba procesy mają wartość czasu 19, najniższy powinien być młodszy.

Bardziej ukierunkowane rozwiązanie

Uruchom SHOW ENGINE INNODB STATUS i spójrz na sekcję „TRANSAKCJA”. Znajdziesz m.in. coś takiego

TRANSACTION 1701, ACTIVE 58 sec;2 lock struct(s), heap size 376, 1 row lock(s), undo log entries 1
MySQL thread id 1396, OS thread handle 0x7fd06d675700, query id 1138 hostname 1.2.3.4 whatever;

Teraz możesz sprawdzić za pomocą SHOW FULL PROCESSLIST co robi wątek id 1396 z transakcją #1701. Są szanse, że jest w stanie "Sleep". A więc:aktywna transakcja (nr 1701) z aktywną blokadą, wprowadziła nawet pewne zmiany, ponieważ ma wpis w dzienniku cofania... ale jest obecnie bezczynna. To i żaden inny nie jest wątkiem, który musisz zabić. Utrata tych zmian.

Pamiętaj, że nie robienie niczego w MySQL nie oznacza generalnie niczego. Jeśli uzyskasz jakieś rekordy z MySQL i zbudujesz plik CSV do przesyłania FTP, podczas przesyłania FTP połączenie MySQL jest bezczynne.

W rzeczywistości, jeśli proces używający MySQL i serwer MySQL znajdują się na tym samym komputerze, na tym komputerze działa Linux i masz uprawnienia roota, istnieje sposób, aby dowiedzieć się, który proces ma połączenie, które zażądało blokady. To z kolei pozwala określić (na podstawie wykorzystania procesora lub, w najgorszym przypadku, strace -ff -p pid ) czy ten proces jest naprawdę robienie czegoś lub nie, aby pomóc zdecydować, czy można bezpiecznie zabić.

Dlaczego tak się dzieje?

Widzę, że dzieje się tak w przypadku aplikacji internetowych, które używają „trwałych” lub „zgrupowanych” połączeń MySQL, co w dzisiejszych czasach zwykle oszczędza bardzo mało czasu:instancja aplikacji internetowej została zakończona, ale połączenie nie , więc jego zamek wciąż żyje... i blokuje wszystkich innych.

Kolejny interesujący sposób znalazłem, w powyższych hipotezach, aby uruchomić zapytanie zwracające niektóre wiersze, i pobrać tylko niektóre z nich . Jeśli zapytanie nie jest ustawione na „automatyczne czyszczenie” (niezależnie od tego, czy bazowy administrator DBA to robi), utrzyma połączenie i zapobiegnie przejściu pełnej blokady tabeli. Zdarzyło mi się to w kawałku kodu, który zweryfikował, czy wiersz istnieje, wybierając ten wiersz i sprawdzając, czy otrzymał błąd (nie istnieje), czy nie (musi istnieć), ale bez faktycznego pobierania wiersza .

Zapytaj DB

Inny sposób na złapanie winowajcy, jeśli masz najnowszy MySQL, ale nie za nowy ponieważ zostanie wycofane , jest (znowu potrzebujesz uprawnień w schemacie informacyjnym)

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS 
     WHERE LOCK_TRX_ID IN 
        (SELECT BLOCKING_TRX_ID FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS);

Rzeczywiste rozwiązanie, wymagające czasu i pracy

Problem jest zwykle spowodowany przez tę architekturę:

Gdy umiera aplikacja internetowa lub instancja lekkiego wątku aplikacji internetowej, pula kontenerów/połączeń może nie działać . I to jest kontener to utrzymuje połączenie otwarte, więc oczywiście połączenie się nie zamyka. Całkiem przewidywalnie, MySQL nie uważa operacji za zakończoną .

Jeśli aplikacja internetowa nie wyczyściła się po sobie (brak ROLLBACK lub COMMIT dla transakcji, nie UNLOCK TABLES itp.), to cokolwiek ta aplikacja zaczęła robić jest nadal aktualne i nadal może blokować wszystkich innych.

Są więc dwa rozwiązania. Najgorsze jest obniżenie limitu czasu bezczynności . Ale zgadnij, co się stanie, jeśli będziesz czekać zbyt długo między dwoma zapytaniami (dokładnie:„Serwer MySQL zniknął”). Możesz wtedy użyć mysql_ping jeśli dostępne (wkrótce zostanie wycofane. Istnieją obejścia dla ChNP. Lub możesz sprawdzić, to błąd i ponownie otwórz połączenie, jeśli tak się stanie (jest to sposób Pythona). Tak więc – za niewielką opłatą za wyniki – jest to wykonalne.

Lepsze, inteligentniejsze rozwiązanie jest mniej proste do wdrożenia. Postaraj się, aby skrypt po sobie posprzątał, zapewniając pobranie wszystkich wierszy lub zwolnienie wszystkich zasobów zapytań, przechwycenie wszystkich wyjątków i poprawną obsługę lub, jeśli to możliwe, całkowite pominięcie trwałych połączeń . Pozwól każdej instancji utworzyć własne połączenie lub użyj inteligentnego kierowca basenu (w PHP PDO użyj PDO::ATTR_PERSISTENT jawnie ustawione na false ). Alternatywnie (np. w PHP) możesz mieć obsługę destruct i wyjątków wymuszających wyczyszczenie połączenia przez zatwierdzanie lub wycofywanie transakcji i wydawanie jawnych odblokowań tabeli.

Nie znam sposobu zapytania o istniejące zasoby zestawu wyników w celu ich uwolnienia; jedynym sposobem byłoby zapisanie te zasoby w prywatnej tablicy.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Konwertuj złożone zapytanie SQL na SQLAlchemy

  2. Wdróż bazę danych MySQL w chmurze hybrydowej za pomocą ClusterControl

  3. JPA lub Hibernate, aby wygenerować wartość kolumny (nie klucz podstawowy), nie zaczynając od 1

  4. Czy sortowanie InnoDB naprawdę jest TAK wolne?

  5. Tabela nie ma klucza podstawowego