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 может быть выполнен на мастере или в любом другом месте

Попробуйте!

RolandoMySQLDBA
источник
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)
Джек говорит, попробуйте topanswers.xyz
источник
Изменение размера поля varchar включает проверку того, что вы не превышаете новый размер. Для увеличения размера эта проверка МОЖЕТ быть оптимизирована.
BillThor
3

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

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

randomx
источник
Шабанг !!! Это так верно. В этом случае можно застрять только с изменением таблицы на исходной таблице. По крайней мере, вам, возможно, придется играть в игры с отключением ограничений, прежде чем произойдет ALTER TABLE. +1 за этот очень хороший улов !!!
RolandoMySQLDBA