ゲームラグ白書 › L12 データベース
インデックスのないクエリ Missing index / full table scan
原因ID db-no-index · 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・DBインフラ
図と実験のあるメインページでこのカードを開く →
インデックスがないと、条件に合う行を探すためにテーブル全体を読む必要があります(フルスキャン)。
なぜ 新機能のデプロイで、インデックスのない条件検索が追加される → すると 数百万行をすべてスキャンし、クエリ1つに数百ms〜数秒 → 画面では 郵便受け・取引履歴のロード遅延、コネクションがふさがって他の要求まで待たされる
- 症状
- 入力遅延, 接続不可・無限ロード
- 要因
- 遅延, ストール
- 誰に起きるか
- 特定の機能だけ, サーバー全体
- いつ
- 特定の操作をしたとき
- 担当
- 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・DBインフラ
- ゲーム開発チームの対応
- 新しいクエリはデプロイ前に実行計画をレビュー、インデックスの追加、更新系のクエリ(UPDATE・DELETE)もインデックスが効くか確認。
- インフラチームの対応
- スロークエリログの監視、フルスキャンのクエリを見つけてゲーム開発チームに共有、運用中のインデックス追加はロックの短いオンライン方式で適用。
- 数値の目安
- インデックスがあれば数ms、なければデータ量に比例して遅くなり、大きなテーブルでは数百〜数万倍。
- グラフでは
- ある時点から階段状に上昇 · DBクエリの遅延、読み取った行数
- 確認箇所
- MySQLはslow query log(log_queries_not_using_indexesを有効にすると、インデックスを使わなかったクエリも記録)のRows_examined・Rows_sent、performance_schemaのevents_statements_summary_by_digestのSUM_NO_INDEX_USED・SUM_ROWS_EXAMINEDを確認し、EXPLAINを実行。PostgreSQLはpg_stat_user_tablesのseq_scan・seq_tup_readを確認し、EXPLAINを実行
- 該当する場合
- デプロイ後に新しく現れたクエリが、返した行(Rows_sent)より数千倍多い行を読み(Rows_examined)、EXPLAINにテーブルのフルスキャン(MySQLはtype ALL、PostgreSQLはSeq Scan)が出る。大きなテーブルのseq_tup_readがデプロイ時刻から急激に増える
- 該当しない場合
- インデックスが効いているのに遅いなら、ロック待ち(db-hot-row、db-ddl-lock)か実行計画の変化(db-plan-flip)。小さなテーブルのフルスキャンは正常なこともある
- 確認手段
- インフラのツールで確認(ゲームコード不要)
- もっと詳しく
- 影響は読み込みが遅くなるだけにとどまりません。インデックスなしの更新系クエリ(UPDATE・DELETE)は、DBによってはスキャンした行までロックし、無関係なプレイヤーの保存まで止めてしまうことがあります。
出典
- How MySQL Uses Indexes MySQL
インデックスがないと先頭行からテーブル全体を読み、テーブルが大きいほどコストが増える - Locks Set by Different SQL Statements in InnoDB MySQL
適切なインデックスがなくテーブル全体をスキャンすると、すべての行がロックされ、他のユーザーの挿入まで止まる - The Slow Query Log MySQL
long_query_time(デフォルト10秒)を超えたクエリを記録、インデックスを使わないクエリも別途記録可能 - CREATE INDEX (PostgreSQL Documentation) PostgreSQL
CONCURRENTLYで作成すると書き込みを止めずにインデックスを作成、通常の作成は終わるまで書き込みを止める - Statement Summary Tables MySQL
events_statements_summary_by_digest:同じ形のクエリごとのSUM_NO_INDEX_USED(インデックスなしで実行した回数)・SUM_ROWS_EXAMINED - EXPLAIN Output Format MySQL
typeがALLならテーブルのフルスキャン、通常はインデックスを追加して回避 - The Cumulative Statistics System (PostgreSQL Documentation) PostgreSQL
pg_stat_user_tablesのseq_scan(シーケンシャルスキャンの回数)・seq_tup_read(シーケンシャルスキャンで読んだ行数) - Using EXPLAIN (PostgreSQL Documentation) PostgreSQL
Seq Scan:テーブルのすべての行を順に読む実行計画
あわせて読みたい原因
同じ層:L12 データベース
同じ症状(入力遅延)を起こすほかの層の原因
図と実験のあるメインページでこのカードを見る