Оптимизация MySQL для ALTER TABLE из InnoDB - PullRequest
29 голосов
/ 17 марта 2009

Вскоре нам потребуется внести изменения в схему нашей производственной базы данных. Мы должны минимизировать время простоя для этих усилий, однако операторы ALTER TABLE будут выполняться довольно долго. Наши самые большие таблицы имеют 150 миллионов записей, самый большой файл таблицы - 50G. Все таблицы InnoDB, и он был настроен как один большой файл данных (вместо файла на таблицу). Мы работаем с MySQL 5.0.46 на 8-ядерном компьютере, 16G памяти и конфигурации RAID10.

У меня есть некоторый опыт настройки MySQL, но обычно он фокусируется на чтении или записи от нескольких клиентов. По этому вопросу в Интернете можно найти много информации, однако, по-видимому, очень мало информации о лучших методах (временной) настройки сервера MySQL для ускорения ALTER TABLE для таблиц InnoDB или для INSERT INTO. . SELECT FROM (мы, вероятно, будем использовать это вместо ALTER TABLE, чтобы иметь больше возможностей немного ускорить процесс).

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

Каковы идеальные параметры для выполнения этой задачи как можно быстрее?

Ответы [ 6 ]

15 голосов
/ 04 июля 2012

Возможно, вы захотите посмотреть pt-online-schema-change из инструментария Percona. По сути, это то, что он делает:

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

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

Есть также хороший вебинар об этом здесь .

PS: я знаю, что это старый вопрос, просто отвечаю на случай, если кто-то нажмет на него через поисковик.

15 голосов
/ 18 марта 2009

Вы должны подумать о своих требованиях немного более тщательно.

На самом простом уровне, самый быстрый способ изменить таблицу - это сделать как можно меньше ALTER TABLE операторов, предпочтительно один. Это связано с тем, что MySQL копирует данные таблицы для изменения схемы и делает пятнадцать изменений, в то время как создание одной копии, очевидно (и действительно), быстрее, чем копирование таблицы пятнадцать раз, делая одно изменение за раз.

Но я подозреваю, что вы спрашиваете, как сделать это изменение с наименьшим количеством простоев. То, как я это сделаю, вы в основном синтезируете, как неблокированный ALTER TABLE будет работать. Но у него есть некоторые дополнительные требования:

  1. вам нужен способ для отслеживания добавленных и измененных данных, например, с помощью «измененного» поля даты для последних или поля AUTO_INCREMENT для первых.
  2. вам нужно место, чтобы иметь две копии вашей таблицы в базе данных.
  3. вам нужен период времени, когда изменения таблицы не будут слишком далеко опережать моментальный снимок

Основная техника, как вы предложили, то есть использование INSERT INTO ... SELECT .... По крайней мере, вы впереди, потому что вы начинаете с таблицы InnoDB, поэтому SELECT не будет блокироваться. Я рекомендую сделать ALTER TABLE для новой пустой таблицы, что позволит MySQL снова скопировать все данные, что будет означать, что вам нужно правильно перечислить все поля в операторе INSERT INTO ... SELECT .... Затем вы можете сделать простой оператор RENAME, чтобы поменять его местами. Затем вам нужно сделать еще INSERT INTO ... SELECT ... WHERE ... и, возможно, UPDATE ... INNER JOIN ... WHERE ..., чтобы получить все измененные данные. Вам нужно быстро INSERT и UPDATE , иначе ваш код начнет добавлять новые строки и обновления в ваш снимок, которые будут мешать вашему обновлению. (У вас не возникнет этой проблемы, если вы сможете перевести свое приложение в режим обслуживания на несколько минут до RENAME.)

Кроме того, есть некоторые настройки, относящиеся к ключу и буферу, которые можно изменить всего за один сеанс, что может помочь перемещению основных данных. Такие вещи, как read_rnd_buffer_size и read_buffer_size были бы полезны для увеличения.

12 голосов
/ 06 октября 2009
  1. Настройка ведомого
  2. Остановить репликацию.
  3. Сделать ALTER на рабе
  4. Пусть раб догонит хозяина
  5. поменяйте местами master и slave, чтобы slave стал рабочим сервером с измененной структурой и минимальным временем простоя
11 голосов
/ 10 июня 2009

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

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

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

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

5 голосов
/ 02 октября 2012

Я проверил различные стратегии, чтобы ускорить изменение таблицы. В итоге я получил увеличение скорости примерно в 10 раз в моем конкретном случае. Результаты могут относиться или не относиться к вашей ситуации. Однако, исходя из этого, я бы предложил поэкспериментировать с параметрами файла журнала / размера буфера InnoDB.

Короче говоря, только увеличение innodb_log_file_size и innodb_log_buffer_size оказало измеримый эффект (будьте осторожны! Изменение innodb_log_file_size рискованно . Для получения дополнительной информации см. Ниже).

На основе приблизительной скорости записи данных (iostat) и активности процессора узкое место было основано на io, но не на пропускной способности данных. В более быстрых запусках 500 с пропускная способность записи по крайней мере находится на том же уровне, который вы ожидаете от жесткого диска.

Пробная оптимизация производительности:

Изменение innodb_log_file_size может быть опасным. См. http://www.mysqlperformanceblog.com/2011/07/09/how-to-change-innodb_log_file_size-safely/ Техника (перемещение файла), описанная в ссылке, хорошо работала в моем случае.

Также см. http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/ и http://www.mysqlperformanceblog.com/2008/11/21/how-to-calculate-a-good-innodb-log-file-size/ для получения информации о innodb и размерах журналов настройки. Один недостаток больших файлов журнала - более длительное время восстановления после сбоя.

Тестовые прогоны и приблизительное время:

  • Простые данные загрузки в только что созданную таблицу: 6500 с
  • загрузка данных w. innodb_log_file_size = 200M, innodb_log_buffer_size = 8M, innodb_buffer_pool_size = 2200M, autocommit = 0; unique_checks = 0, foreign_key_checks = 0: 500 с
  • загрузка данных w. innodb_log_file_size = 200 МБ, innodb_log_buffer_size = 8 МБ: 500 с
  • Эквивалентная таблица прямых изменений w. datainnodb_log_file_size = 200M, innodb_log_buffer_size = 8M: 500 с

Подробности тестирования : Таблица: InnoDB, 6M строк, 2.8G на диске, один файл (опция innodb_file_per_table), первичный ключ - 1 целое число, +2 уникальных ограничения / индекса, 8 столбцов, среднее значение. длина строки 218 байт. Сервер: Ubuntu 12.04, x86_64, виртуальная машина, 8 ядер, 16 ГБ, диск sata потребительского уровня, без raid, без активности базы данных, минимальная активность других процессов, минимальная активность на других и гораздо меньших виртуальных машинах. Mysql 5.1.53. Начальная конфигурация сервера довольно стандартная, за исключением увеличенного innodb_buffer_pool_size 1400M. Таблица изменения добавляет 2 маленьких столбца. Я не синхронизировал необработанную таблицу изменения, но вместо этого экспериментировал с оператором эквивалентной загрузки данных, наконец, я сделал таблицу прямого изменения и получил сопоставимый результат.

Этот вопрос относится как минимум к следующим вопросам:

0 голосов
/ 17 марта 2009

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

Затем вы можете запускать сценарии БД, скажем, в 3 часа ночи, поэтому не должно иметь большого значения, если время простоя больше, чем идеальное.

...