mysqldump - jedna transakcja, ale zapytania dotyczące aktualizacji czekają na kopię zapasową

10

Jeśli użyję mysqldump --single-transakcja, zgodnie z dokumentami powinna ona robić flush tabele z blokadą odczytu, aby uzyskać spójny stan, a następnie rozpocząć transakcję i żaden pisarz nie powinien czekać.

Jednak ostatniej nocy złapałem następującą sytuację:

fragment z pełnej listy procesów:

setki tych ...

   Command: Query
   Time: 291
   State: Waiting for table flush
   Info: insert into db_external_notification.....

wtedy to:

Command: Query
Time: 1204
State: Sending data
Info: SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`

a reszta wątków jest w trybie uśpienia

czy ktoś ma pojęcie na co czekają te wkładki? Nie widzę żadnych tabel FLUSH, DDL ani niczego wspomnianego w instrukcji, które mogą powodować, że zapytania będą czekać.

pełne polecenie mysqldump

mysqldump --quick --add-drop-table --single-transaction --master-data=2 -uxx -pxx dbname

Myślę, że - szybkie tutaj jest zbędne, prawdopodobnie pozostałość po wcześniejszych czasach, ten skrypt jest bardzo stary, ale nie powinien nic zranić

Aleksandar Ivanisevic
źródło
pełne wyjście show full processlist i show innodb (anonimowe) jest tutaj: pastebin.com/D7WS3QAE
Aleksandar Ivanisevic
Do czego służy pełna linia poleceń mysqldump? W szczególności używasz --flush-logslub --master-data...? Istnieją potencjalne interakcje między opcjami.
Michael - sqlbot
dodano pełne polecenie mysqldump, dzięki za spojrzenie
Aleksandar Ivanisevic

Odpowiedzi:

6

--Single-transakcja opcja mysqldump nie robi FLUSH TABLES WITH READ LOCK;. Powoduje to, że mysqldump konfiguruje powtarzalną transakcję odczytu dla wszystkich zrzutowanych tabel.

W swoim pytaniu stwierdziłeś, że SELECT mysqldump dla db_external_notificationtabeli wstrzymuje setki poleceń INSERT dla tej samej tabeli. Dlaczego to się dzieje ?

Najprawdopodobniej blokada na indeksie gen_clust_index (lepiej znanym jako Indeks klastrowany). Ten paradygmat powoduje współistnienie danych i stron indeksu dla tabeli. Te strony indeksowe są oparte na KLUCZU PODSTAWOWYM lub na automatycznie wygenerowanym indeksie RowID (w przypadku braku KLUCZA PODSTAWOWEGO).

Powinieneś być w stanie to zauważyć, uruchamiając SHOW ENGINE INNODB STATUS\Gi szukając dowolnej strony z gen_clust_index, która ma wyłączną blokadę. Wprowadzanie WSTAWEK do tabeli z indeksem klastrowanym wymaga wyłącznej blokady do obsługi BTREE KLUCZA PODSTAWOWEGO, a także serializacji przyrostu automatycznego.

Omawiałem to zjawisko wcześniej

AKTUALIZACJA 2014-07-21 15:03 EDT

Proszę spojrzeć na linie 614-617 twojego PastBin

mysql tables in use 1, locked 0
MySQL thread id 6155315, OS thread handle 0x85f11b70, query id 367774810 localhost root Sending data
SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`
Trx read view will not see trx with id >= 1252538405, sees < 1252538391

Zauważ, że wiersz 617 mówi

Trx read view will not see trx with id >= 1252538405, sees < 1252538391

Co mi to mówi? Masz jakiś KLUCZ PODSTAWOWY z włączonym auto_increment id.

Twoje maksimum iddla tabeli db_external_notificationbyło mniejsze niż w 1252538391momencie uruchomienia mysqldump. Gdy odejmiemy 1252538391od 1252538405, oznacza to, że 14 lub więcej poleceń INSERT zostały prób. Wewnętrznie musiałoby to przesunąć auto_increment tej tabeli co najmniej 14 razy. Jednak nic nie można zatwierdzić ani nawet wepchnąć do bufora dziennika z powodu zarządzania tą idluką.

Teraz spójrz na listę procesów z Twojego PasteBin. O ile nie przeliczyłem, zobaczyłem 38 połączeń DB wykonujących WSTAW (19 Przed procesem mysqldump (identyfikator procesu 6155315), 19 Po). Jestem pewien, że 14 lub więcej takich połączeń zostało zawieszonych z powodu zarządzania luką auto_increment.

RolandoMySQLDBA
źródło
Długo szukałem i nie mogłem znaleźć żadnych ekskluzywnych zamków. wkleiłem status pełnego pokazu innodb na pastebin.com/D7WS3QAE , nic dla mnie nie wygląda na wyłączną blokadę
Aleksandar Ivanisevic
Dziękuję za wyjaśnienie. Zastanawiam się, dlaczego nie używają transakcji tylko do odczytu, ponieważ jest oczywiste, że kopia zapasowa nigdy nie zapisze, ale domyślam się, że zachowują tę funkcję do tworzenia kopii zapasowych dla przedsiębiorstw.
Aleksandar Ivanisevic
10

--single-transactionOpcja mysqldump nie zrobić FLUSH TABLES WITH READ LOCKprzed rozpoczęciem zadania kopii zapasowej , ale tylko pod pewnymi warunkami. Jednym z tych warunków jest podanie --master-dataopcji.

W kodzie źródłowym z mysql-5.6.19/client/mysqldump.cwiersza 5797:

if ((opt_lock_all_tables || opt_master_data ||
     (opt_single_transaction && flush_logs)) &&
    do_flush_tables_read_lock(mysql))
  goto err;

Aby uzyskać solidną blokadę dokładnych współrzędnych binlog przed rozpoczęciem transakcji z powtarzalnym odczytem, --master-dataopcja wyzwala tę blokadę, a następnie zostaje zwolniona po uzyskaniu współrzędnych binlog.

W rzeczywistości mysqldumprobi FLUSH TABLESnastępnie przez FLUSH TABLES WITH READ LOCKbo robi obie rzeczy umożliwia odczyt blokady należy uzyskać szybciej w przypadku, gdy początkowa równo zajmuje trochę czasu.

...jednak...

Jak tylko uzyska współrzędne binlog, mysqldumpwydaje UNLOCK TABLESpolecenie, więc nic nie powinno blokować w wyniku rozpoczętego koloru. Żaden wątek nie powinien być Waiting for table flushrównież wynikiem wstrzymanej transakcji mysqldump.

Gdy zobaczysz wątek w Waiting for table flushstanie, powinno to oznaczać, że FLUSH TABLES [WITH READ LOCK]instrukcja została wydana i nadal działała w momencie rozpoczęcia zapytania - więc zapytanie musi poczekać na opróżnienie tabeli, zanim będzie mogło zostać wykonane. W przypadku opublikowanej listy procesów mysqldumpczyta się z tej samej tabeli, a zapytanie działa od jakiegoś czasu, ale zapytania blokujące nie blokowały się tak długo.

To wszystko sugeruje, że wydarzyło się coś innego.

Istnieje wewnętrzny problem wyjaśniony w błędzie nr 44884 dotyczący sposobu FLUSH TABLESdziałania wewnętrznego. Nie zdziwiłbym się, gdyby problem nadal występował, byłbym zaskoczony, gdyby problem ten został kiedykolwiek „rozwiązany”, ponieważ jest to bardzo złożony problem do rozwiązania - praktycznie niemożliwy do naprawienia w środowisku o wysokiej współbieżności - i każda próba naprawienie go niesie ze sobą znaczne ryzyko złamania czegoś innego lub stworzenia nowego, innego i wciąż niepożądanego zachowania.

Wydaje się prawdopodobne, że będzie to wyjaśnienie tego, co widzisz.

Konkretnie:

  • jeśli masz długo działające zapytanie działające na tabeli i problem FLUSH TABLES, FLUSH TABLESblokuje się, dopóki długo nie zakończy się zapytanie.

  • dodatkowo wszelkie zapytania rozpoczynające się po FLUSH TABLESwydaniu będą blokowane do momentu FLUSH TABLESzakończenia.

  • dodatkowo, jeśli zabijesz FLUSH TABLESzapytanie, blokowane zapytania będą nadal blokować oryginalne długo działające zapytanie, które blokowało FLUSH TABLESzapytanie, ponieważ mimo że zabite FLUSH TABLESzapytanie nie zakończyło się, ta tabela (ta lub więcej, zaangażowany w długo działające zapytanie) jest wciąż w trakcie opróżniania, a to oczekujące opróżnienie nastąpi zaraz po zakończeniu długo działającego zapytania - ale nie wcześniej.

Prawdopodobnym wnioskiem tutaj jest to, że inny proces - być może inny mysqldump, niewłaściwe zapytanie lub źle napisany proces monitorowania próbował opróżnić tabelę.

To zapytanie zostało następnie zabite lub przekroczone przez nieznany mechanizm, ale jego następstwa utrzymywały się aż do mysqldumpzakończenia odczytu z tabeli, o której mowa.

Możesz zreplikować ten warunek, próbując wykonać FLUSH TABLESpodczas długotrwałego zapytania. Następnie uruchom kolejne zapytanie, które zostanie zablokowane. Następnie zabij FLUSH TABLESzapytanie, które nie odblokuje ostatniego zapytania. Następnie zabij pierwsze zapytanie lub pozwól mu zakończyć, a ostatnie zapytanie zostanie pomyślnie uruchomione.


W związku z tym nie ma to związku:

Trx read view will not see trx with id >= 1252538405, sees < 1252538391

Jest to normalne, ponieważ mysqldump --single-transactionproblemy a START TRANSACTION WITH CONSISTENT SNAPSHOT, które uniemożliwiają zrzut danych, które zostały zmienione podczas zrzutu. Bez tego współrzędne binlog uzyskane na początku byłyby bez znaczenia, ponieważ --single-transactionnie byłyby tym, za co się podaje. Nie powinno to być w żaden sposób związane z Waiting for table flushproblemem, ponieważ transakcja ta oczywiście nie zawiera żadnych blokad.

Michael - sqlbot
źródło
Ta odpowiedź jest właściwie poprawna.
Boban P.
2

Przesłałem żądanie funkcji: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902 .

Napisałem również poprawkę do 5.6.37, która używa tej samej metody co - pojedyncza transakcja - kombinacja danych master z - pojedyncza transakcja - slave danych, która jest dostarczana bez gwarancji. Używaj na własne ryzyko.

--- mysql-5.6.37/client/mysqldump.c.bak 2017-11-14 12:24:41.846647514 -0600
+++ mysql-5.6.37/client/mysqldump.c 2017-11-14 14:17:51.187050091 -0600
@@ -4900,10 +4900,10 @@
   return 0;
 }

+/*
 static int do_stop_slave_sql(MYSQL *mysql_con)
 {
   MYSQL_RES *slave;
-  /* We need to check if the slave sql is running in the first place */
   if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS"))
     return(1);
   else
@@ -4911,23 +4911,21 @@
     MYSQL_ROW row= mysql_fetch_row(slave);
     if (row && row[11])
     {
-      /* if SLAVE SQL is not running, we don't stop it */
       if (!strcmp(row[11],"No"))
       {
         mysql_free_result(slave);
-        /* Silently assume that they don't have the slave running */
         return(0);
       }
     }
   }
   mysql_free_result(slave);

-  /* now, stop slave if running */
   if (mysql_query_with_error_report(mysql_con, 0, "STOP SLAVE SQL_THREAD"))
     return(1);

   return(0);
 }
+*/

 static int add_stop_slave(void)
 {
@@ -5841,10 +5839,12 @@
   if (!path)
     write_header(md_result_file, *argv);

+  /*
   if (opt_slave_data && do_stop_slave_sql(mysql))
     goto err;
+  */

-  if ((opt_lock_all_tables || opt_master_data ||
+  if ((opt_lock_all_tables || opt_master_data || opt_slave_data ||
        (opt_single_transaction && flush_logs)) &&
       do_flush_tables_read_lock(mysql))
     goto err;
@@ -5853,7 +5853,7 @@
     Flush logs before starting transaction since
     this causes implicit commit starting mysql-5.5.
   */
-  if (opt_lock_all_tables || opt_master_data ||
+  if (opt_lock_all_tables || opt_master_data || opt_slave_data ||
       (opt_single_transaction && flush_logs) ||
       opt_delete_master_logs)
   {
 static int add_stop_slave(void)
 {
@@ -5841,10 +5839,12 @@
   if (!path)
     write_header(md_result_file, *argv);

+  /*
   if (opt_slave_data && do_stop_slave_sql(mysql))
     goto err;
+  */

-  if ((opt_lock_all_tables || opt_master_data ||
+  if ((opt_lock_all_tables || opt_master_data || opt_slave_data ||
        (opt_single_transaction && flush_logs)) &&
       do_flush_tables_read_lock(mysql))
     goto err;
@@ -5853,7 +5853,7 @@
     Flush logs before starting transaction since
     this causes implicit commit starting mysql-5.5.
   */
-  if (opt_lock_all_tables || opt_master_data ||
+  if (opt_lock_all_tables || opt_master_data || opt_slave_data ||
       (opt_single_transaction && flush_logs) ||
       opt_delete_master_logs)
   {

Przetestowałem to w następujący sposób z niewolnikami do bardzo zajętego mistrza, używając wielu tabel InnoDB z relacjami FK:

  1. Zatrzymaj niewolnika A.
  2. Poczekaj ~ 15 minut.
  3. Zrzuć DB 1 z urządzenia podrzędnego B z opcją --single-transaction i --dump-slave = 2
  4. Uruchom slave A, aż współrzędne zrzucą z kroku 3.
  5. Upuść DB 1 i 2 z slave A.
  6. Utwórz puste DB 1 i 2 na slave A.
  7. Załaduj zrzut z kroku 3 do urządzenia podrzędnego A.
  8. Zrzuć DB 2 ze slave B z tymi samymi opcjami. DB 2 ma powiązania FK z DB 1.
  9. Dodaj replicate_ignore_db dla DB 2 i skip_slave_start na slave A.
  10. Zrestartuj slave A.
  11. Uruchom slave do współrzędnych ze zrzutu w kroku 8 na slave A.
  12. Załaduj zrzut z kroku 8 do urządzenia podrzędnego A.
  13. Usuń opcje replicate_ignore_db i skip_slave_start z slave A.
  14. Zrestartuj slave A.
  15. Poczekaj ~ 1 tydzień.
  16. Użyj sumy kontrolnej pt, aby sprawdzić integralność danych.

Proces przesyłania poprawek przez Oracle jest dość intensywny, dlatego wybrałem tę drogę. Mogę spróbować z Perconą i / lub MariaDB, aby ją zintegrować.

użytkownik44127
źródło