MySQLにおけるメタデータとは
注意
こちらの記事は、2023年夏に執筆したものになります。
(Advent Calendarのために掘り出しました😇)
背景
こんにちは。残暑も残りわずかとなってきましたが、久々に怖い話を聞いたので、みなさまに共有させていただこうと思います。
それはとある機能の要件定義の際、新しく追加するテーブルの設計に想像をめぐらせていた時のことでした。
長時間のトランザクションは望ましくないという示唆を同僚からいただき、理由について説明していただきました。
「トランザクションは短くあるべき」
「なぜならトランザクションが長ければ、メタデータロックの危険性があるからだ。」
「過去に一度、1000万件程度のレコードが入ったテーブルロックに遭遇したことがある」
「そのテーブルはワークロードからの参照を持っていて、…(地獄を見た)」
みたいなお話でした。
ふむふむなるほど。……自分は絶対にそんな目に逢いたくないぞ^^`
動機
ということで、長時間のトランザクションが引き起こすメタデータロックという事象について、
後学のために調べてみることにしました。
lock!メソッドで回避できないんか〜いみたいなゆるゆるの解決法が通用するのか、考えてみます。
そもそもメタデータとは
「え?メタデータロック?DDL固有の表現で回避できないの?」
なにそれ美味しいのレベルの理解だったため、まずはメタデータについて調べてみます。
カラム名、データベース名、ユーザー名、バージョン名、および SHOW の文字列結果のほとんどがメタデータです。
引用元:
メタデータロックとは
MySQLはデータベースオブジェクトへの同時アクセスを管理し、一貫性を確保するために、オブジェクトのメタデータに対して取得するロック(メタデータロック)があります。
引用元:
オンラインDDL(Railsアプリ)における振る舞い
オンラインDDLとは
データベースが稼働中のままで構造の変更を行うことができる仕組みを指す。
テーブルの追加、削除、カラムの変更、インデックスの作成や削除などがあります。
ちなみにMySQLにおいてオンラインとは
データベースのダウンタイム、ブロック、または制限された操作のないタイプの操作。
を指します。
つまり、メタデータではなく、テーブルに対してLOCK句を発行するlock!メソッドでは不可避!?かと思いきや、migration時に危険な操作を検知してくれるgemがあるようです!
実験
それでは、実際にstrong_migrationsを使って危険なマイグレーションが検知されるようすを見てみたいと思います。
そもそもstrong_migration is 何
危険な操作を検出します
デフォルトで実行を防ぎます
安全な方法への指示を提供します
このgemをインストール&migration実行すると、危険なマイグレーションの実行についてメッセージが出力されます。
- アプリケーション再起動時、カラム削除する場合
- カラム名を変更する場合(R/Wがブロックされる)
- マイグレーション実行時、バックフィルによりテーブルがロックされる場合
- etc, etc...
ベストプラクティスが記載されていますhttps://github.com/ankane/strong_migrations#checks
今回回避してみる内容
non-uniqueで、3つインデックスを貼ってしまっている場合の例を見てみてます👀
※3つのカラムにまたがるインデックスはパフォーマンスがほとんど向上されることはない、という見解です。
ちょっとgem reading
詳細な挙動を知りたくなってきたので、少しgemリーディングしてみます。
この記述でmigrationメソッドをオーバーライドしているようです。
superで親クラスのStrongMigrationsが初期化されるようですね。
L6ではStrongMigrations::Checkerを初期化し、transaction_disabledがtrueであればDBとの接続を開始します。
ここでmethod_missingもオーバーライドしていますが、ActiveRecordのmethod_missingの前で本gemの処理を差し込み、順番に実行するようにできています。
Checkerでは!safe? || safe_by_default_method?(method)を呼び出し、migrationのパターンに応じた分岐のメソッド or シンボルを返してあげています。
🌟今回はadd_indexをしようとしたと仮定して、check_add_indexの処理の中を見ていきます。columns.is_a?(Array) && columns.size > 3 && !options[:unique]
とあるように、3以上のカラムをunique optionなしで実行しようとした場合、例外が投げられています。
したがって、今回はadd_indexのメッセージが出力されます。
実践
ということで、migrationファイルをdown→upしてみます。
上で読んでみたように、columns.size > 3
かつ unique
ではないインデックスだったら検知してくれる図です。
注意されるだろうか、ドキドキ。
ちなみにこんな内容が書かれているmigrationファイルを用います。
add_index :hogehoge, %i(hoge fuga piyo aaa), name: :idx_column_names
結果
しっかりActiveRecordのエラー表示からstrong_migrationの注意文が表示されました🙌
2回同じことを言われているのは、例外時のメッセージと、エラーメッセージを読み込むモジュールで内容が被っているからだと思われます。
教訓
危険なマイグレーションパターンを学んでおいて、いつも安全なスクリプトを書くよう心掛けること。
宣伝
弊社ではCloudSQL(MySQL 5.7)を使用しています🙌
チューニング大好き、DB設計大好きな変態のみなさん、力を貸してくださいませ〜〜!
株式会社クロスビットでは、デスクレスワーカーのためのHR管理プラットフォームを開発しています!
一緒に開発を行ってくれる各ポジションのエンジニアを募集中です。
Discussion