
2026/08/30 4:15
安全性を謳った MySQL のアップグレードが、実際にはそれほど安全ではなかった件
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
改善された要約
このテキストは、ライフサイクル終了(EOL)アップグレード後に発生した重大な本番環境の災害について詳しく説明しています。"グリーン"レプリカデータベースへのアップグレードにより、プライマリキー ID がシフトした結果として深刻な障害が発生しました。根本原因は、特定のテーブル(テーブル X)に対して
ALTER TABLE を使用して AUTO_INCREMENT プライマリーキーを追加した以前のマイグレーションに起因します。このマイグレーションでは、6 つの関連テーブルを更新ステートメントで更新しましたが、ソースとレプリカの間での複製動作が異なっていました。
ソースデータベースは MySQL の
MIXED バイナリログ形式を使用していました。5 つの関連テーブルについて、更新は STATEMENT モードで複製され、これはレプリカ上で更新ロジックを安全に再実行してローカル ID を生成するものでした。しかし、自動インクリメント列を有するテーブル X については、MIXED 設定下で MySQL が自動的に複製を ROW モードへ切り替えました。これにより、MySQL はソースからシフトされた ID 値(例:旧 ID 1 が ID 26 へ)をレプリカに盲目的にコピーし、更新ロジックを再実行する代わりに、データ整合性を損なうような正しくない行へのポインタを持つ 6 つの参照テーブルの一つが破綻しました。この事件は、厳格な AUTO_INCREMENT 動作のための検証チェックを実装する前にレプリカ更新を行わない場合、予想外の自動インクリメントの不整合が多テーブルにわたる外鍵参照を静かに汚染し、マイグレーションリスクを主要な障害に変える可能性があることを強調しています。本文
MySQL アップグレード中の「AUTO_INCREMENT」不一致事故と原因分析
背景:アップグレードと移行計画
AWS のデータベース拡張サポート費用やエンド・オブ・ライフ(EOL)通知を受け、早急なアップグレードが必要となりました。当初の移行計画は以下の通りです。
- 手順: グリーン環境(待機用レプリカ)を先にアップグレードし、動作確認を行う。
- スワッチオーバー: 問題がなければ本番への切り替えを実施。
- 想定: シンプルで確実なプロセスと考えられていました。
発生した事故:ID 参照不一致
アップグレードから約 1 時間後、以下の深刻な不具合が発生しました。
- 現象: テーブル X の特定行が、アップグレード前の ID
から新しい ID1
に変更されていました。26 - 影響範囲: テーブル X は他テーブルから参照されており、そのうち 5 つのテーブルは正常に動作していましたが、残り 1 つのテーブルだけが破綻しました。
- 不整合: 残りの 1 つのテーブルは古い ID(1)を参照し続けており、実際には異なる行を指すようになっていました(致命的なデータ不一致)。
原因:マージョン作業と ALTER TABLE
事故の前段階として、以下の DDL/DML 操作が実施されていました。
1. テーブル X の改変
アップグレードの数週間前、テーブル X に新しい AUTO_INCREMENT カラムを追加する作業が行われました。
ALTER TABLE X ADD COLUMN id INT NOT NULL AUTO_INCREMENT PRIMARY KEY;
2. 関連テーブルの更新
6 つの関連テーブルを介して古い ID から新しい ID へ参照先を変更する UPDATE 文が実行されました。
UPDATE some_table JOIN x ON x.old_id = some_table.x_old_id SET some_table.x_id = x.id;
3. AUTO_INCREMENT の特性
MySQL ドキュメントによると、ソース側とレプリカ側に
AUTO_INCREMENT カラムを追加する際、ID の割り当て順序は保証されません。
- ID 生成順序はストレージエンジンや行の処理順序に依存します。
- ソースとレプリカで異なる ID が付与される可能性があります。
根本原因:バイナリログ形式の違い (STATEMENT vs ROW)
真の要因は MySQL の複製機能における「バイナリログ形式」の設定にあります。
バイナリログ形式の種類
MySQL は以下の 3 つの方式をサポートしています。
- STATEMENT: SQL 文そのものを記録し、レプリカ側で再実行します。
- ROW: 行レベルの変更内容を記録し、レプリカ側で直接適用します。
- MIXED: デフォルトは STATEMENT ですが、安全な保証が得られない場合は自動的に ROW に切り替わります。
今回のケースでの挙動
ソース側の
binlog_format は MIXED で設定されていました。
正常に動作した 5 つのテーブル(STATEMENT 形式)
- 判定: UPDATE 文の結果として ID が変わるだけなので、ステートメントベースで安全とみなされました。
- 処理: バイナリログに UPDATE 文自体が記録され、レプリカ側でも同様の文が実行されました。
- 結果: レプリカのローカル ID 照会に基づき、正しい値が再計算・適用されました。
破綻した 1 つのテーブル(ROW 形式へ強制)
- 判定: MySQL はこのテーブルを「ステートメントベースの複製にとって不安全」と判断しました。
- 理由: このテーブルだけが
カラムを持つことに加え、特定のカウントアップロジックが関与しているためです。AUTO_INCREMENT - 処理: 自動的に ROW モード に切り替えられ、バイナリログには「ID が 26 になった」という変更内容(行データのみ)だけが記録されました。レプリカ側では元の UPDATE 文は実行されず、ソースからの差分がそのまま適用されました。
- 結果: ソースの ID
がそのままコピーされたため、レプリカ上の実際の行番号(ID 順序)との不一致が発生しました。26
結論と教訓
今回の事故は、**「AUTO_INCREMENT カラムを持つテーブル」**が MIXED 形式下で自動的に ROW 形式に切り替わり、結果として ID 同期のバグを引き起こしたことに起因しています。
重要な注意点
- 予期せぬ障害: レプリケーションの設定や挙動を深く理解していない場合、問題が見過ごされやすいです。
- 迅速な拡大: 本番環境では小さな不一致もすぐに深刻な障害(ディザスター)に発展し、事後的な原因解明が困難になります。
- 対策: レプリケーションを使用する際は、特に AUTO_INCREMENT カラムを含むテーブルのアップグレードや DDL 変更には十分な注意とテストが必要です。