ALTER TABLE для большой таблицы с индексированным столбцом


14

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

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

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

Поэтому мой вопрос: каков наилучший способ изменить эту таблицу с минимальными помехами для моих пользователей?

Изменить: таблица InnoDB.


добавление столбца tinyint означало добавление столбца со значением по умолчанию? Потому что выполнение этого на огромном столе может занять много времени ..
Marian

Ответы:


13

Если вы немного любите приключения, вы можете взять дело в свои руки, выполнив ALTER TABLE поэтапно, как вы можете видеть. Предположим, таблица, которую вы хотите изменить, называется 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'. Я провел базовый тест и был удивлен, увидев, что CREATE TABLE .. LIKE не копирует значение auto_increment в новую таблицу
Дерек Дауни,

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

@RolandoMySQLDBA Другая вещь, которую можно сделать, - это создать WorkingTableNew, скопировав операторы «show create table WorkingTable» и изменив значение поля auto_increment, добавив его на безопасное для вас число, а также изменив столбец на varchar (50 ), а затем выполнить оба переименования в одном операторе: «переименовать таблицу WorkingTable в WorkingTableOld, переименовать таблицу WorkingTableNew в WorkingTable». Выполнение обоих переименований в одной команде гарантирует, что ни одна вставка не завершится неудачно (проверено, что это Prod для таблицы, которая получает 1000 вставок / с). Затем Вы можете сделать команду «вставить в ... из»
Гаутам Сомани

4

Я предполагаю из документации , что простое увеличение ограничения длины varcharне вызовет таких же проблем, как добавление столбца:

Для некоторых операций возможна ALTER TABLE на месте, которая не требует временной таблицы:

Но это , кажется, противоречит в комментариях по этому С.О. вопросу.

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

По крайней мере, на 5.0, я думаю, я могу подтвердить, что увеличение длины действительно требует временной таблицы (или какой-либо другой столь же дорогой операции):

Testbed:

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)

Изменение размера поля varchar включает проверку того, что вы не превышаете новый размер. Для увеличения размера эта проверка МОЖЕТ быть оптимизирована.
BillThor

3

Я думал, я бы упомянул, что с ENGINE=INNODB

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


Шабанг !!! Это так верно. В этом случае можно застрять только с изменением таблицы на исходной таблице. По крайней мере, вам, возможно, придется играть в игры с отключением ограничений, прежде чем произойдет ALTER TABLE. +1 за этот очень хороший улов !!!
RolandoMySQLDBA
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.