mysqldump: transacción única, pero las consultas de actualización esperan la copia de seguridad

10

Si uso mysqldump --single-transaction, de acuerdo con los documentos, debería hacer tablas de descarga con bloqueo de lectura para obtener un estado coherente y luego comenzar una transacción y ningún escritor debería estar esperando.

Sin embargo, me he dado cuenta de la siguiente situación anoche:

Extracto de mostrar la lista de procesos completa:

cientos de esos ...

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

luego esto:

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

y el resto de los hilos están en reposo

¿Alguien tiene alguna idea de qué están esperando estos insertos? No veo ninguna tabla FLUSH ni DDL ni nada mencionado en el manual que pueda hacer que las consultas esperen.

comando completo mysqldump

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

Supongo que aquí el rápido es redundante, probablemente un sobrante de épocas anteriores, este script es muy antiguo, pero no debería dañar nada

Aleksandar Ivanisevic
fuente
La salida completa de show full processlist y show innodb status (anonymized) está aquí: pastebin.com/D7WS3QAE
Aleksandar Ivanisevic
¿Para qué es su línea de comando completa mysqldump? En particular, ¿estás usando --flush-logso --master-data...? Hay posibles interacciones entre las opciones.
Michael - sqlbot
comando completo de mysqldump agregado, gracias por mirar
Aleksandar Ivanisevic

Respuestas:

6

La opción de transacción única de mysqldump no funciona FLUSH TABLES WITH READ LOCK;. Hace que mysqldump configure una transacción de lectura repetible para todas las tablas que se vuelcan.

A partir de su pregunta, usted indicó que el SELECCIONAR mysqldump para la db_external_notificationtabla está reteniendo cientos de comandos INSERT a esa misma tabla. Por qué está pasando esto ?

Lo más probable es un bloqueo en el gen_clust_index (mejor conocido como el índice agrupado ). Este paradigma hace que coexistan páginas de datos e índices para una tabla. Esas páginas de índice se basan en la CLAVE PRIMARIA o en el índice RowID generado automáticamente (en el caso de que no haya una CLAVE PRIMARIA).

Debería poder detectar esto ejecutando SHOW ENGINE INNODB STATUS\Gy buscando cualquier página de gen_clust_index que tenga un bloqueo exclusivo. Hacer INSERTs en una tabla con un índice agrupado requiere un bloqueo exclusivo para manejar el BTREE de PRIMARY KEY, así como la serialización del auto_increment.

He discutido este fenómeno antes

ACTUALIZACIÓN 2014-07-21 15:03 EDT

Mire las líneas 614-617 de su 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

Tenga en cuenta que la línea 617 dice

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

¿Qué me dice esto? Tiene una CLAVE PRIMARIA con un aumento automático activado id.

Su máximo idpara la tabla db_external_notificationfue menor que 1252538391cuando se lanzó mysqldump. Cuando resta 1252538391de 1252538405, esto significa que se han intentado 14 o más comandos INSERT. Internamente, esto necesitaría mover el auto_increment de esta tabla al menos 14 veces. Sin embargo, nada puede ser comprometido o incluso empujado al Log Buffer debido a la gestión de esta idbrecha.

Ahora, mira la lista de procesos de tu PasteBin. A menos que haya contado mal, vi 38 DB Connections haciendo un INSERT (19 Antes del proceso mysqldump (identificación del proceso 6155315), 19 Después). Estoy seguro de que 14 o más de esas conexiones están congeladas debido a la gestión de la brecha auto_increment.

RolandoMySQLDBA
fuente
Estuve buscando mucho tiempo y no pude encontrar ninguna cerradura exclusiva. He pegado el estado de innodb del show completo en pastebin.com/D7WS3QAE , nada me parece un bloqueo exclusivo para nada
Aleksandar Ivanisevic
Gracias por la aclaración. Me pregunto por qué no usan una transacción de solo lectura, ya que está claro que la copia de seguridad nunca se escribirá, pero supongo que mantienen esa característica para su copia de seguridad empresarial.
Aleksandar Ivanisevic
10

La --single-transactionopción de mysqldump hace hacer FLUSH TABLES WITH READ LOCKantes de iniciar la tarea de respaldo , pero sólo bajo ciertas condiciones. Una de esas condiciones es cuando también especificas la --master-dataopción.

En el código fuente, desde la mysql-5.6.19/client/mysqldump.clínea 5797:

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

Para obtener un bloqueo sólido en las coordenadas precisas de binlog antes de comenzar la transacción de lectura repetible, la --master-dataopción activa este bloqueo que se obtendrá y luego se liberará una vez que se hayan obtenido las coordenadas de binlog.

De hecho, mysqldumphace un FLUSH TABLESseguido por un FLUSH TABLES WITH READ LOCKporque hacer ambas cosas permite que el bloqueo de lectura se obtenga más rápido en los casos en que el enjuague inicial toma algún tiempo.

...sin embargo...

Tan pronto como haya obtenido las coordenadas de binlog, mysqldumpemite una UNLOCK TABLESdeclaración, por lo que no debería haber nada bloqueado como resultado del vaciado que inició. Tampoco debe haber ningún subproceso Waiting for table flushcomo resultado de la transacción que se mysqldumpestá reteniendo.

Cuando vea un subproceso en el Waiting for table flushestado, eso debería significar que la FLUSH TABLES [WITH READ LOCK]instrucción se emitió y aún se estaba ejecutando cuando se inició la consulta, por lo que la consulta debe esperar el vaciado de la tabla antes de que pueda ejecutarse. En el caso de la lista de procesos que ha publicado, mysqldumpestá leyendo esta misma tabla, y la consulta se ha estado ejecutando durante un tiempo, sin embargo, las consultas de bloqueo no se han bloqueado durante tanto tiempo.

Todo esto sugiere que algo más ha sucedido.

Hay un problema de larga data explicado en el Bug # 44884 con la forma en que FLUSH TABLESfunciona internamente. No me sorprendería si el problema persiste, me sorprendería si este problema alguna vez se "soluciona" porque es un problema muy complejo de resolver, prácticamente imposible de solucionar en un entorno de alta concurrencia, y cualquier intento de arreglarlo conlleva un riesgo significativo de romper algo más, o crear un comportamiento nuevo, diferente y aún indeseable.

Parece probable que esta sea la explicación de lo que estás viendo.

Específicamente:

  • si tiene una consulta de larga duración ejecutándose en una tabla y emite FLUSH TABLES, FLUSH TABLESse bloqueará hasta que se complete la consulta de larga duración.

  • además, cualquier consulta que comience después de que FLUSH TABLESse emita se bloqueará hasta que FLUSH TABLESse complete.

  • Además, si matas a la FLUSH TABLESconsulta, las consultas que se están bloqueando aún bloquear en el original de consulta de larga duración, la que estaba bloqueando la FLUSH TABLESconsulta, porque a pesar de los muertos FLUSH TABLESconsulta no terminó, esa tabla (el uno, o más, involucrado con la consulta de larga duración) todavía está en proceso de ser vaciado, y ese vaciado pendiente va a suceder tan pronto como finalice la consulta de larga duración, pero no antes.

La conclusión probable aquí es que otro proceso, tal vez otro mysqldump, o una consulta mal aconsejada, o un proceso de monitoreo mal escrito, intentó eliminar una tabla.

Esa consulta fue posteriormente eliminada o expirada por un mecanismo desconocido, pero sus efectos posteriores persistieron hasta que mysqldumpterminó de leer la tabla en cuestión.

Puede replicar esta condición intentando FLUSH TABLESmientras una consulta de larga duración está en proceso. Luego comience otra consulta, que bloqueará. Luego elimine la FLUSH TABLESconsulta, que no desbloqueará la última consulta. Luego elimine la primera consulta o deje que finalice, y la consulta final se ejecutará con éxito.


Como una ocurrencia tardía, esto no está relacionado:

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

Eso es normal, porque mysqldump --single-transactionemite un START TRANSACTION WITH CONSISTENT SNAPSHOT, que evita que arroje datos que se modificaron mientras el volcado estaba en progreso. Sin eso, las coordenadas de binlog obtenidas al principio no tendrían sentido, ya --single-transactionque no serían lo que dicen ser. Esto no debería en ningún sentido estar relacionado con el Waiting for table flushproblema, ya que esta transacción obviamente no tiene bloqueos.

Michael - sqlbot
fuente
Esta respuesta es realmente correcta.
Boban P.
2

Envié una solicitud de función: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902 .

También escribí un parche contra 5.6.37 que usa el mismo método que - combinación única - combinación de datos maestro con - transacción única - datos esclavos, que se proporciona tal cual sin garantía. Úselo bajo su propio riesgo.

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

Lo probé con el siguiente proceso con esclavos a un maestro muy ocupado usando muchas tablas InnoDB con relaciones FK:

  1. Detén al esclavo A.
  2. Espera unos 15 minutos.
  3. Volcar DB 1 del esclavo B con la opción --single-transaction y --dump-slave = 2
  4. Inicie el esclavo A hasta que se coordinen en el volcado del paso 3.
  5. Suelte los DB 1 y 2 del esclavo A.
  6. Cree los DB 1 y 2 vacíos en el esclavo A.
  7. Cargue el volcado del paso 3 en el esclavo A.
  8. Volcar DB 2 del esclavo B con las mismas opciones. DB 2 tiene relaciones FK con DB 1.
  9. Agregue replicate_ignore_db para DB 2 y skip_slave_start en el esclavo A.
  10. Reiniciar esclavo A.
  11. Inicie el esclavo hasta las coordenadas del volcado en el paso 8 en el esclavo A.
  12. Cargue el volcado del paso 8 en el esclavo A.
  13. Elimine las opciones replicate_ignore_db y skip_slave_start del esclavo A.
  14. Reiniciar esclavo A.
  15. Espera ~ 1 semana.
  16. Utilice pt-checksum para verificar la integridad de los datos.

El proceso de envío de parches de Oracle es bastante intenso, por lo tanto, por qué tomé esta ruta. Puedo intentarlo con Percona y / o MariaDB para integrarlo.

usuario44127
fuente