ALTER TABLE без блокировки таблицы?

107

При выполнении оператора ALTER TABLE в MySQL вся таблица блокируется для чтения (разрешает одновременное чтение, но запрещает одновременную запись) на время выполнения оператора. Если это большая таблица, инструкции INSERT или UPDATE могут быть заблокированы на очень долгое время. Есть ли способ выполнить «горячее изменение», например, добавить столбец таким образом, чтобы таблица оставалась обновляемой на протяжении всего процесса?

В основном меня интересует решение для MySQL, но мне были бы интересны другие СУБД, если MySQL не может этого сделать.

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

Даниэль
источник
2
Придется задаться вопросом, сколько раз вы будете менять таблицу?
Allain Lalonde
1
IMHO, изменения схемы базы данных связаны с целыми новыми версиями - они не выпускаются спорадически, как другие изменения. Это неизбежно большое дело.
dkretz
9
@AllainLalonde - более 0 раз делает этот вопрос законным, особенно если простой вашей системы будет стоить жизни или больших денег. Во всяком случае, новые требования к программному обеспечению иногда появляются.
Натан Лонг

Ответы:

60

Единственный другой вариант - сделать вручную то, что и так делают многие системы СУБД ...
- Создать новую таблицу

Затем вы можете копировать содержимое старой таблицы по фрагменту за раз. При этом всегда будьте осторожны с любыми INSERT / UPDATE / DELETE в исходной таблице. (Можно управлять с помощью триггера. Хотя это приведет к замедлению, это не блокировка ...)

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

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

РЕДАКТИРОВАТЬ:

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

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

  • Фактически вы меняете физическую структуру диска, каждая запись перемещается.
  • Это действительно похоже на ОБНОВЛЕНИЕ для всей таблицы, но с большим влиянием ...
MatBailie
источник
2
И составьте подробный план тестирования перед заменой. Если не получится, начните заново.
dkretz
2
Управление синхронизацией с помощью триггеров было хорошей идеей. Я использую MySQL так долго, что все время забываю, что у них есть триггеры. Я использовал эту технику, и теперь у меня есть работающий сценарий горячей замены. С индикатором выполнения. И это работает с MyISAM. Жизнь хороша.
Daniel
2
+1 Это буквально то, что менеджер SQL Enterprise делает за кулисами, когда вы вносите определенные изменения в таблицы в пользовательском интерфейсе. В SQL 2008 они фактически добавили предупреждение, чтобы пользователь ЗНАЕТ, что он выполняет это радикальное действие.
BradC
2
Вы ничего не упомянули о внешних ключах, ссылающихся на изменяемые таблицы. Разве это не проблема?
Рафай
2
@MohammadRafayAleem - И поля AUTOINCREMENT, и просмотры, и триггеры, и т.д., и т.д. Но даже в этом случае подход все еще работает.
MatBailie
42

Percona создает инструмент под названием pt-online-schema-change, который позволяет это сделать.

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

Это похоже на метод, предложенный Демсом выше, но выполняется автоматически.

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

Пример:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends
Шон Дауни
источник
Похоже ссылка не работает. Я обнаружил, что эта ссылка работает.
Ноам Бен Ари
25

Этот вопрос от 2009 года. Теперь MySQL предлагает решение:

Онлайн DDL (язык определения данных)

Функция, которая улучшает производительность, параллелизм и доступность таблиц InnoDB во время операций DDL (в первую очередь ALTER TABLE). За подробностями обратитесь к Разделу 14.11, «InnoDB и онлайн-DDL».

Детали различаются в зависимости от типа операции. В некоторых случаях таблица может быть изменена одновременно с выполнением ALTER TABLE. Операция может быть выполнена без копирования таблицы или с использованием специально оптимизированного типа копии таблицы. Использование пространства контролируется параметром конфигурации innodb_online_alter_log_max_size.

Он позволяет вам регулировать баланс между производительностью и параллелизмом во время операции DDL, выбирая, следует ли полностью блокировать доступ к таблице (предложение LOCK = EXCLUSIVE), разрешать запросы, но не DML (предложение LOCK = SHARED), или разрешать полный запрос и DML доступ к таблице (предложение LOCK = NONE). Когда вы опускаете предложение LOCK или указываете LOCK = DEFAULT, MySQL допускает максимально возможный параллелизм в зависимости от типа операции.

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

см. Справочное руководство MySQL 5.6 -> InnoDB и Online DDL для получения дополнительной информации.

Кажется, что онлайн-DDL также доступен в MariaDB

В качестве альтернативы вы можете использовать ALTER ONLINE TABLE, чтобы гарантировать, что ваша ALTER TABLE не блокирует параллельные операции (не требует блокировок). Это эквивалентно LOCK = NONE.

КБ MariaDB об ALTER TABLE

Иванов
источник
3
Жаль, что нет другого способа, кроме голосов, поднять этот вопрос наверх, поскольку он в основном сводит на нет все остальные ответы просто потому, что они больше не ссылаются на текущую версию MySQL.
Бурхан Али
14

Я рекомендую Postgres, если это возможно. С postgres практически нет простоев при выполнении следующих процедур:

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

Я написал это немного назад, возможно, он поможет лучше понять другие достоинства.

микеликеспи
источник
6
Постгрес по-прежнему создает эксклюзивную блокировку для изменения, не позволяя другим читать из этой таблицы.
clofresh
5
Я не согласен с принципом «по существу без простоев». Как сказано в clofresh, ALTER TABLE захватывает исключительную блокировку таблицы, блокируя все одновременные операции чтения и записи. По моему опыту, для активных таблиц в большинстве случаев вы даже не получите блокировку (ALTER TABLE будет голодать). А с транзакциями вы можете легко попасть в тупик, если не будете предельно осторожны. Из-за этого я теперь всегда устанавливаю время простоя при изменении существующих таблиц в Postgres.
Панкрат
1
более подробное объяснение: dba.stackexchange.com/questions/27153/… в нем упоминаются последствия эксклюзивной блокировки и некоторые способы ее решения
Джон Даутат
4
Да, изменение таблицы в postgres захватывает эксклюзивную блокировку, но поскольку сама операция завершается за миллисекунды, в большинстве случаев это практически не имеет значения. Я лично добавил столбцы в таблицы с сотнями миллионов строк в середине рабочего дня, и в результате время простоя было нулевым.
Ной Йеттер
2
@cobbzilla Да, DROP COLUMN работает так же быстро. Под капотом он в основном отмечает столбец как скрытый. Значения, которые существовали в этом столбце до удаления, все еще находятся в файлах данных (и видимы для других транзакций) и будут оставаться таковыми до тех пор, пока вы не выполните VACUUM FULL.
Ной Йеттер
7

Поскольку вы спрашивали о других базах данных, вот некоторая информация об Oracle.

Добавление столбца NULL в таблицу Oracle - очень быстрая операция, поскольку она обновляет только словарь данных. Это удерживает исключительную блокировку таблицы в течение очень короткого периода времени. Однако это сделает недействительными все депедантные хранимые процедуры, представления, триггеры и т. Д. Они будут автоматически перекомпилированы.

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

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

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

Оттуда вы можете переключить серверы приложений на новую версию кода, и она продолжит работу. Бросьте хитрый спусковой крючок.

В качестве альтернативы вы можете использовать DBMS_REDEFINITION, который представляет собой черный ящик, предназначенный для подобных вещей.

Все это настолько утомительно для тестирования и т. Д., Что у нас просто происходит отключение питания рано утром в воскресенье, когда мы выпускаем основную версию.

WW.
источник
3

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

  • подождите, пока все изменения будут реплицированы на пассивном ведомом устройстве
  • изменить пассивное ведомое устройство на активное ведущее
  • Сделайте структурные изменения старого мастера
  • реплицировать изменения обратно с нового мастера на старый
  • выполните главную замену снова и развертывание нового приложения одновременно

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

Джинус
источник
1
Старый мастер находится вне кластера или внутри кластера?
John Chornelius 05
2

Нет. Если вы используете таблицы MyISAM, насколько я понимаю, они выполняют только блокировки таблиц - нет никаких блокировок записей, они просто пытаются сохранить все сверхбыстро за счет простоты. (Другие таблицы MySQL работают иначе.) В любом случае вы можете скопировать таблицу в другую таблицу, изменить ее, а затем переключить их, обновляя различия.

Это настолько масштабное изменение, что я сомневаюсь, что какая-либо СУБД его поддержит. Считается преимуществом в первую очередь иметь возможность делать это с данными в таблице.

dkretz
источник
InnoDB использует блокировки строк - dev.mysql.com/doc/refman/5.0/en/internal-locking.html
Эран Гальперин,
Да, MySQL - это заблуждение. Вот почему я особо остановился на «стандартных» таблицах.
dkretz
Вы написали - стандартные таблицы MySQL только блокируют таблицы - что неверно.
Эран Гальперин
Как вы интерпретируете это о таблицах MyISAM (т.е. стандарта MySQL) со страницы, которую вы процитировали? «MySQL использует блокировку на уровне таблиц для таблиц MyISAM и MEMORY, блокировку на уровне страниц для таблиц BDB и блокировку на уровне строк для таблиц InnoDB».
dkretz
некоторые механизмы хранения используют блокировку на уровне строк, а некоторые - блокировку на уровне таблицы. Стандартного механизма хранения нет (возможно, вы имели в виду значение по умолчанию в phpMyAdmin ...)
Эран Гальперин
2

Временное решение...

Другим решением может быть добавление другой таблицы с первичным ключом исходной таблицы вместе с вашим новым столбцом.

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

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

В противном случае вы можете использовать метод кластеризации, репликацию, инструмент pt-online-schema от percona.

Баласундарам
источник
1

Используя подключаемый модуль Innodb, операторы ALTER TABLE, которые только добавляют или удаляют вторичные индексы, могут выполняться «быстро», то есть без перестройки таблицы.

Однако, вообще говоря, в MySQL любая команда ALTER TABLE включает в себя перестройку всей таблицы, что может занять очень много времени (например, если в таблице содержится полезный объем данных).

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

MarkR
источник
1

Я бы порекомендовал один из двух подходов:

  1. Создавайте таблицы базы данных с учетом возможных изменений. Например, я работал с системами управления контентом, которые регулярно меняют поля данных в контенте. Вместо того, чтобы строить физическую структуру базы данных в соответствии с начальными требованиями к полям CMS, гораздо лучше создать гибкую структуру. В этом случае используется текстовое поле большого двоичного объекта (например, varchar (max)) для хранения гибких данных XML. Это значительно снижает частоту структурных изменений. Структурные изменения могут быть дорогостоящими, поэтому здесь тоже есть выгода.

  2. Имейте время на обслуживание системы. Либо система отключается во время изменений (ежемесячно и т. Д.), И изменения планируются на наименее загруженное время дня (например, 3-5 утра). Изменения производятся до развертывания производственной среды, поэтому вы будете иметь хорошую фиксированную оценку времени простоя.

2а. Иметь резервные серверы, чтобы во время простоя системы не выходил из строя весь сайт. Это позволит вам «выкатывать» обновления в шахматном порядке без остановки всего сайта.

Варианты 2 и 2а могут оказаться невозможными; они, как правило, предназначены только для крупных сайтов / операций. Однако это допустимые варианты, и я лично использовал все варианты, представленные здесь.

Pearcewg
источник
1

Если кто-то все еще читает это или приходит сюда, это большое преимущество использования системы баз данных NoSQL, такой как mongodb. У меня была такая же проблема, связанная с изменением таблицы для добавления столбцов для дополнительных функций или индексов в большой таблице с миллионами строк и большим количеством записей. Это привело бы к блокировке на очень долгое время, поэтому выполнение этой операции в базе данных LIVE расстроило бы наших пользователей. На небольших столах это может сойти с рук.

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

Стоит проверить: www.mongodb.com

Брайан Грубер
источник
2
MySQL до сих пор используется во многих системах, поэтому вопрос действительно заключается в том, как добиться изменения схемы в SQL RDBMS, хотя я тоже яростный сторонник NoSQL.
Alexy
1

В общем, ответ будет «Нет». Вы меняете структуру таблицы, что потенциально потребует большого количества обновлений », и я определенно согласен с этим. Если вы ожидаете, что будете делать это часто, я предложу альтернативу« фиктивным »столбцам - VIEWвместо этого используйте s таблиц для SELECTданных. IIRC, изменение определения представления является относительно легким, и косвенное обращение к представлению выполняется при компиляции плана запроса. Расходы состоят в том, что вам придется добавить столбец в новую таблицу и сделать посмотреть JOINв колонке.

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

Просто мысль.

Д.Шоули
источник
1

Разница между Postgres и MySQL в этом отношении заключается в том, что в Postgres он не воссоздает таблицу заново, а изменяет словарь данных, аналогичный Oracle. Следовательно, операция выполняется быстро, хотя по-прежнему требуется выделить исключительную блокировку таблицы DDL на очень короткое время, как указано выше другими.

В MySQL операция будет копировать данные в новую таблицу при блокировании транзакций, что было основной проблемой для администраторов баз данных MySQL до версии 5.6.

Хорошая новость заключается в том, что с момента выпуска MySQL 5.6 ограничение было в основном снято, и теперь вы можете наслаждаться истинной мощью базы данных MYSQL.

Дмитрий Ройзенберг
источник
3
Похоже, вы пытались создать ссылку на ссылку об изменении в MySql 5.6, но это не сработало. Пожалуйста, попробуйте еще раз.
dg99
1

Как уже упоминал Шон Дауни, pt-online-schema-changeэто один из лучших инструментов для выполнения того, что вы описали в вопросе здесь. Недавно я внес много изменений в схему в живой БД, и все прошло хорошо. Вы можете прочитать об этом в моем сообщении в блоге здесь: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/ .

Рафай
источник
1

Вам обязательно стоит попробовать pt-online-schema-change. Я использовал этот инструмент для миграции на AWS RDS с несколькими ведомыми устройствами, и он мне очень понравился. Я написал подробное сообщение в блоге о том, как сделать то, что может быть полезно для вас.

Блог: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

Рафай
источник
0

Фиктивные столбцы - хорошая идея, если вы можете предсказать их тип (и сделать их допускающими значение NULL). Проверьте, как ваш механизм хранения обрабатывает нули.

MyISAM заблокирует все, если вы хотя бы мимоходом упомянете имя стола по телефону в аэропорту. Он просто делает это ...

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

SquareCog
источник
1
Я попытался добавить столбец NULL в таблицу InnoDB, и ему пришлось перестроить всю таблицу; не простая операция «обновить метаданные».
Daniel
Я думаю, что идея заключалась в том, чтобы включить дополнительные столбцы, допускающие значение NULL, в базу данных при ее проектировании, чтобы, если требуется новая функция, можно было «добавить» новый столбец, просто начав ее использовать. У него не будет красивого имени, но если тип данных был правильно выбран / предсказан, он должен работать.
supercat
0

TokuDB может добавлять / удалять столбцы и добавлять индексы «горячо», таблица полностью доступна на протяжении всего процесса. Он доступен на сайте www.tokutek.com.

tmcallaghan
источник
-6

На самом деле, нет.

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

Если вы планируете делать это часто, вам лучше просто заполнить таблицу «фиктивными» столбцами, которые будут доступны для использования в будущем.

Уилл Хартунг
источник
3
Заполнение таблицы фиктивными столбцами кажется действительно плохой идеей.
Jost