АЛЬТЕР ТАБЛИЦЯ, не замикаючи стіл?


107

Під час виконання оператора ALTER TABLE в MySQL вся таблиця зчитується з блоком читання (дозволяючи одночасно читати, але забороняючи одночасне записування) протягом тривалості оператора. Якщо це велика таблиця, заяви INSERT або UPDATE можуть бути заблоковані протягом певного часу. Чи є спосіб зробити «гарячу зміну», як-от додати стовпець таким чином, щоб таблиця все ще оновлювалася протягом усього процесу?

Переважно мене цікавить рішення для MySQL, але мене зацікавлять інші RDBMS, якщо MySQL не може цього зробити.

Для уточнення, моя мета - просто уникнути простоїв, коли нова виробнича функція, яка потребує додаткової колонки таблиці, буде висунута до виробництва. Будь-яка схема бази даних буде змінюватися з часом, це просто факт життя. Я не бачу, чому ми повинні прийняти, що ці зміни неминуче повинні спричинити простої; це просто слабко.


2
Потрібно задуматися, скільки разів ви будете змінювати стіл?
Аллайн Лалонде

1
Зміни схеми баз даних IMHO пов’язані з цілими новими версіями - вони не випускаються спорадично, як це роблять інші зміни. Це неминуче велика справа.
dkretz

9
@AllainLalonde - більше 0 разів робить це питання законним, особливо якщо час простою у вашій системі коштуватиме життя чи багато грошей. І в будь-якому випадку нові вимоги до програмного забезпечення іноді з’являються.
Натан Лонг

Відповіді:


60

Єдиний інший варіант - зробити вручну все, що робить багато систем RDBMS ...
- Створіть нову таблицю

Потім можна скопіювати вміст старої таблиці за шматок одночасно. Хоча завжди будьте обережні до будь-яких ВСТАВЛЕННЯ / ОНОВЛЕННЯ / УДАЛЕННЯ у вихідному столі. (Можна керувати тригером. Хоча це спричинить уповільнення, це не блокування ...)

Після закінчення змінити ім'я вихідної таблиці, а потім змінити назву нової таблиці. Переважно в операції.

Після завершення перекомпілюйте всі збережені процедури тощо, які використовують цю таблицю. Плани виконання, ймовірно, більше не діють.

Редагувати:

Деякі зауваження були зроблені щодо цього обмеження, яке було трохи поганим. Тож я подумав, що я покладу на це нову перспективу, щоб показати, чому це так, як це ...

  • Додавання нового поля - це як зміна одного поля в кожному рядку.
  • Field Locks було б набагато складніше, ніж Lock Lock, незважаючи на блокові таблиці.

  • Ви фактично змінюєте фізичну структуру на диску, кожен запис рухається.
  • Це дійсно схоже на ОНОВЛЕННЯ на всю таблицю, але з більшим впливом ...

2
І перед тим, як проводити заміну, мати ретельний план тестування. Якщо це не вдається, починайте спочатку.
dkretz

2
Управління синхронізацією через тригери було приємною ідеєю. Я так довго використовую MySQL, що я постійно забуваю, що в них є тригери. Я використовував цю техніку, і тепер у мене є функціональний сценарій гарячої зміни. З смужкою прогресу. І це працює з MyISAM. Життя чудове.
Даніель

2
+1 Це буквально те, що робить менеджер SQL Enterprise за кадром, коли ви вносите певні типи змін таблиці в інтерфейсі користувача. У SQL 2008 вони фактично додали попередження, щоб користувач знав, що виконує цю різку дію.
BradC

2
Ви нічого не згадували про зовнішні ключі, що посилаються на таблиці, які змінюються. Хіба це не буде проблемою?
Рафай

2
@MohammadRafayAleem - І АВТОМОБІЛЬНІ поля, і види, і тригери, і т. Д., Але навіть незважаючи на це, підхід досі працює.
MatBailie

42

Percona створює інструмент під назвою pt-online-schema-change, який дозволяє це зробити.

Він по суті робить копію таблиці та змінює нову таблицю. Щоб тримати нову таблицю синхронізованою з оригіналом, для оновлення використовує тригери. Це дозволяє отримати доступ до оригінальної таблиці, поки нова таблиця готується у фоновому режимі.

Це схоже на запропонований вище метод Dems, але це робиться в автоматизованому вигляді.

Деякі з їх інструментів мають криву навчання, а саме підключення до бази даних, але як тільки ви її знизите, вони є чудовими інструментами.

Наприклад:

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 = ЕКСКЛЮЗИВНО), дозволити запити, але не DML (LOCK = пункт розділеного), або дозволити повний запит та DML доступ до таблиці (LOCK = пункт NONE). Якщо ви опустите пункт LOCK або вкажете LOCK = DEFAULT, MySQL дозволяє отримати якомога більше одночасності залежно від типу операції.

Внесення змін на місці, де це можливо, а не створення нової копії таблиці, дозволяє уникнути тимчасового збільшення використання дискового простору та накладних витрат, пов'язаних з копіюванням таблиці та реконструкцією вторинних індексів.

див. посібник MySQL 5.6 -> InnoDB та Інтернет-DDL для отримання додаткової інформації.

Схоже, що онлайн-DDL також доступний в MariaDB

Крім того, ви можете використовувати ALTER ONLINE TABLE, щоб переконатися, що ваша ALTER TABLE не блокує одночасні операції (не замикає). Це еквівалентно LOCK = NONE.

MariaDB KB про ПОДІЛЬНУ ТАБЛИЦЮ


3
Прикро, що немає іншого способу, окрім голосів, щоб проплисти це до вершини, враховуючи, що це здебільшого заперечує всі інші відповіді чисто тому, що вони більше не посилаються на поточну версію MySQL.
Бурхан Алі


14

Я рекомендую Postgres, якщо це варіант. З postgres фактично немає простоїв із наступними процедурами:

Інша чудова особливість полягає в тому, що більшість операторів DDL є транзакційними, тому ви можете зробити цілу міграцію в рамках транзакції SQL, і якщо щось піде не так, вся справа повернеться назад.

Я писав це трохи раніше, можливо, це може пролити ще трохи розуміння інших достоїнств.


6
Postgres все ще створює ексклюзивний замок на alter, заважаючи іншим читати з цієї таблиці.
clofresh

5
Я не погоджуюся з бітом "фактично без простоїв". Як сказав Clofresh, ALTER TABLE захоплює ексклюзивний замок на столі, блокуючи всі одночасні читання та записи. На мій досвід, для активних столів більшість випадків ви навіть не отримаєте блокування (ALTER TABLE буде голодувати). І при транзакціях ви можете легко закінчитись тупиком, якщо ви не будете надзвичайно обережні. Через це я завжди встановлюю простої під час зміни існуючих таблиць у Postgres.
Панкрат

1
більш детальне пояснення: dba.stackexchange.com/questions/27153/… в ньому згадуються наслідки ексклюзивного блокування та деякі способи
подолання

4
Так, зміна таблиці в postgres захоплює ексклюзивний замок, але оскільки сама операція завершується за мілісекунди, це в більшості випадків практично не має значення. Я особисто додав стовпці до стомільйонних таблиць у середині робочого дня з нульовим наслідком простою.
Ной Ітер

2
@cobbzilla Так, ДРОП КОЛЮН так само швидко. Під кришкою те, що він в основному робить, позначає стовпчик як прихований. Значення, які існували в цьому стовпці раніше, ніж було скинуто, все ще знаходяться у файлах даних (і видимі для інших транзакцій), і залишатимуться такими, якщо і до тих пір, поки ви не зробите VACUUM FULL.
Ной Ітер

7

Оскільки ви запитували про інші бази даних, ось деякі відомості про Oracle.

Додавання стовпця NULL до таблиці Oracle - це дуже швидка операція, оскільки вона лише оновлює словник даних. Це тримає ексклюзивний замок на столі на дуже короткий проміжок часу. Однак це призведе до недійсності будь-яких збережених процедур, поглядів, тригерів тощо. Вони будуть перекомпільовані автоматично.

Звідти при необхідності можна створити індекс за допомогою пункту ONLINE. Знову ж таки, лише дуже короткі словники блокуються. Він прочитає всю таблицю, шукаючи речі для індексації, але нікого не блокує, роблячи це.

Якщо вам потрібно додати іноземний ключ, ви можете зробити це і змусити Oracle довіряти вам, що дані є правильними. В іншому випадку потрібно прочитати всю таблицю і перевірити всі значення, які можуть бути повільними (спершу створіть свій індекс).

Якщо вам потрібно ввести значення за замовчуванням або обчислене значення в кожен рядок нового стовпця, вам потрібно буде запустити масове оновлення або, можливо, трохи корисну програму, яка заповнює нові дані. Це може бути повільним, особливо якщо рядки стають набагато більшими і більше не поміщаються в їхні блоки. Блокуванням можна керувати під час цього процесу. Оскільки старий версино вашої програми, який все ще працює, не знає про цей стовпець, вам може знадобитися підлий тригер або вказати за замовчуванням.

Звідти ви можете зробити перехід на своїх серверах додатків до нової версії коду, і він буде продовжувати працювати. Скинь свій підлий курок.

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

Все це настільки турбує тестування тощо, що у нас просто виникає ранній недільний ранок, коли ми випускаємо основну версію.


3

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

  • чекайте, коли всі зміни будуть повторені на пасивному рабі
  • змінити пасивного раба, щоб бути активним господарем
  • зробити структурні зміни старого майстра
  • повторити зміни назад від нового головного до старого
  • зробіть головний замін знову та розгортання нового додатку одночасно

Це не завжди просто, але це працює, як правило, за 0 простоїв! Другий вузол не повинен бути лише пасивним, його можна використовувати для тестування, статистики чи резервного вузла. Якщо у вас немає інфраструктури, реплікація може бути налаштована в межах однієї машини (з двома екземплярами MySQL).


1
Старий господар знаходиться поза кластером чи всередині кластеру?
Іоан Чорнелій

2

Ні. Якщо ви використовуєте таблиці MyISAM, наскільки я найкраще розумію, вони роблять лише блоки столів - не існує записів, вони просто намагаються зберегти все надшвидко за допомогою простоти. (Інші таблиці MySQL діють по-різному.) У будь-якому випадку ви можете скопіювати таблицю в іншу таблицю, змінити її, а потім переключити їх, оновивши для відмінностей.

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


InnoDB використовує блокування рядків - dev.mysql.com/doc/refman/5.0/uk/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, якщо ви не готові чекати або не змінюєте крихітні таблиці.


1

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

  1. Створіть таблиці баз даних з урахуванням потенційних змін. Наприклад, я працював із системами управління вмістом, які регулярно змінюють поля даних у вмісті. Замість того, щоб будувати фізичну структуру бази даних, щоб відповідати початковим вимогам поля CMS, набагато краще будувати гнучку структуру. У цьому випадку, використовуючи текстове поле для крапки (наприклад, varchar (max)) для зберігання гнучких XML-даних. Це робить структурні зміни дуже рідкими. Структурні зміни можуть бути дорогими, тому тут також є користь.

  2. Майте час обслуговування системи. Або система відключається в режимі офлайн під час змін (щомісяця тощо), і зміни проводяться в найменший час, коли торгують людьми (наприклад, 3-5 ранку). Зміни проводяться до початку випуску, тож ви матимете хорошу фіксовану оцінку вікна простою.

2а. Майте зайві сервери, щоб у випадку простою весь сайт не виходив з ладу. Це дозволить вам "розгортати" свої оновлення поетапно, не знімаючи весь сайт.

Варіанти 2 та 2а можуть бути недоцільними; вони, як правило, лише для великих сайтів / операцій. Однак вони є дійсними варіантами, і я особисто використав усі варіанти, представлені тут.


1

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

Я ненавиджу той факт, що ми маємо "розробляти наші таблиці, щоб не змінювати їх". Я просто не думаю, що це працює в сучасному світі веб-сайтів. Ви не можете передбачити, як люди будуть використовувати ваше програмне забезпечення, тому ви швидко змінюєте речі на основі відгуків користувачів. За допомогою mongodb ви можете додавати "стовпці" за бажанням без простоїв. Ви навіть не додаєте їх, ви просто вставляєте дані в нові стовпці, і це робиться автоматично.

Варто перевірити: www.mongodb.com


2
MySQL до цих пір використовується в багатьох системах, тому питання справді полягає в тому, як домогтися зміни схеми в SQL RDBMS, хоча я і гарячий прихильник NoSQL.
Олексій

1

Загалом, відповідь буде «Ні». Ви змінюєте структуру таблиці, яка потенційно потребуватиме багато оновлень ", і я, безумовно, з цим погоджуюся. Якщо ви розраховуєте робити це часто, то я запропоную альтернативу" фіктивним "стовпцям - використовуйте VIEWs замість таблиць для SELECTпередачі даних. IIRC, зміна визначення представлення є відносно легкою, а перехід через подання робиться при складанні плану запитів. Витрата полягає в тому, що вам доведеться додати стовпець до нової таблиці та зробити перегляд JOINу стовпці.

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

Просто думка.


1

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

У MySQL операція буде копіювати дані в нову таблицю, блокуючи транзакції, що було головним болем для DBA MySQL до версії 5.6.

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


3
Схоже, ви намагалися зв’язати посилання на зміну MySql 5.6, але це не вийшло. Будь ласка спробуйте ще раз.
dg99

1

Як згадував SeanDowney, 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

Стовпці-манекени - це гарна ідея, якщо ви можете передбачити їх тип (і зробити їх нульовими). Перевірте, як ваш двигун зберігання обробляє нулі.

MyISAM заблокує все, якщо ви навіть згадати ім'я таблиці, проходячи попутно, по телефону, в аеропорту. Це просто так ...

За словами, замки насправді не така вже й велика справа; до тих пір, поки ви не намагаєтеся додавати значення нового стовпця за замовчуванням до кожного рядка, але нехай воно стоїть як нулеве, а ваш накопичувач досить розумний, щоб не писати його, у вас повинно бути гаразд із блокуванням, який є лише тримається досить довго, щоб оновити метадані. Якщо ви все-таки намагаєтесь написати нове значення, ну, ви тости.


1
Я спробував додати стовпчик NULL до таблиці InnoDB, і він повинен був відновити всю таблицю; не проста операція "оновлення метаданих".
Даніель

Я думаю, що ідея полягала в тому, щоб додаткові, нульові стовпці включати в базу даних, коли вона розроблена, так що якщо потрібна нова функція, можна "додати" новий стовпець, просто почавши його використовувати. Він не матиме приємного імені, але якщо тип даних був правильно обраний / передбачений, він повинен працювати.
supercat

0

TokuDB може додавати / видаляти стовпці та додавати індекси "гарячими", таблиця є повністю доступною протягом усього процесу. Він доступний через www.tokutek.com


-6

Не зовсім.

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

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


3
Набивання столу манекенними стовпцями здається дійсно поганою ідеєю.
Jost
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.