Как преобразовать таблицу из 66 862 521 строк из MyISAM в InnoDB, не выходя из сети в течение нескольких часов?

18

Возможно ли (и как) преобразовать огромную таблицу MyISAM в InnoDB, не переводя приложение в автономный режим. Требуется вставлять пару строк в эту таблицу каждую секунду, но можно приостановить ее примерно на 2 минуты.

Очевидно, что ALTER TABLE ... engine = innodb не будет работать. Для этого у меня был план создать новую таблицу с движком innodb и скопировать в нее содержимое. И, наконец, приостановить поток журнала приложения и переименовать таблицу.

К сожалению, даже копирование небольшими партиями по 100 строк приводит к значительному отставанию через некоторое время.

Изменить : существующие строки никогда не изменяются, эта таблица используется для ведения журнала.

Хендрик Бруммерманн
источник
1
Дубликат: dba.stackexchange.com/questions/310/…
Джо
3
Ну, этот вопрос о минимизации времени разговора. Мне все равно, если разговор займет пару дней или недель. Но он должен работать в фоновом режиме, не требуя времени простоя приложения и не создавая заметного лага.
Хендрик Браммерманн

Ответы:

15

Создайте настройку Мастер-Мастер следующим образом:

  • Создать второго мастера, MasterB
  • MasterB действует как раб logTable
  • Создать logTable_newкак innodb
  • Запуск INSERT INTO logTable_new SELECT * FROM logTable(psuedocode) на MasterB, который отправляет репликацию на MasterA
  • Когда logTable_newна MasterA завершит синхронизацию, поменяйте местами таблицы
Дерек Дауни
источник
10

Учитывая ограничение:

Мне все равно, если разговор займет пару дней или недель. Но он должен работать в фоновом режиме, не требуя времени простоя приложения и не создавая заметного лага

В процессе ведения журнала, если у вас есть какой-то хороший способ установить маркер, чтобы вы могли сказать, с чего вы запускаете процесс, вы можете затем повторно применить любые журналы или записать журналы в текстовый файл, чтобы позже вы можете проглотить их с LOAD DATA INFILE

Частично проблема заключается в том, что запись небольшими партиями означает, что индексы нужно пересчитывать снова и снова; лучше запустить все сразу, но это может привести к некоторой «заметной» задержке в системе… но вам не нужно делать это на рабочем сервере.

  1. Приостановите ведение журнала или установите маркер, чтобы вы могли повторно применить журналы с этого момента позже.
  2. Скопируйте таблицу MyISM в другую систему
  3. В другой системе создайте таблицу InnoDB под другим именем и перенесите данные (возможно, будет даже быстрее создать дамп и использовать LOAD DATA INFILE)
  4. Скопируйте таблицу InnoDB обратно в исходную систему
  5. Установите другой маркер для регистрации.
  6. Повторно примените все журналы к новой таблице между двумя последними маркерами.
  7. (повторите шаги 5 и 6, если шаг № 6 занял больше минуты или около того, пока не прошло всего несколько секунд)
  8. Поменяйте местами таблицы (переименуйте старую в table_BACKUP, новая под именем старой)
  9. Догнать логи с момента последнего маркера.
Джо
источник
9

К сожалению, даже копирование небольшими партиями по 100 строк приводит к значительному отставанию через некоторое время.

Добавляете ли вы какую-либо задержку между каждым пакетом или просто пакетируете обновления и запускаете каждый пакет сразу после предыдущего?

Если это так, попробуйте написать сценарий преобразования на вашем любимом языке, например:

repeat
    copy oldest 100 rows that haven't been copied yet to new table
    sleep for as long as that update took
until there are <100 rows unprocessed
stop logging service
move the last few rows
rename tables
restart logging
delete the old table when you are sure the conversion has worked

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

Или, если вы хотите использовать как можно больше времени, когда служба относительно простаивает, но отключается (потенциально может быть приостановлена ​​на длительный период времени), когда базе данных необходимо выполнить некоторую работу для своих пользователей, замените ее sleep for as long as the update tookна if the server's load is above <upper measure>, sleep for some seconds then check again, loop around the sleep/check until the load drops below <lower measure>. Это будет означать, что он может двигаться вперед в спокойное время, но полностью остановится, когда сервер занят выполнением своей нормальной рабочей нагрузки. Определение нагрузки будет зависеть от вашей ОС - в Linux и аналогичных значениях средней загрузки за 1 минуту /proc/loadavgили выходных данных uptime. <lower measure>и <upper measure>может иметь одинаковое значение, хотя обычно в элементах управления, подобных этому, есть различие, поэтому ваш процесс не продолжает запускаться, а затем сразу приостанавливается из-за того, что его собственный перезапуск влияет на показатель нагрузки.

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

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

Дэвид Спиллетт
источник
4

Будет ли что-то вроде этой работы?

  1. Приостановить ведение журнала (чтобы $auto_incrementтаблица журналов mytable не менялась).
  2. Обратите внимание на $auto_incrementзначение, используя SHOW TABLE STATUS LIKE 'mytable'.
  3. CREATE TABLE mytable_new LIKE mytable
  4. ALTER TABLE mytable_new AUTO_INCREMENT=$auto_increment ENGINE=Innodb
  5. RENAME TABLE mytable TO mytable_old, mytable_new TO mytable
  6. Включите ведение журнала снова. Таблица Innodb начнет заполняться.
  7. INSERT INTO mytable SELECT * FROM mytable_old,

Вы можете выполнить шаг 7 в пакетном режиме или в одном операторе, поскольку он не должен блокировать обычное ведение журнала.

Riedsio
источник
это все равно будет блокировать из-за того, как innodb обрабатывает auto_increment ,. по умолчанию innodb берет блокировку на уровне таблицы при вставке в столбец auto_increment и снимает блокировку после завершения вставки.
ovais.tariq