WEBサイトを運用をしている場合、仕様の追加変更やパフォーマンス対策など、様々な理由で、テーブルの構造を途中で変更する必要性が出てきます。
このような場合、通常では一時的にWEBサイトを停止し、メンテナンス期間中にDBにALTERをします。

しかし、このような機会が頻繁に得られない場合などは、稼働中に変更しなければならないケースもあるかと思います。
そこで今回は、EC2でMySQL(VP編 VPってなんじゃ?)で紹介したVPを利用して、
停止することなくテーブルにALTERをする方法を紹介したいと思います。

初期のテーブルが以下の通りとしますと、

create table gift(
  id int auto_increment,
  gift_name varchar(255),
  description text,
  created_at datetime not null,
  primary key(id)
)engine=InnoDB;

これに削除日(deleted_at)を追加し、下記のように変更したいとします。

create table gift(
  id int auto_increment,
  gift_name varchar(255),
  description text,
  created_at datetime not null,
  deleted_at datetime,
  primary key(id)
)engine=InnoDB;

それでは、VPを利用して元のテーブルと新規テーブルを入れ替えてみます。
まず、初期イメージは以下の通りです。

新しいスキーマのテーブルと、入れ替え用のダミーのテーブルを用意し、VPテーブルで新スキーマテーブルとダミーテーブルを繋げておきます。

mysql> create table gift_new(
   id int auto_increment,
   gift_name varchar(255),
   description text,
   created_at datetime not null,
   deleted_at datetime,
   primary key(id)
 )engine=InnoDB;
Query OK, 0 rows affected (0.03 sec)

mysql> create table gift_dummy like gift;
Query OK, 0 rows affected (0.01 sec)

mysql> create table gift_vp(
   id int auto_increment,
   gift_name varchar(255),
   description text,
   created_at datetime not null,
   primary key(id)
 )engine=vp
 comment 'table_name_list "gift_dummy gift_new", cit "2", cil "2", ctm "1", ist "1", zru "1"';
Query OK, 0 rows affected (0.01 sec)

VPテーブルには、Spiderと同様、基本設定以外にも様々なオプションがあり、ここでは、cit、cil、ctm、ist、zruというオプション値を指定しています。
これらはそれぞれ以下のような意味があります。(マニュアルから抜粋)

  • choose_ignore_table_list(cit)
    検索時に利用するテーブルの選択から指定した番号のテーブルを除外する。
    デフォルト値は指定なし。
  • choose_ignore_table_list_for_lock(cil)
    ロック付検索時に利用するテーブルの選択から指定した番号のテーブルを除外する。
    更新は行われる。
    デフォルト値は指定なし。
  • choose_table_mode(ctm)
    検索時に利用するテーブルの選択モード。
    サーバパラメータvp_choose_table_modeが設定されている場合は、そちらが優先される。
     0:最適化モード。
     1:「table_list」の前からの記載順に利用するテーブルを決定する。
    デフォルト値は 0
  • infomation_source_table(ist)
    テーブルステータス取得のモード。
     0 :全ての子テーブルからテーブルステータス取得を行う。
     1-:指定した子テーブルからのみテーブルステータス取得を行う。
    デフォルト値は 0
  • zero_record_update_mode(zru)
    0件更新の際のモード。
     0:何もしない。
     1:choose_ignore_table_list_for_lockの対象子テーブルであった場合は、insertする。
    デフォルト値は 0

つまり、cit:2、cil:2、ctm:1、ist:1、zru:1という設定の意味は、

VPテーブルに対して検索されたときにgift_newは使用しない
VPテーブルに対して検索されたときにgift_dummyを使用する
VPテーブルのステータスチェックにはgift_dummyのステータスを使用する
VPテーブルの更新対象が0件であった場合は、INSERTする

ということを示します。

次に、各テーブルをリネームします。
gift_dummyは必要なくなったのでgift_deleteに、元のgiftをVPの接続先であるgift_dummyに、VPテーブルのgift_vpをgiftにそれぞれ変更します。
これにより、VPテーブルの接続先テーブルが、オリジナルのテーブルと新スキーマのテーブルに切り替わり、アプリケーションからのアクセスはVPテーブルに瞬時に切り替わります。

mysql> rename table 
  gift_dummy to gift_delete,  
  gift to gift_dummy,  
  gift_vp to gift;
Query OK, 0 rows affected (0.03 sec)

vp_copy_tables()を実行します。
vp_copy_tablesはVPに付属されているUDFで、VPテーブルを介して、子テーブル間でデータコピーを 行うことができます。
コピー元テーブルリストとコピー先テーブルリストのテーブルが、指定した親テーブルからのみ更新される場合、
コピー中にそのVPテーブルに行われた更新もコピー先にコピーされるため、テーブルへの更新を止めずにコピーすることができます。

mysql> select vp_copy_tables('gift', 'gift_dummy', 'gift_new');

そして、コピーが完了したら、テーブルのリネームを再度行います。
新スキーマのgift_newをgiftに、giftだったVPテーブルをgift_vpに変更します。

mysql> rename table 
  gift to gift_vp,  
  gift_new to gift;

最後に、必要のなくなったテーブルを削除します。

mysql> drop table 
  gift_dummy,
  gift_vp, 
  gift_delete;

このように、データアクセスをさせたまま、テーブル定義の変更とデータ移行をすることができました。

こちらの記事はなかの人(memorycraft)監修のもと掲載しています。
元記事は、こちら