mysqldump - одиночная транзакция, но запросы на обновление ждут резервного копирования

10

Если я использую mysqldump --single-транзакции, в соответствии с документацией он должен выполнить очистку таблиц с блокировкой чтения, чтобы получить согласованное состояние, а затем начать транзакцию, и никаких авторов не должно ждать.

Однако вчера вечером я обнаружил следующую ситуацию:

выдержка из показа полного списка процессов:

сотни таких ...

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

тогда это:

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

а остальные темы спят

Кто-нибудь есть идеи, чего ждут эти вставки? Я не вижу ни таблиц FLUSH, ни DDL, ни чего-либо, упомянутого в руководстве, которое может заставить запросы ждать.

полная команда mysqldump

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

Я полагаю, --quick здесь избыточен, вероятно, пережиток прошлого, этот скрипт очень старый, но не должен причинять вреда.

Александар Иванишевич
источник
полный вывод show full processlist и показа статуса innodb (анонимный) находится здесь: pastebin.com/D7WS3QAE
Александар Иванишевич
Для чего нужна ваша полная командная строка mysqldump? В частности, вы используете --flush-logsили --master-data...? Есть потенциальные взаимодействия между вариантами.
Майкл - sqlbot
добавлена ​​полная команда mysqldump, спасибо за просмотр
Александр Иванишевич

Ответы:

6

Опция --single -action для mysqldump не подходит FLUSH TABLES WITH READ LOCK;. Это приводит к тому, что mysqldump устанавливает повторяемую транзакцию чтения для всех дампируемых таблиц.

От вашего вопроса вы заявили, что SELECT mysqldump для db_external_notificationтаблицы содержит сотни команд INSERT для этой же таблицы. Почему это происходит ?

Наиболее вероятная вещь - блокировка на gen_clust_index (более известный как Clustered Index). Эта парадигма заставляет сосуществовать данные и страницы индекса для таблицы. Эти индексные страницы основаны либо на PRIMARY KEY, либо на автоматически сгенерированном индексе RowID (в случае, если PRIMARY KEY нет).

Вы должны быть в состоянии определить это, запустив SHOW ENGINE INNODB STATUS\Gи ища любую страницу из gen_clust_index, которая имеет эксклюзивную блокировку. Выполнение INSERT в таблице с кластеризованным индексом требует монопольной блокировки для обработки BTREE PRIMARY KEY, а также сериализации auto_increment.

Я обсуждал это явление раньше

ОБНОВЛЕНИЕ 2014-07-21 15:03 ПО ВОСТОЧНОМУ ВРЕМЕНИ

Пожалуйста, посмотрите на строки 614-617 вашей 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

Обратите внимание, что строка 617 говорит

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

Что это говорит мне? У вас есть ПЕРВИЧНЫЙ КЛЮЧ с включенным auto_increment id.

Ваш максимум idдля таблицы db_external_notificationбыл меньше, чем 1252538391при запуске mysqldump. Когда вы вычитаете 1252538391из 1252538405, это означает, что было предпринято 14 или более команд INSERT. Внутренне это должно было бы переместить auto_increment этой таблицы по крайней мере 14 раз. Тем не менее, ничто не может быть зафиксировано или даже помещено в Log Buffer из-за управления этим idразрывом.

Теперь посмотрите на список процессов из вашего PasteBin. Если я не учел ошибки, я увидел, что 38 соединений с БД выполняли INSERT (19 до процесса mysqldump (идентификатор процесса 6155315), 19 после). Я уверен, что 14 или более из этих соединений заморожены из-за разрыва auto_increment.

RolandoMySQLDBA
источник
Я долго искал и не мог найти никаких эксклюзивных замков. я вставил полный статус innodb шоу на pastebin.com/D7WS3QAE , там нет ничего похожего на эксклюзивную блокировку для меня
Александар Иванишевич
Благодарю за разъяснение. Мне интересно, почему они не используют транзакцию только для чтения, поскольку ясно, что резервное копирование никогда не будет записываться, но я предполагаю, что они сохраняют эту функцию для резервного копирования своего предприятия.
Александар Иванишевич
10

--single-transactionВариант mysqldump делает сделать FLUSH TABLES WITH READ LOCKперед началом задания резервного копирования , но только при определенных условиях. Одно из таких условий - когда вы также указываете --master-dataопцию.

В исходном коде из mysql-5.6.19/client/mysqldump.cстроки 5797:

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

Чтобы получить надежную блокировку точных координат бинлога до начала транзакции повторяемого чтения, эта --master-dataопция вызывает получение этой блокировки, а затем освобождение после получения координат бинлога.

На самом деле, следует mysqldumpли FLUSH TABLESпосле a, FLUSH TABLES WITH READ LOCKпотому что выполнение обеих вещей позволяет быстрее получить блокировку чтения в случаях, когда первоначальная очистка занимает некоторое время.

...Однако...

Как только он получит координаты бинлога, mysqldumpвыдает UNLOCK TABLESинструкцию, поэтому в результате начавшегося сброса не должно быть ничего блокирующего. Ни один из потоков не должен быть Waiting for table flushрезультатом транзакции, которая mysqldumpудерживается.

Когда вы видите поток в Waiting for table flushсостоянии, это должно означать, что FLUSH TABLES [WITH READ LOCK]инструкция была выполнена и все еще выполнялась, когда запрос начинался - поэтому запрос должен ждать сброса таблицы, прежде чем он сможет выполнить. В случае списка процессов, который вы опубликовали, mysqldumpон читает из этой же таблицы, и запрос выполнялся какое-то время, но блокирующие запросы не блокировали все это время.

Все это говорит о том, что произошло что-то еще.

В Bug # 44884 есть давняя проблема, объясняющая, как FLUSH TABLESработает внутренне. Я не был бы удивлен, если проблема все еще сохраняется, я был бы удивлен, если бы эта проблема когда-либо была «исправлена», потому что это очень сложная проблема, которую практически невозможно решить - практически невозможно действительно решить в среде с высоким уровнем параллелизма - и любая попытка исправление этого влечет за собой значительный риск поломки чего-либо другого или создания нового, другого и все еще нежелательного поведения.

Кажется вероятным, что это будет объяснением того, что вы видите.

В частности:

  • если у вас есть долгосрочный запрос, работающий с таблицей, и вы FLUSH TABLESвыполняете команду, то он FLUSH TABLESбудет блокироваться до тех пор, пока не будет выполнен длительный запрос.

  • Кроме того, любые запросы, которые начинаются после FLUSH TABLESвыдачи, будут блокироваться до FLUSH TABLESзавершения.

  • Кроме того, если вы убьете FLUSH TABLESзапрос, блокирующие запросы все равно будут блокировать исходный длительный запрос, который блокировал FLUSH TABLESзапрос, потому что, даже если завершенный FLUSH TABLESзапрос не завершился, эта таблица (та, или более того, связанный с долгосрочным запросом) все еще находится в процессе сброса, и эта ожидающая очистка произойдет, как только закончится длительный запрос - но не раньше.

Вероятный вывод здесь состоит в том, что другой процесс - возможно, другой mysqldump, или опрометчивый запрос, или плохо написанный процесс мониторинга попытался очистить таблицу.

Впоследствии этот запрос был прерван или заблокирован тайм-аутом неизвестного механизма, но его последствия оставались до mysqldumpзавершения чтения из рассматриваемой таблицы.

Вы можете повторить это условие, пытаясь сделать так, чтобы в FLUSH TABLESтечение длительного времени выполнялся запрос. Затем запустите другой запрос, который заблокирует. Затем убейте FLUSH TABLESзапрос, который не разблокирует последний запрос. Затем убейте первый запрос или дайте ему закончить, и последний запрос будет успешно выполнен.


Как запоздалая мысль, это не связано:

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

Это нормально, потому что mysqldump --single-transactionвыдает сообщение a START TRANSACTION WITH CONSISTENT SNAPSHOT, которое не позволяет создавать дамп данных, которые были изменены во время выполнения дампа. Без этого координаты бинлога, полученные в начале, были бы бессмысленными, поскольку --single-transactionони не были бы такими, какими они себя называют . Это ни в коем случае не должно быть связано с Waiting for table flushпроблемой, поскольку эта транзакция, очевидно, не содержит блокировок.

Майкл - sqlbot
источник
Этот ответ на самом деле правильный.
Бобан П.
2

Я отправил запрос на добавление функции: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902 .

Я также написал патч для 5.6.37, который использует тот же метод, что и комбинация --single-транзакции - master-data с --single-транзакции --slave-data, которая предоставляется без каких-либо гарантий. Используйте на свой риск.

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

Я проверил это с помощью следующего процесса с ведомыми устройствами для очень занятого мастера, использующего множество таблиц InnoDB с отношениями FK:

  1. Останови раба А.
  2. Подождите ~ 15 минут.
  3. Дамп DB 1 от ведомого B с опцией --single -action и --dump-slave = 2
  4. Запустите подчиненный A до координат в дампе с шага 3.
  5. Бросьте DB 1 и 2 из ведомого А.
  6. Создайте пустые БД 1 и 2 на ведомом устройстве A.
  7. Загрузите дамп с шага 3 в подчиненное устройство A.
  8. Дамп DB 2 от ведомого B с теми же параметрами. БД 2 имеет отношения FK к БД 1.
  9. Добавьте replicate_ignore_db для DB 2 и skip_slave_start на ведомом устройстве A.
  10. Перезагрузите раб А.
  11. Запустите ведомый до координат от дампа в шаге 8 на ведомом A.
  12. Загрузите дамп с шага 8 в ведомое устройство A.
  13. Удалите параметры replicate_ignore_db и skip_slave_start из подчиненного устройства A.
  14. Перезагрузите раб А.
  15. Подождите ~ 1 неделю.
  16. Используйте контрольную сумму pt для проверки целостности данных.

Процесс отправки патчей Oracle довольно интенсивный, поэтому я выбрал этот путь. Я могу попробовать с Percona и / или MariaDB, чтобы интегрировать его.

user44127
источник