web-dev-qa-db-ja.com

インデックス付きの列を持つ大きなテーブルに対するALTER TABLE

VARCHAR(20)列を持つ大きなテーブルがあり、それをVARCHAR(50)列になるように変更する必要があります。通常、この特定のテーブルでALTER TABLE(TINYINTを追加)を実行すると、完了するまでに約90〜120分かかるため、データベースのユーザーへの影響を避けるために、土曜日または日曜日の夜にのみ実行できます。可能であれば、その前にこの変更を行いたいと思います。

列にもインデックスが付けられます。これにより、列の長さを変更した後にインデックスを再構築する必要があるため、ALTER TABLEが遅くなります。

WebアプリはMySQLレプリケーション環境(26スレーブと1マスター)でセットアップされています。ある場所を読んだときに、まず各スレーブでALTER TABLEを実行し(ユーザーへの影響を最小限に抑え)、次にマスターでこれを実行することを思い出しましたが、その後、ALTER TABLEコマンドをスレーブに複製しようとしませんか?

だから私の質問は:ユーザーへの混乱を最小限に抑えてこのテーブルを変更するための最良の方法は何ですか?

編集:テーブルはInnoDBです。

14
Matt Healy

少し冒険したい場合は、目に見える段階で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はマスターまたは他のどこでも実行できます

試してみる !!!

13
RolandoMySQLDBA

documentation からの私の推測では、単にvarcharの長さの制約を増やしても、列を追加するのと同じ問題は発生しません。

一部の操作では、一時テーブルを必要としないインプレースALTER TABLEが可能です。

しかし、これはこの [〜#〜] so [〜#〜] 質問のコメントで矛盾しているようです。

編集

少なくとも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)

ENGINE=INNODB以来、

外部キー制約がある場合、古いテーブル(現在は名前が変更されています)を指す制約がないと、変更や名前の変更はできません。後で変更するか、期間の制約を削除する必要があります。

3
randomx