Si j'utilise mysqldump --single-transaction, selon les documents, il devrait faire des tables de vidage avec verrou en lecture pour obtenir un état cohérent, puis démarrer une transaction et aucun écrivain ne devrait attendre.
Cependant, j'ai constaté hier soir la situation suivante:
extrait de la liste complète des processus:
des centaines de ceux-là ...
Command: Query
Time: 291
State: Waiting for table flush
Info: insert into db_external_notification.....
ensuite ceci:
Command: Query
Time: 1204
State: Sending data
Info: SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`
et le reste des fils sont en veille
quelqu'un a-t-il une idée de ce que ces insertions attendent? Je ne vois aucune table FLUSH ou DDL ou quoi que ce soit mentionné dans le manuel qui puisse faire attendre les requêtes.
commande mysqldump complète
mysqldump --quick --add-drop-table --single-transaction --master-data=2 -uxx -pxx dbname
Je suppose que --quick est redondant ici, probablement un reste des temps anciens, ce script est très ancien, mais ne devrait pas faire de mal
L'option - single-transaction de mysqldump ne fait pas FLUSH TABLES WITH READ LOCK;
. Il provoque mysqldump pour configurer une transaction de lecture répétable pour toutes les tables en cours de vidage.
De votre question, vous avez déclaré que le SELECT de mysqldump pour le db_external_notification
table contient des centaines de commandes INSERT sur cette même table. Pourquoi cela arrive-t-il ?
La chose la plus probable est un verrou sur le gen_clust_index (mieux connu sous le nom d'index clusterisé). Ce paradigme fait coexister des données et des pages d'index pour une table. Ces pages d'index sont basées sur la PRIMARY KEY ou sur l'index RowID généré automatiquement (dans le cas où il n'y a pas de PRIMARY KEY).
Vous devriez pouvoir le détecter en exécutant SHOW ENGINE INNODB STATUS\G
et recherchez n'importe quelle page du gen_clust_index qui a un verrou exclusif. Faire des INSERT dans une table avec un index clusterisé nécessite un verrou exclusif pour gérer le BTREE de la PRIMARY KEY, ainsi que la sérialisation de l'auto_increment.
J'ai déjà discuté de ce phénomène
Aug 08, 2011
: Les blocages InnoDB sont-ils exclusifs à INSERT/UPDATE/DELETE?Dec 22, 2011
: Blocage MySQL - ne peut pas redémarrer normalement?Dec 13, 2012
: MySQL InnoDB verrouille la clé primaire lors de la suppression, même en LECTURE COMMISEVeuillez regarder les lignes 614-617 de votre 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
Notez que la ligne 617 indique
Trx read view will not see trx with id >= 1252538405, sees < 1252538391
Qu'est-ce que cela me dit? Vous avez une PRIMARY KEY avec un auto_increment sur id
.
Votre max id
pour la table db_external_notification
était inférieur à 1252538391
lors du lancement de mysqldump. Lorsque vous soustrayez 1252538391
de 1252538405
, cela signifie que 14 commandes INSERT ou plus ont été tentées. En interne, cela devrait déplacer au moins 14 fois l'auto_increment de cette table. Pourtant, rien ne peut être validé ou même poussé dans le tampon de journal en raison de la gestion de cet écart id
.
Maintenant, regardez la liste des processus de votre Pastebin. Sauf erreur de compte, j'ai vu 38 connexions DB effectuer un INSERT (19 avant le processus mysqldump (processus id 6155315
), 19 Après). Je suis sûr que 14 ou plus de ces connexions sont gelées en raison de la gestion de l'écart auto_increment.
L'option --single-transaction
De mysqldump
fait faire un FLUSH TABLES WITH READ LOCK
Avant de démarrer la tâche de sauvegarde mais seulement sous certaines conditions. L'une de ces conditions est lorsque vous spécifiez également l'option --master-data
.
Dans le code source, à partir de mysql-5.6.19/client/mysqldump.c
À la ligne 5797:
if ((opt_lock_all_tables || opt_master_data ||
(opt_single_transaction && flush_logs)) &&
do_flush_tables_read_lock(mysql))
goto err;
Pour obtenir un verrou solide sur les coordonnées binlog précises avant de commencer la transaction de lecture répétable, l'option --master-data
Déclenche l'obtention de ce verrou, puis le relâche une fois les coordonnées binlog obtenues.
En fait, mysqldump
fait un FLUSH TABLES
Suivi d'un FLUSH TABLES WITH READ LOCK
Car faire les deux choses permet d'obtenir le verrou de lecture plus rapidement dans les cas où le vidage initial prend un certain temps.
...toutefois...
Dès qu'il a obtenu les coordonnées binlog, mysqldump
émet une instruction UNLOCK TABLES
, Donc il ne devrait pas y avoir de blocage à la suite du vidage que vous avez commencé. Aucun thread ne doit non plus être Waiting for table flush
À la suite de la transaction que mysqldump
détient.
Lorsque vous voyez un thread dans l'état Waiting for table flush
, Cela devrait signifie que l'instruction FLUSH TABLES [WITH READ LOCK]
A été émise et était toujours en cours d'exécution = au début de la requête - la requête doit donc attendre le vidage de la table avant de pouvoir s'exécuter. Dans le cas de la liste de processus que vous avez publiée, mysqldump
lit dans cette même table et la requête est en cours d'exécution depuis un certain temps, mais les requêtes de blocage ne bloquent pas depuis si longtemps.
Tout cela suggère que quelque chose d'autre s'est produit.
Il y a un problème de longue date expliqué dans le bogue # 44884 avec la façon dont FLUSH TABLES
Fonctionne, en interne. Je ne serais pas surpris si le problème persiste, Je serais surpris si ce problème est jamais "résolu" car il s'agit d'un problème très complexe à résoudre - pratiquement impossible à résoudre véritablement dans un environnement à concurrence élevée - et toute tentative de le résoudre comporte un risque important de casser quelque chose d'autre, ou créer un comportement nouveau, différent et toujours indésirable.
Il semble probable que ce sera l'explication de ce que vous voyez.
Plus précisément:
si vous avez une requête de longue durée exécutée sur une table et émettez FLUSH TABLES
, alors FLUSH TABLES
se bloquera jusqu'à la fin de la requête de longue durée.
en outre, toutes les requêtes qui commencent après l'émission de FLUSH TABLES
seront bloquées jusqu'à ce que FLUSH TABLES
soit terminé.
en outre, si vous supprimez la requête FLUSH TABLES
, les requêtes qui bloquent bloqueront toujours sur la requête de longue durée d'origine, celle qui bloquait la requête FLUSH TABLES
, car même si la requête FLUSH TABLES
supprimée ne s'est pas terminée, cette table (celle ou plus impliquée dans la requête de longue durée) est toujours dans le processus de vidage, et ce vidage en attente va se produire dès que la requête de longue durée se termine - mais pas avant.
La conclusion probable ici est qu'un autre processus - peut-être un autre mysqldump, ou une requête mal avisée, ou un processus de surveillance mal écrit a tenté de vider une table.
Cette requête a ensuite été supprimée ou expirée par un mécanisme inconnu, mais ses séquelles ont persisté jusqu'à ce que mysqldump
ait terminé la lecture de la table en question.
Vous pouvez répliquer cette condition en essayant de FLUSH TABLES
Pendant qu'une requête de longue durée est en cours. Ensuite, lancez une autre requête, qui se bloquera. Tuez ensuite la requête FLUSH TABLES
, Qui ne débloquera pas la dernière requête. Ensuite, supprimez la première requête ou laissez-la terminer, et la requête finale s'exécutera avec succès.
Après coup, cela n'est pas lié:
Trx read view will not see trx with id >= 1252538405, sees < 1252538391
C'est normal, car mysqldump --single-transaction
Émet un START TRANSACTION WITH CONSISTENT SNAPSHOT
, Ce qui l'empêche de vider les données modifiées pendant le vidage. Sans cela, les coordonnées binlog obtenues au début n'auraient aucun sens, car le --single-transaction
Ne serait pas ce qu'il prétend être. Cela ne devrait en aucun cas être lié au problème Waiting for table flush
, Car cette transaction ne contient évidemment aucun verrou.
J'ai soumis une demande de fonctionnalité: https://support.Oracle.com/epmos/faces/BugDisplay?id=27103902 .
J'ai également écrit un correctif contre 5.6.37 qui utilise la même méthode que la combinaison --single-transaction --master-data avec --single-transaction --slave-data, qui est fourni tel quel sans garantie. À utiliser à vos risques et périls.
--- 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)
{
Je l'ai testé avec le processus suivant avec des esclaves vers un maître très occupé en utilisant beaucoup de tables InnoDB avec des relations FK:
Le processus de soumission des correctifs d'Oracle est assez intensif, c'est pourquoi j'ai choisi cette voie. Je peux essayer avec Percona et/ou MariaDB pour l'intégrer.