Как удалить 1000+ баз данных с innodb_file_per_table = 1 без зависания процесса MySQL? - PullRequest
0 голосов
/ 12 февраля 2019

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

Через несколько месяцев необходимо очистить базы данных,Мы просто вызываем DROP DATABASE customer_1 для каждой базы данных (предоставляя серверу MySQL 10 секунд между каждым оператором 'rest'), а затем DROP USER 'customer_1'@'127.0.0.1').

Время от времени вся база данных просто зависает.SHOW PROCESSLIST дает

Id     User       Command    Time    State         Info
[pid]  adm-user   Query      300     System lock   DROP DATABASE `customer_1`

Новые запросы не будут выполняться.Уничтожение соответствующего pid запроса приведет к Command = Killing, и все.Ничего не произошло.Демон MySQL также не может быть остановлен, потому что он все еще ожидает завершения запроса.

Мы привели к отключению всего сервера, его перезапуску и выполнению автоматического восстановления после сбоя MySQL, что прекрасно работает.После чего мы можем отбросить еще 10-30 баз данных, и затем это событие повторяется.

Мы много читали по этому вопросу, включая, но не ограничиваясь:

Похоже, консенсус, да, это MySQL, который использует глобальную блокировку мьютекса на таблице (пробел), в сочетании с большим размером пула буферов.

Наш my.cnf:

innodb_file_per_table   = 1
innodb_buffer_pool_size = 9G
innodb_log_file_size    = 256M
innodb_flush_method     = O_DIRECT
table_open_cache        = 200000
table_definition_cache  = 110000
innodb_flush_log_at_trx_commit = 2

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

Я читал, что простое удаление всех файлов таблиц может сработать, а затем удалить базу данных, в которой MySQL должен просто удалить ссылки на базу данных.

Ответы [ 4 ]

0 голосов
/ 20 февраля 2019

Рекомендации Билла Карвина кажутся разумными (хотя RENAME TABLE вызывал некоторые из тех же проблем, что и DROP TABLE в прошлом), но большая часть этого материала должна была быть исправлена: Ошибка 51325 былаисправлено в 2011-12-20 в 5.6.4 и Ошибка 64284 было исправлено в 2012-08-09 в 5.6.6 .

Возможно, вы столкнулись с чем-то, связанным с MySQL ошибка 91977 , для которой одним из возможных решений является отключение Adaptive Hash Index при удалении таблиц / баз данных.

SET GLOBAL innodb_adaptive_hash_index = OFF;
DROP TABLE ...
SET GLOBAL innodb_adaptive_hash_index = ON;

Или, может быть, просто отбросьте адаптивные хеш-индексы полностью.См. Приведенную выше связанную документацию, в которой говорится, что они являются чистым преимуществом или нет, зависят от рабочей нагрузки, и вы должны провести тестирование производительности, чтобы решить, использовать их или нет.

Возможно, вы захотите перейти на текущий MySQL.5.7.x, то есть 5.7.25 и подайте отчет об ошибке, если вы все еще можете воспроизвести проблему.

0 голосов
/ 14 февраля 2019

Одна важная вещь, которую вы должны сделать, - это использовать файловую систему XFS для своего каталога данных MySQL.

Удаление больших файлов в файловой системе ext3 занимает слишком много времени, как вы, без сомнения, читали в блоге Percona, на который ссылались.Использование XFS значительно ускоряет удаление большого файла, поэтому глобальный мьютекс удерживается в течение более короткого времени.

Я бы также отбрасывал таблицы по одному, чтобы еще больше сократить время удержания мьютекса.Затем, после того как вы удалили все таблицы, удалите базу данных.

База данных в MySQL вряд ли является физическим объектом вообще.Это подкаталог каталога данных MySQL и крошечный файл с именем db.opt, в котором хранятся некоторые свойства базы данных, такие как набор символов по умолчанию (в MySQL 8.0 это уже не отдельный файл).После удаления всех таблиц удаление самой базы данных является тривиальным.

Другое предложение - сначала удалить пользователя MySQL клиента, а затем разрешить MySQL работать в течение нескольких часов, пока данные из таблиц этого клиента больше не будут кэшированы.в пуле буферов.Когда вы отбрасываете большую таблицу, MySQL должен сканировать буферный пул, чтобы освободить страницы, принадлежащие этой таблице.Чем больше буферный пул, тем дольше это занимает.Таким образом, вы можете свести к минимуму это влияние, если вы позволите страницам для таблиц этого клиента истечь и покинуть буферный пул.Это может занять некоторое время, потому что это больше зависит от спроса на другие таблицы.Нет хорошего способа заставить страницу таблицы покинуть буферный пул, если не считать удаления таблицы.

Я делал это в некоторых средах.Сделайте запрос DROP TABLE в RENAME TABLE, чтобы переместить таблицу в другую схему, к которой нет доступа ни у одного пользователя.Затем периодически запускайте скрипт, чтобы действительно отбрасывать таблицы, которые были в этой ручке более 7 дней.Это дает время для постепенного удаления страниц из буферного пула, поскольку данные из других таблиц вытесняют их.Кроме того, это также дает льготный период для пользователей, чтобы они могли передумать, если решили, что они в конце концов отбросили стол, который им нужен.

0 голосов
/ 18 февраля 2019

На основании этой цепочки комментариев:

Является ли решение на основе сценария приемлемым для вас?Например, создать скрипт php и запустить его?- treyBake 14 февраля в 17:33@treyBake Да, конечно.У нас есть полный контроль над всем.- Сделайте 5 минут ag

Вы можете сделать это через PHP (назовите его rm_databases.php, например:

$tableName = 'customer_';

for ($i = 1; $i <= 300; $++)
{
    # set up db conn
    $conn = new \PDO(
        'mysql:dbname='. $tableName .$i. ';host=localhost;',
        'user',
        'pass'
     );

     # create the SQL statement
     $sql = 'DROP DATABASE IF EXISTS '. $tableName .$i. ';';

     # exec it
     $conn->prepare($sql);
     $conn->execute();
}

echo 'done!';

Затем у вас есть выбор: либо запустить руководство, запустив:

php -f rm_databases.php

или вы можете настроить запуск cronjob каждые 3 месяца:

0 0 12 ? 1/3 MON#1 * php -f rm_databases.php

Это будет запускаться в первый понедельник каждые 3 месяца.

Примечание: Это будет работать, если каждый клиентский БД имеет префикс customer_$i - но если он более динамичен, каким бы изнурительным он ни был, возможно, стоит создать его.Мега массив имен баз данных и цикл через него. Начальное время установки будет больше, но как только вы закончите, добавление новых пользователей займет 2 секунды:

$databases = [
    'foo', 'bar3', 'foobar', 'treyisawesome', 'wp-firesf'
    # etc etc
];

foreach ($databases as $el)
{
   # set up db conn
   $conn = new \PDO(
       'mysql:dbname='. $el .';host=localhost;',
       'user',
       'pass'
    );

    # create the SQL statement
    $sql = 'DROP DATABASE IF EXISTS '. $el .';';

    # rest of the script stays the same
}
0 голосов
/ 14 февраля 2019

Вы должны указать ядру базы данных не делать эксклюзивную (глобальную) блокировку.Это можно сделать двумя способами:

  1. Использовать предложение LOCK (НЕТ или РАЗДЕЛ.).
  2. Использовать ALTER ONLINE TABLE (то же, что LOCK = NONE).

ALTER TABLE tbl_name ADD PRIMARY KEY (столбец), ALGORITHM = INPLACE, LOCK = NONE;

После изменения всех таблицили если вы создаете с разделяемой блокировкой, вы можете удалить все таблицы.

...