LayerX エンジニアブログ

LayerX の エンジニアブログです。

Coding AgentとはじめるMySQLパフォーマンスチューニング

こんにちは!バクラク申請・経費精算のエンジニアリングマネージャーをやっています、@ar_tamaです。

今回は、私たちのプロダクトで最近行ったバックエンドのパフォーマンスチューニング(スロークエリの改善)について書いてみたいと思います。比較的地味なトピックではありますが、Coding Agentをフル活用したエピソードとして、主にMySQLをバックエンドに持つアプリケーションの性能問題に直面している方の助けになれば幸いです。

発端:データ量や分布の変化で実行計画が変わった

私たちは、プロダクトごとに内部SLO(サービスレベル目標)を定めてモニタリングし、基準値を割ったらアクションすることを習慣づけています。

SLOというと大きな障害や可用性の話を想像されるかもしれませんが、特にバクラクのような業務システムでは、毎日繰り返し使う操作の遅さがそのまま利用者(お客様)の生産性低下に直結してしまうため、観測対象に主要エンドポイントの応答時間も追加しています。

今回その基準に違反したのは、複数のテーブルをJOINしてSELECTする処理のあるエンドポイントでした。これまでも必要に応じて、クエリをチューニングしたり、インデックスやインデックスヒントを追加したり、ミドルウェアを立てたり……と対策をさまざま取ってきた箇所です。今度は一体何が……?と調べたところ、たくさんのお客様にお使いいただきデータ量や分布(偏り)に変化が生じたことで、実行計画が意図どおり選ばれなくなったことが分かりました。

やったこと:AIと実行計画を眺める

対象のクエリが見えてきたので、実際に発行されているSQLとパラメータを取得し、Coding Agentと一緒にEXPLAINとEXPLAIN ANALYZEを確認しました。世は大AI時代ですが、主なやることは昔と変わりませんね。

EXPLAINは、MySQLがそのSQLをどう実行しようとしているかを見るためのものです。JOINの順序、使われるインデックス、推定行数などを確認できます。MySQLの公式ドキュメントでも、インデックスを追加すべき箇所やJOINの処理順を確認するための手段として説明されています。

一方で、EXPLAINはあくまで推定です。実際にどのくらい時間がかかったのか、どのステップでどれくらい行を読んだのかを見るには、EXPLAIN ANALYZEが役に立ちます。実際にステートメントを実行するので実行環境には注意が必要ですが、推定と実測のズレによってより精緻に改善することができます。

実際のSELECT文で双方を確認してみたところ、先述のデータ量・分布の変化によって、効かせたいはずのインデックスが選択されていなかったり、頻出のソート条件に合う複合インデックスがなくテンポラリソートが起きていたりと、大小様々な問題が発見されました。その中でも今回特に取り上げたいのは以下です。

  1. JOINの起点が期待と違い、サブテーブルに持たせている種別の絞り込みから始まっていた
    • その後、メインテーブル側で数万件規模の候補行を読み、除外条件を1行ずつ評価していた
    • → クエリの責務を分け、サブテーブルの絞り込み→結果を使ってメインテーブルの絞り込み、と2回クエリを発行
  2. 以前のパフォーマンス改善対応(NOT IN+サブクエリでフィルタ)が、むしろ大きな集合を作るような実行計画に変化していた
    • → サブクエリではなく、 カラム NOT IN (...) とクエリ自体を書き換えることで対応

1についてはEXPLAINの目視でもすぐに分かるところでしたが、2についてはEXPLAIN結果を分析したCoding Agentからの指摘で初めて気づきました。

以前の対応時は、たしかに除外対象をサブクエリとして扱うほうが効率的だったはずなのですが、現時点でのデータ分布ではそのサブクエリを作るために大きなスキャンが発生しており、インデックスを辿りながら直接条件を評価する方が速くできそう、という見立てが出されたのです。

同僚からその部分の実装意図を聞いていたこともあり、はじめは「いやいやそんな〜まさか〜」と半信半疑でしたが、修正版を試してみたところ劇的に計画が改善されました。余計なコンテキストを持たないほうが強いこともある……🥲

なお、今回はGORMを使ってクエリを発行している部分が修正の対象だったため、実際のクエリの書き換えにあたり、同僚であるponさんのgormgoldenが大変頼りになりました。アプリケーションコード上では良さそうに見えても、出力されたクエリが大事故…ということもときには生じ得るため、このようにBefore/Afterの確認を行いやすい状態にしておくことも大事ですね。

github.com

結果として、対象クエリ群のほぼ全てで改善が叶い、ベストケースでは50倍以上もの高速化が叶いました。やったぜ!

デプロイ後にアラート対象のクエリが激減した様子

Coding Agentと人間との役割分担

今回のようなクエリチューニング作業においては、Coding Agentの網羅性と手の速さがとても頼りになります。クエリ改善で振る舞いが変わってしまうことのリスクとテストの重要性は前述の通りですが、分析・複数の改善案出し・検証・実装にいたるまで、積極的にAgentに任せながら改善を進めたおかげで、従来の改善活動よりもずっと早く修正が叶いました。

振る舞いの担保についても、以下のようなソースを参照して破綻しないことを確認してもらいました。

  • ヘルプサイトや仕様書
  • 対応するクライアント(フロントエンド)の実装

これにより人の目だけでは拾いにくい検証観点も補え、動作確認も楽に行えました。

ただし、Agentは放っておくと限界までチューニングしようとしてしまいます。改善提案の中には、修正後のクエリが複雑になりすぎてメンテナンス性を損ねたり、修正量の割に改善効果が限定的だったりするものもありました。そのため計画時、または修正を走らせた後でも、「どの程度改善される見込みか」を確認しながら改善を採用する・しないの判断を行いました。

ほかにも、過去経緯などのコードや仕様に立ち現れないコンテキストを補完したり、ハルシネーションにツッコミを入れたり、計測と修正のサイクルを回すためにステップを分けたりなど、人間の仕事も思いの外残りました。とはいえ、今回の改善を回す過程で補完した文脈はドキュメントに残しましたし、モデルの進化も著しいため、次回以降はどんどん手がかからなくなっていくことが予想されます。

おわりに

今後の展望としては、今回の一連の流れを元に、SLO違反を検知→スロークエリをEXPLAINして原因を特定→修正・検証までを実行するSkillなどを作れると、改善のサイクルがより速く・多く回せそうです。

人間が本当に必要な判断に集中できるよう、またAI Agentたちにより多くの業務を任せられるよう、引き続き事業成長を支えるための改善活動を(も)進めていきます。

LayerXでは、AIと共に爆速で価値を生む・守る仲間を大大大募集しています!ご興味を持っていただけたらぜひこちらからご応募ください。

open.talentio.com

本職はマネージャーなので、AI時代のマネジメントどうなる〜?みたいな話も大好物です。カジュアル面談のお誘いもお気軽にどうぞ!

jobs.layerx.co.jp