mysqldump - Einzeltransaktion, aber Aktualisierungsabfragen warten auf die Sicherung


10

Wenn ich mysqldump --single-transaction verwende, sollte es laut den Dokumenten Tabellen mit Lesesperre leeren, um einen konsistenten Status zu erhalten, und dann eine Transaktion starten, und es sollten keine Writer warten.

Ich habe jedoch letzte Nacht die folgende Situation festgestellt:

Auszug aus der vollständigen Prozessliste anzeigen:

Hunderte von denen ...

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

dann das:

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

und der Rest der Threads ist im Schlaf

Hat jemand eine Idee, worauf diese Beilagen warten? Ich sehe keine FLUSH-Tabellen oder DDL oder irgendetwas im Handbuch Erwähntes, das dazu führen kann, dass die Abfragen warten.

vollständiger Befehl mysqldump

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

Ich denke - schnell ist hier überflüssig, wahrscheinlich ein Überbleibsel aus früheren Zeiten. Dieses Skript ist sehr alt, sollte aber nichts schaden


Die vollständige Ausgabe von show full processlist und show innodb status (anonymisiert) finden Sie hier: pastebin.com/D7WS3QAE
Aleksandar Ivanisevic

Wofür ist Ihre vollständige Befehlszeile mysqldump? Verwenden Sie insbesondere --flush-logsoder --master-data...? Es gibt mögliche Wechselwirkungen zwischen den Optionen.
Michael - sqlbot

Fügte den vollständigen Befehl mysqldump hinzu, danke fürs Schauen
Aleksandar Ivanisevic

Antworten:


6

Die Option --single-transaction von mysqldump funktioniert nicht FLUSH TABLES WITH READ LOCK;. Dadurch richtet mysqldump eine wiederholbare Lesetransaktion für alle Tabellen aus, die gesichert werden.

In Ihrer Frage haben Sie angegeben, dass SELECT für die db_external_notificationTabelle von mysqldump Hunderte von INSERT-Befehlen für dieselbe Tabelle enthält. Warum passiert das ?

Am wahrscheinlichsten ist eine Sperre für den gen_clust_index (besser bekannt als Clustered Index). Dieses Paradigma bewirkt, dass Daten- und Indexseiten für eine Tabelle nebeneinander existieren. Diese Indexseiten basieren entweder auf dem PRIMARY KEY oder einem automatisch generierten RowID-Index (falls es keinen PRIMARY KEY gibt).

Sie sollten dies erkennen können, indem Sie SHOW ENGINE INNODB STATUS\Geine Seite aus dem gen_clust_index mit einer exklusiven Sperre ausführen und suchen . Um INSERTs in eine Tabelle mit einem Clustered-Index einzufügen, ist eine exklusive Sperre für die Behandlung des BTREE des PRIMARY KEY sowie für die Serialisierung des auto_increment erforderlich.

Ich habe dieses Phänomen bereits diskutiert

UPDATE 2014-07-21 15:03 EDT

Bitte beachten Sie die Zeilen 614-617 Ihres 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

Beachten Sie, dass in Zeile 617 steht

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

Was sagt mir das? Sie haben einen PRIMARY KEY mit einem auto_increment aktiviert id.

Ihr Maximum idfür die Tabelle db_external_notificationwar geringer als 1252538391beim Start von mysqldump. Wenn Sie subtrahieren 1252538391aus 1252538405, bedeutet dies , dass 14 oder mehr INSERT Befehle versucht. Intern müsste das auto_increment dieser Tabelle mindestens 14 Mal verschoben werden. Aufgrund der Verwaltung dieser idLücke kann jedoch nichts festgeschrieben oder sogar in den Protokollpuffer verschoben werden .

Schauen Sie sich jetzt die Prozessliste in Ihrem PasteBin an. Sofern ich nicht falsch gezählt habe, habe ich 38 DB-Verbindungen gesehen, die ein INSERT ausgeführt haben (19 vor dem mysqldump-Prozess (Prozess-ID 6155315), 19 danach). Ich bin sicher, dass 14 oder mehr dieser Verbindungen aufgrund der Verwaltung der Lücke auto_increment eingefroren sind.


Ich habe lange gesucht und konnte keine exklusiven Schlösser finden. Ich habe die volle Show innodb Status bei klebte pastebin.com/D7WS3QAE , nichts da sieht aus wie exklusive Sperre auf irgendetwas zu mir
Aleksandar Ivanisevic

Danke für die Klarstellung. Ich frage mich, warum sie keine schreibgeschützte Transaktion verwenden, da klar ist, dass Backups niemals geschrieben werden, aber ich vermute, dass sie diese Funktion für ihre Unternehmenssicherungen beibehalten.
Aleksandar Ivanisevic

10

Die --single-transactionOption mysqldump führt eine FLUSH TABLES WITH READ LOCKAktion vor dem Starten des Sicherungsjobs durch, jedoch nur unter bestimmten Bedingungen. Eine dieser Bedingungen ist, wenn Sie auch die --master-dataOption angeben .

Im Quellcode aus mysql-5.6.19/client/mysqldump.cZeile 5797:

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

Um eine solide Sperre für die genauen Binlog-Koordinaten zu erhalten, bevor die Transaktion mit wiederholbarem Lesen gestartet wird, --master-datalöst die Option aus, dass diese Sperre abgerufen und dann freigegeben wird, sobald die Binlog-Koordinaten erhalten wurden.

Tatsächlich mysqldumpwird a FLUSH TABLESgefolgt von a ausgeführt, FLUSH TABLES WITH READ LOCKda in beiden Fällen die Lesesperre in Fällen, in denen das anfängliche Spülen einige Zeit dauert, schneller erreicht werden kann.

...jedoch...

Sobald es die binlog-Koordinaten erhalten hat, mysqldumpgibt es eine UNLOCK TABLESAnweisung aus, sodass aufgrund des von Ihnen gestarteten Flushs nichts blockiert werden sollte. Threads sollten auch nicht das Waiting for table flushErgebnis der Transaktion sein, die gehalten mysqldumpwird.

Wenn Sie einen Thread im Waiting for table flushStatus sehen, sollte dies bedeuten, dass die FLUSH TABLES [WITH READ LOCK]Anweisung ausgegeben wurde und zum Zeitpunkt des Starts der Abfrage noch ausgeführt wurde. Daher muss die Abfrage auf das Löschen der Tabelle warten, bevor sie ausgeführt werden kann. Bei der von Ihnen veröffentlichten Prozessliste mysqldumpwird aus derselben Tabelle gelesen, und die Abfrage wurde eine Weile ausgeführt, die blockierenden Abfragen wurden jedoch nicht so lange blockiert.

Dies alles deutet darauf hin, dass etwas anderes passiert ist.

In Bug # 44884 wird ein langjähriges Problem mit der FLUSH TABLESinternen Funktionsweise erläutert . Ich wäre nicht überrascht, wenn das Problem weiterhin besteht. Ich wäre überrascht, wenn dieses Problem jemals "behoben" würde, da es sich um ein sehr komplexes Problem handelt, das in einer Umgebung mit hoher Parallelität praktisch unmöglich zu beheben ist Das Reparieren birgt ein erhebliches Risiko, etwas anderes zu beschädigen oder neues, anderes und immer noch unerwünschtes Verhalten zu erzeugen.

Es ist wahrscheinlich, dass dies die Erklärung für das ist, was Sie sehen.

Speziell:

  • Wenn eine Abfrage mit langer Laufzeit für eine Tabelle ausgeführt wird und ein Problem auftritt FLUSH TABLES, FLUSH TABLESwird die Abfrage blockiert, bis die Abfrage mit langer Laufzeit abgeschlossen ist.

  • Darüber hinaus werden alle Abfragen, die nach der FLUSH TABLESAusgabe beginnen, blockiert, bis die FLUSH TABLESabgeschlossen ist.

  • Wenn Sie die FLUSH TABLESAbfrage beenden, werden die blockierenden Abfragen weiterhin für die ursprüngliche Abfrage mit langer Laufzeit blockiert, die die FLUSH TABLESAbfrage blockiert hat , da FLUSH TABLESdiese Tabelle (die eine oder Darüber hinaus wird die lange laufende Abfrage noch geleert, und diese anstehende Leerung wird stattfinden, sobald die lang laufende Abfrage abgeschlossen ist - jedoch nicht vorher.

Die wahrscheinliche Schlussfolgerung hier ist, dass ein anderer Prozess - vielleicht ein anderer mysqldump oder eine schlecht beratene Abfrage oder ein schlecht geschriebener Überwachungsprozess - versucht hat, eine Tabelle zu leeren.

Diese Abfrage wurde später durch einen unbekannten Mechanismus beendet oder abgelaufen, aber ihre Nachwirkungen blieben bestehen, bis mysqldumpdas Lesen aus der fraglichen Tabelle abgeschlossen war.

Sie können diese Bedingung replizieren, indem Sie versuchen, FLUSH TABLESwährend eine lang laufende Abfrage ausgeführt wird. Starten Sie dann eine weitere Abfrage, die blockiert wird. Beenden Sie dann die FLUSH TABLESAbfrage, wodurch die letzte Abfrage nicht entsperrt wird. Beenden Sie dann die erste Abfrage oder lassen Sie sie beenden, und die letzte Abfrage wird erfolgreich ausgeführt.


Nachträglich hat dies nichts zu tun:

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

Das ist normal, da mysqldump --single-transactionProbleme a auftreten START TRANSACTION WITH CONSISTENT SNAPSHOT, die verhindern, dass Daten ausgegeben werden, die während des Speicherauszugs geändert wurden. Ohne das wären die zu Beginn erhaltenen Binlog-Koordinaten bedeutungslos, da das --single-transactionnicht das wäre, was es behauptet zu sein. Dies sollte in keiner Weise mit dem Waiting for table flushProblem zusammenhängen, da diese Transaktion offensichtlich keine Sperren enthält.


Diese Antwort ist tatsächlich richtig.
Boban P.

2

Ich habe eine Funktionsanfrage gesendet: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902 .

Ich habe auch einen Patch gegen 5.6.37 geschrieben, der die gleiche Methode wie die Kombination von --single-transaction --master-data mit --single-transaction --slave-data verwendet und ohne Gewähr bereitgestellt wird. Benutzung auf eigene Gefahr.

--- 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)
   {

Ich habe es mit dem folgenden Prozess mit Slaves für einen sehr beschäftigten Master unter Verwendung vieler InnoDB-Tabellen mit FK-Beziehungen getestet:

  1. Stoppen Sie Slave A.
  2. Warten Sie ~ 15 Minuten.
  3. Dump DB 1 von Slave B mit der Option --single-transaction und --dump-Slave = 2
  4. Starten Sie Slave A bis zu den Koordinaten im Dump ab Schritt 3.
  5. Löschen Sie DB 1 und 2 von Slave A.
  6. Erstellen Sie leere DB 1 und 2 auf Slave A.
  7. Laden Sie den Dump von Schritt 3 in Slave A.
  8. Dump DB 2 von Slave B mit den gleichen Optionen. DB 2 hat FK-Beziehungen zu DB 1.
  9. Fügen Sie replicate_ignore_db für DB 2 und skip_slave_start auf Slave A hinzu.
  10. Starten Sie Slave A neu.
  11. Starten Sie den Slave, bis die Koordinaten vom Dump in Schritt 8 auf Slave A abgelaufen sind.
  12. Laden Sie den Dump von Schritt 8 in Slave A.
  13. Entfernen Sie die Optionen replicate_ignore_db und skip_slave_start von Slave A.
  14. Starten Sie Slave A neu.
  15. Warten Sie ~ 1 Woche.
  16. Verwenden Sie die pt-Prüfsumme, um die Datenintegrität zu überprüfen.

Der Patch-Einreichungsprozess von Oracle ist ziemlich intensiv, weshalb ich diesen Weg gegangen bin. Ich kann mit Percona und / oder MariaDB versuchen, es zu integrieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.