Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

リアル事例から読み解くWebパフォーマンスチューニングの勘所/Offers web perfo...

リアル事例から読み解くWebパフォーマンスチューニングの勘所/Offers web performance tuning

FUJIWARA Shunichiro

December 14, 2023
Tweet

More Decks by FUJIWARA Shunichiro

Other Decks in Technology

Transcript

  1. @fujiwara (X(Twitter), GitHub, Bluesky) 面白法人カヤック SREチーム ISUCON 優勝 4回 ISUCON

    運営(出題) 4回 Amazon ECSデプロイツール github.com/kayac/ecspresso
  2. 負荷試験時点での最終的なボトルネックのクエリ SELECT ... l.is_xxx AND h.id IS NOT NULL AS

    isXXX, ... FROM l LEFT JOIN (SELECT ... FROM h) INNER JOIN u ON l.uid=u.uid INNER JOIN ... ORDER BY isXXX DESC, ... LIMIT 100 ORDER BY している isXXX はJOIN後の演算結果によって決まるbool値 false のものを下に落としたかったためらしい… INDEXは使えないため l テーブルをフルスキャンする実行計画に 100行しか必要ないのに2000行程度をスキャンしていた
  3. なぜ致命的ではない(はずだった)クエリが致命傷になったのか SELECT ... l.is_xxx AND h.id IS NOT NULL AS

    isXXX, ... FROM l LEFT JOIN (SELECT ... FROM h) INNER JOIN u ON l.uid=u.uid INNER JOIN ... ORDER BY isXXX DESC, ... LIMIT 100 負荷試験では、 l テーブル(約2,000行)のフルスキャンになっていた 本番では u テーブル (8万行以上)のフルスキャン になっていた!
  4. なぜ実行計画が変わってしまったのか INDEXはどちらにちゃんとある 行数もほぼ同様 オプティマイザの気まぐれ…? ではない INNER JOIN u ON l.uid=u.uid

    -- <- 犯人 この uid カラムは VARCHAR(255) 負荷試験環境では問題なくINDEXで結合できていた 本番環境ではINDEXが使えずにフルスキャンになっていた ヒント 本番環境のDBはMySQL 5.7からアップグレードしていた 負荷試験環境のDBは最初からMySQL 8.0だった
  5. どうやって原因究明したか 当日自分はオンコールを受けなかった 担当者が調査したがすぐには判明せず、アクセスが減って自然回復したので翌日に持ち 越し 翌日調査の結果 RDS Performance Insightsで、問題のクエリが大きな負荷になっている rows が当初の想定よりもかなり多い(10万行レベル)

    問題のクエリが発行されているAPIのレスポンスタイムが2秒程度から徐々に10秒ま で遅くなっている アクセスログから、10秒付近が上限になっているリクエストが多い(クライアントタ イムアウト) 問題のクエリを発行しているAPIへのリクエストが増加→DBのCPU負荷が限界に→クラ イアントがタイムアウトからのリトライ、と断定
  6. モニタリングが足りなかった問題 普段はMackerelでモニタリングすることが多い この案件ではMackerelを入れられなかった。CloudWatchとRDS Performance Insights のみ 自分が MySQL(RDS, Aurora) で見るメトリクス筆頭

    > InnoDB rows read InnoDBから何行を読み取っているか 単位時間あたりに読み込む行数がほぼCPU負荷に直結(比例することが多い) 発行しているクエリ数と比較して、読み込む行数が多い = indexが使えていない or 使え ていても非効率なクエリが多いのがすぐ分かる ISUCON本にも3章でMySQLの負荷を見るところで最初に書いた話 しかし、CloudWatchには実はこのメトリクスはない