オンラインDDLのWaiting for table metadata lockについて
(1) トランザクション中に対象のテーブルをSELECT
A: begin;
A: select * from target_table where id=1;
(2) 対象のテーブルにオンラインDDLを実行
B: CREATE INDEX a_target_table_index ON target_table (field1, field2);
-- show processlist;
-- Waiting for table metadata lock | CREATE INDEX a_target_table_index...
(3) 別のトランザクションで対象のテーブルをSELECT
C: begin;
C: select * from target_table where id=2;
-- Waiting for table metadata lock | CREATE INDEX a_target_table_index...
-- Waiting for table metadata lock | select * from target_table...
(4) Aのトランザクションを終了するとDDLは実行開始
A: rollback;
-- altering table | CREATE INDEX a_target_table_index...
(5) Cのトランザクションをそのまま放置するとほどなく
-- Waiting for table metadata lock | CREATE INDEX a_target_table_index...
(6) Cのトランザクションを終了
C: rollback;
(7) Bは即完了
- ターゲットのテーブルを参照するトランザクション(A)が開始中はオンラインDDLは開始を待つ
- Aが終了するとオンラインDDLは開始される
- オンラインDDLがWaiting for table metadata lockで待っている間に、別のトランザクション(C)が始まる
- オンラインDDLは終了時にCのトランザクション終了を待つ
- Cが終了するとオンラインDDLは即完了する
つまり、オンラインDDL開始時に長いAのトランザクションがあった場合、Cにあたるトランザクションが積み重なってシステムが死ぬ可能性がある。
(6)のタイミングでさらに別のトランザクションを追加した場合はオンラインDDLの終了を阻害したりはしない模様。つまりAのトランザクションが実行中の(1)〜(4)間が問題。
回避方法としては、DDLを流してすぐprocesslistをチェックし、 Waiting for table metadata lock
になっていたら、killする、というのを altering table
になるまで繰り返す、と思われます。