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


14

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

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

Webアプリは、MySQLレプリケーション環境(26個のスレーブと1個のマスター)でセットアップされます。一度どこかで、1つの方法は最初に各スレーブでALTER TABLEを実行し(ユーザーへの影響を最小限に抑える)、次にマスターでこれを行うことを思い出しますが、それからALTER TABLEコマンドをスレーブに複製しようとしませんか?

私の質問は次のとおりです。ユーザーの混乱を最小限に抑えてこのテーブルを変更する最良の方法は何ですか?

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


そのtinyint列を追加することは、実際にデフォルト値を持つ列を追加することを意味しますか?巨大なテーブルの上に長い時間がかかることができることをやっているので...
マリアン

回答:


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
問題の1つは、テーブルに「auto_increment」フィールドが含まれているかどうかです。私は基本的なテストを行なったし、TABLEを作成することを見て驚きました... LIKEは、新しいテーブルにAUTO_INCREMENT値をコピーしません
デレク・ダウニー

1
@DTest:良いキャッチと素晴らしいコメント!!!。information_schema.tables列AUTO_INCREMENTからauto_increment値を取得できると思います。テーブルにAUTO_INCREMENTフィールドがない場合、information_schema.tablesのAUTO_INCREMENTカラムはNULLになります。それ以外の場合は、必要なAUTO_INCREMENT値が含まれます。NULL以外の値を抽出し、ALTER TABLE WorkingSet AUTO_INCREMENT = <somenumber>;を実行するスクリプトを作成できると思います。一時テーブルの名前をWorkingSetに戻す直前。
RolandoMySQLDBA

@RolandoMySQLDBA他にできることは、「show create table WorkingTable」ステートメントをコピーしてWorkingTableNewを作成し、auto_incrementフィールドの値を安全な数値でさらに変更することで変更します。また、列をvarchar(50 )、その後、単一のコマンドで両方の名前を変更します「テーブルWorkingTableの名前をWorkingTableOldに変更し、テーブルWorkingTableNewの名前をWorkingTableに変更」両方の名前変更を単一のコマンドで実行すると、挿入が失敗しないことが保証されます「…に挿入」コマンドを実行できます
ゴータムソマニ

4

ドキュメントからの私の推測は、単に長さの制約を増やすだけでは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)

varcharフィールドのサイズを変更するには、新しいサイズを超えていないことを確認する必要があります。サイズを大きくするには、このチェックを最適化してください。
-BillThor

3

私はそれを言及したいと思いました ENGINE=INNODB

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


シャバン!!! それは本当です。その場合、元のテーブルでalter tableを実行することだけにこだわることができます。少なくとも、ALTER TABLEが実行される前に、制約を無効にしてゲームをプレイする必要があります。その非常に良いキャッチのために+1!
-RolandoMySQLDBA
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.