ALTER TABLE на великій таблиці з індексованою колонкою


14

У мене є велика таблиця зі стовпцем VARCHAR (20), і мені потрібно змінити її, щоб стати стовпцем VARCHAR (50). Як правило, виконання ALTER TABLE (додавання TINYINT) на цій конкретній таблиці займає приблизно 90-120 хвилин, тому я дійсно можу це зробити лише в ніч на суботу чи неділю, щоб не впливати на користувачів бази даних. Якщо можливо, я хотів би зробити цю модифікацію до цього.

Стовпчик також індексується, що, я вважаю, зробить ТАБЛИЧУ ПОВІТЬ повільнішим, оскільки він повинен відновити індекс після зміни довжини стовпця.

Веб-додаток налаштовано в середовищі реплікації MySQL (26 рабів і один ведучий). Я пригадую, колись десь читав, що один метод - це спочатку виконати ПОРІДНУЮ ТАБЛИЦЮ на кожному підлеглому (мінімізуючи вплив на користувачів), потім зробити це на Master, але чи не буде потім спробувати повторити команду ALTER TABLE на раби?

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

Редагувати: таблиця InnoDB.


додавання, що стовпець tinyint означав насправді додавання стовпця зі значенням за замовчуванням? Тому що робити це на величезному столі може зайняти багато часу ..
Маріан

Відповіді:


13

Якщо ви трохи пригодний, ви можете взяти справи в свої руки, виконуючи НАЗАДУ ТАБЛИЦЮ на етапах, які ви можете бачити. Припустимо, таблиця, яку ви хочете змінити, називається WorkingTable. Ви можете виконати зміни на етапах:

#
#  Script 1
#  Alter table structure of a single column of a large table
#
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Ви можете виконувати це на всіх рабах. Що з майстром ??? Як ви заважаєте цьому поширюватися на рабів. Просте: не надсилайте SQL у бінарні журнали майстра. Просто вимкніть двійковий журнал у сеансі перед тим, як робити ALTER TABLE:

#
#  Script 2
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#
SET SQL_LOG_BIN = 0;
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Але зачекайте !!! Що з новими даними, які надходять під час обробки цих команд ??? Перейменування таблиці на початку операції повинно зробити трюк. Дозвольте трохи змінити цей код, щоб запобігти введенню нових даних з цього приводу:

#
#  Script 3
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#  and preventing new data from entering into the old table
#
SET SQL_LOG_BIN = 0;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
CREATE TABLE WorkingTableNew LIKE WorkingTableOld;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;
  • Сценарій 1 може бути виконаний на будь-якому підлеглому, у якого не ввімкнено бінарні журнали
  • Сценарій 2 може бути виконаний на будь-якому підлеглому, у якого ввімкнено бінарні журнали
  • Сценарій 3 може бути виконаний у майстер або будь-де інше

Спробувати !!!


2
Я бачу одну проблему, якщо таблиця містить поле 'auto_increment'. Я зробив основний тест і був здивований, побачивши, що СТВОРИТИ ТАБЛИЦЮ. LIKE не копіює значення auto_increment в нову таблицю
Дерек Дауні

1
@DTest: Хороший ловець і чудовий коментар !!!. Я вірю, що ви можете отримати значення auto_increment з стовпця information_schema.tables AUTO_INCREMENT. Якщо в таблиці немає поля AUTO_INCREMENT, стовпець AUTO_INCREMENT у information_schema.tables буде NULL. В іншому випадку воно буде містити необхідне значення AUTO_INCREMENT. Я здогадуюсь, що це може бути скриптовано, щоб витягнути це значення, яке НУЛЬНЕ, і зробити ALTER TABLE WorkingSet AUTO_INCREMENT = <числовий номер>; перед тим, як перейменувати таблицю темпів назад у WorkingSet.
RolandoMySQLDBA

@RolandoMySQLDBA Інше, що можна зробити, - це створити WorkingTableNew, скопіювавши оператори "show create table WorkingTable" та змінивши значення поля auto_increment, додавши його на безпечне для вас число, а також змінивши стовпчик на varchar (50 ). ви можете виконати команду "вставити в ... з"
Гаутам Сомані

4

Моя здогадка з документації полягатиме в тому, що просто збільшення обмеження довжини на a varcharне спричинить таких же проблем, як додавання стовпця:

Для деяких операцій можлива вставка ALTER TABLE, яка не потребує тимчасової таблиці:

Але це, мабуть, суперечить коментарям до цього питання ОГ .

EDIT

Принаймні на 5,0, я думаю, що можу підтвердити, що збільшення довжини дійсно вимагає тимчасової таблиці (або якоїсь іншої не менш дорогої операції):

тестова площадка:

create table my_table (id int auto_increment primary key, varchar_val varchar(10));
insert into my_table (varchar_val)
select 'HELLO'
from (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s1,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s2,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s3,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s4,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s5,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s6;

результат:

alter table my_table modify varchar_val varchar(20);
Query OK, 1000000 rows affected (2.91 sec)

alter table my_table add int_val int;
Query OK, 1000000 rows affected (2.86 sec)

Зміна розміру поля варшара передбачає перевірку того, що ви не перевищуєте новий розмір. Для збільшення розміру цей чек МОЖЕ бути оптимізований.
BillThor

3

Я думав, згадаю про це з часу ENGINE=INNODB

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


Шабанг !!! Це так правда. У такому випадку можна лише затриматися, якщо зробити таблицю alter на оригінальній таблиці. Як мінімум, вам, можливо, доведеться грати в ігри з обмежувальними обмеженнями, перш ніж відбудеться НАЗАД. +1 за цей дуже хороший улов !!!
RolandoMySQLDBA
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.