mysqldump - single-transazione, ma le query di aggiornamento sono in attesa del backup


10

Se uso mysqldump --single-transazione, secondo i documenti dovrebbe eseguire lo svuotamento delle tabelle con il blocco di lettura per ottenere uno stato coerente e quindi avviare una transazione e nessuno scrittore dovrebbe essere in attesa.

Tuttavia, ieri sera ho riscontrato la seguente situazione:

estratto dall'elenco completo dei processi:

centinaia di quelli ...

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

Poi questo:

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

e il resto dei thread è in modalità di sospensione

qualcuno ha idea di cosa stanno aspettando questi inserti? Non vedo alcuna tabella FLUSH o DDL o qualsiasi cosa menzionata nel manuale che possa far attendere le query.

comando mysqldump completo

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

Immagino che - quick sia ridondante qui, probabilmente un residuo dei tempi precedenti, questo script è molto vecchio, ma non dovrebbe danneggiare nulla


l'output completo di show processlist completo e show status innodb (anonimizzato) è qui: pastebin.com/D7WS3QAE
Aleksandar Ivanisevic

A cosa serve la tua riga di comando completa mysqldump? In particolare, stai usando --flush-logso --master-data...? Esistono potenziali interazioni tra le opzioni.
Michael - sqlbot,

aggiunto il comando mysqldump completo, grazie per la ricerca
Aleksandar Ivanisevic,

Risposte:


6

L' opzione --single-transazione di mysqldump non funziona FLUSH TABLES WITH READ LOCK;. Fa sì che mysqldump imposti una transazione di lettura ripetibile per tutte le tabelle in dumping.

Dalla tua domanda, hai dichiarato che il SELECT di mysqldump per la db_external_notificationtabella sta trattenendo centinaia di comandi INSERT su quella stessa tabella. Perché sta succedendo ?

La cosa più probabile è un blocco su gen_clust_index (meglio noto come indice cluster). Questo paradigma fa coesistere le pagine di dati e indici per una tabella. Le pagine dell'indice si basano sul PRIMARY KEY o sull'indice RowID generato automaticamente (nel caso in cui non vi sia PRIMARY KEY).

Dovresti essere in grado di individuarlo eseguendo SHOW ENGINE INNODB STATUS\Ge cercando qualsiasi pagina dal gen_clust_index che ha un blocco esclusivo. Effettuare INSERT in una tabella con un indice cluster richiede un blocco esclusivo per la gestione del BTREE di PRIMARY KEY, nonché la serializzazione di auto_increment.

Ho già discusso di questo fenomeno

AGGIORNAMENTO 2014-07-21 15:03 EDT

Guarda le righe 614-617 di 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

Nota che la riga 617 dice

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

Cosa mi dice questo? Hai un PRIMARY KEY con un auto_increment attivo id.

Il massimo idper il tavolo db_external_notificationera inferiore rispetto a 1252538391quando è stato avviato mysqldump. Quando si sottrae 1252538391da 1252538405, ciò significa che sono stati tentati 14 o più comandi INSERT. Internamente, ciò dovrebbe spostare l'incremento automatico di questa tabella almeno 14 volte. Tuttavia, nulla può essere impegnato o addirittura inserito nel buffer di registro a causa della gestione di questo iddivario.

Ora, guarda l'elenco dei processi dal tuo PasteBin. A meno che non abbia contato male, ho visto 38 DB Connections fare un INSERT (19 Before the mysqldump process (id processo 6155315), 19 After). Sono sicuro che 14 o più di queste connessioni sono bloccate a causa della gestione del gap auto_increment.


Stavo cercando da molto tempo e non riuscivo a trovare serrature esclusive. ho incollato lo stato innodb dello show completo su pastebin.com/D7WS3QAE , niente che mi sembri un blocco esclusivo su qualcosa per me
Aleksandar Ivanisevic,

Grazie per il chiarimento. Mi chiedo perché non utilizzino una transazione di sola lettura in quanto è chiaro che il backup non scriverà mai, ma suppongo che mantengano tale funzionalità per il backup aziendale.
Aleksandar Ivanisevic,

10

La --single-transactionpossibilità di mysqldump non fare una FLUSH TABLES WITH READ LOCKprima di iniziare il processo di backup , ma solo a determinate condizioni. Una di queste condizioni è quando si specifica anche l' --master-dataopzione.

Nel codice sorgente, dalla mysql-5.6.19/client/mysqldump.criga 5797:

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

Per ottenere un blocco solido sulle coordinate binlog precise prima di iniziare la transazione di lettura ripetibile, l' --master-dataopzione attiva questo blocco che deve essere ottenuto e quindi rilasciato una volta ottenute le coordinate binlog.

In effetti, mysqldumpfa FLUSH TABLESseguito da a FLUSH TABLES WITH READ LOCKperché fare entrambe le cose consente di ottenere il blocco della lettura più velocemente nei casi in cui il flush iniziale richiede del tempo.

...però...

Non appena ha ottenuto le coordinate del binlog, mysqldumpemette UNLOCK TABLESun'istruzione, quindi non dovrebbe esserci nulla che blocchi a causa del flush che hai iniziato. Né alcun thread dovrebbe essere Waiting for table flushil risultato della transazione che mysqldumpsta trattenendo.

Quando vedi un thread nello Waiting for table flushstato, ciò dovrebbe significare che l' FLUSH TABLES [WITH READ LOCK]istruzione è stata emessa ed era ancora in esecuzione all'avvio della query, quindi la query deve attendere lo svuotamento della tabella, prima che possa essere eseguita. Nel caso dell'elenco processi che hai pubblicato, mysqldumpsta leggendo da questa stessa tabella e la query è in esecuzione da un po ', ma le query di blocco non sono state bloccate per così tanto tempo.

Tutto ciò suggerisce che è successo qualcos'altro.

C'è un problema di vecchia data spiegato nel Bug # 44884 con il modo in cui FLUSH TABLESfunziona, internamente. Non sarei sorpreso se il problema persiste, sarei sorpreso se questo problema fosse mai "risolto" perché è un problema molto complesso da risolvere - praticamente impossibile da risolvere veramente in un ambiente ad alta concorrenza - e qualsiasi tentativo di risolverlo comporta un rischio significativo di rompere qualcos'altro o di creare un comportamento nuovo, diverso e ancora indesiderabile.

Sembra probabile che questa sarà la spiegazione di ciò che stai vedendo.

In particolare:

  • se si dispone di una query di lunga durata in esecuzione su una tabella e si verifica un problema FLUSH TABLES, il FLUSH TABLESblocco verrà eseguito fino al completamento della query di lunga durata.

  • inoltre, tutte le query che iniziano dopo l' FLUSH TABLESemissione verranno bloccate fino al FLUSH TABLEScompletamento.

  • Inoltre, se si uccide FLUSH TABLESl'interrogazione, le query che stanno bloccando saranno ancora bloccare sull'originale query di lunga durata, quella che stava bloccando l' FLUSH TABLESinterrogazione, perché anche se l'ucciso FLUSH TABLESquery non ha finito, quel tavolo (l'uno, o inoltre, coinvolto con la query di lunga durata) è ancora in fase di svuotamento e tale svuotamento in sospeso avverrà non appena termina la query di lunga durata, ma non prima.

La probabile conclusione qui è che un altro processo - forse un altro mysqldump, o una query sconsiderata, o un processo di monitoraggio scritto male ha tentato di cancellare una tabella.

Quella query è stata successivamente eliminata o scaduta da un meccanismo sconosciuto, ma i suoi effetti collaterali sono rimasti fino al mysqldumptermine della lettura dalla tabella in questione.

È possibile replicare questa condizione provando FLUSH TABLESmentre è in corso una query di lunga durata. Quindi avviare un'altra query, che verrà bloccata. Quindi uccidi la FLUSH TABLESquery, che non sbloccherà l'ultima query. Quindi uccidere la prima query o lasciarla terminare e la query finale verrà eseguita correttamente.


Come ripensamento, questo non è correlato:

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

È normale, perché mysqldump --single-transactionemette a START TRANSACTION WITH CONSISTENT SNAPSHOT, che gli impedisce di scaricare i dati che sono stati modificati mentre era in corso il dump. Senza questo, le coordinate binlog ottenute all'inizio sarebbero prive di significato, dal momento --single-transactionche non sarebbero quelle che afferma di essere. Ciò non dovrebbe in alcun modo essere correlato al Waiting for table flushproblema, poiché questa transazione ovviamente non ha vincoli.


Questa risposta è in realtà corretta.
Boban P.

2

Ho inviato una richiesta di funzione: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902 .

Ho anche scritto una patch contro 5.6.37 che utilizza lo stesso metodo della combinazione --single-transazione --master-data con --single-transazione - slave-data, che viene fornita così com'è senza garanzia. Utilizzare a proprio rischio.

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

L'ho provato con il seguente processo con gli slave a un master molto impegnato usando molte tabelle InnoDB con relazioni FK:

  1. Stop slave A.
  2. Attendere ~ 15 minuti.
  3. Dump DB 1 dallo slave B con opzione --single-transazione e --dump-slave = 2
  4. Avviare lo slave A fino alle coordinate nel dump dal passaggio 3.
  5. Eliminare i DB 1 e 2 dallo slave A.
  6. Creare DB 1 e 2 vuoti sullo slave A.
  7. Caricare il dump dal passaggio 3 nello slave A.
  8. Scaricare DB 2 dallo slave B con le stesse opzioni. DB 2 ha relazioni FK con DB 1.
  9. Aggiungere replicate_ignore_db per DB 2 e skip_slave_start sullo slave A.
  10. Riavvia lo slave A.
  11. Avviare lo slave fino alle coordinate dal dump al punto 8 sullo slave A.
  12. Caricare il dump dal passaggio 8 nello slave A.
  13. Rimuovere le opzioni replicate_ignore_db e skip_slave_start dallo slave A.
  14. Riavvia lo slave A.
  15. Attendere ~ 1 settimana.
  16. Utilizzare pt-checksum per verificare l'integrità dei dati.

Il processo di invio delle patch di Oracle è piuttosto intenso, quindi perché sono andato su questa strada. Potrei provare con Percona e / o MariaDB per integrarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.