RUNSTATS を流したのに実行時間が変わらない。インデックスを追加したのに使われない。昨日まで一瞬だったSQLが今朝から数倍かかる。Db2のSQLチューニングでよく持ち込まれるのはこの3つです。
いずれも「統計情報が古い」の一言では説明がつきません。統計・パッケージキャッシュ・実行計画は別のレイヤーで、どこが原因かによって打つ手が変わります。この記事では見る順番と、症状からの逆引きを最短でまとめます。実機での確認ログやSQL例を含む詳しい解説は末尾のZenn記事にあります。
まず見る(結論)
統計を疑う前に、そもそも統計が取れているかを確認します。SYSCAT.TABLES の CARD が -1 なら、そのテーブルは RUNSTATS が一度も走っていません。オプティマイザーは行数を知らないままデフォルト値で計画を組んでいます。
# 統計の鮮度を確認する(古い順)
db2 "SELECT TABNAME, CARD, STATS_TIME FROM SYSCAT.TABLES
WHERE TABSCHEMA = 'SCHEMA1' ORDER BY STATS_TIME ASC"
CARD に実数が入っていて STATS_TIME も今日なら、統計は容疑から外れます。次に見るのはパッケージキャッシュです。統計を取り直しても、キャッシュに残った古い実行計画がそのまま使われ続けることがあります。
症状 → 原因 → 対処(逆引き)
| 症状 | よくある原因 | まず打つ手 |
|---|---|---|
RUNSTATS を流したのに実行時間が変わらない |
パッケージキャッシュに古い実行計画が残っており、再コンパイルされていない | FLUSH PACKAGE CACHE DYNAMIC。実行後は全動的SQLが再コンパイルされるので、負荷の低い時間帯に |
| 統計は最新なのに件数の見積もりが外れる | ステータス区分のような偏りのある列で、通常の RUNSTATS では値ごとの件数を表現できない |
RUNSTATS ... WITH DISTRIBUTION AND DETAILED INDEXES ALL。取得状況は SYSCAT.COLDIST で確認 |
| インデックスを作ったのに使われない | 複合インデックスの先頭列がWHERE句に無い。複合索引は先頭列から順にしか使えない | SYSCAT.INDEXES の COLNAMES で列順を確認し、WHERE句と突き合わせる |
| WHERE句に指定した列の索引が無視される | 列に関数を掛けている(YEAR(created_at) = 2026 など) |
範囲条件に書き換える(>= '2026-01-01' AND < '2027-01-01') |
| そもそもどのSQLが遅いか分からない | アプリ側のログだけでは実行時間の内訳が追えない | MON_GET_PKG_CACHE_STMT を平均実行時間の降順で上位10件。ROWS_READ と SORT_OVERFLOWS も併せて見る |
| CPUが高止まりし、キャッシュヒット率が低い | アプリがリテラル値を直接埋め込んでおり、値が変わるたび別SQLとしてコンパイルされる | パラメーターマーカー(?)に変更。ヒット率は MON_GET_DATABASE の PKG_CACHE_LOOKUPS と PKG_CACHE_INSERTS から算出 |
| 昨日まで速かったSQLが突然遅くなった | RUNSTATS・データ量の増加・フィックスパック適用のいずれかで実行計画が入れ替わった |
EXPLAIN と db2exfmt で現在の計画を取り、平常時の出力と diff で比較 |
上から順に確認するのではなく、症状に一致する行から入ります。統計の鮮度だけを見て「最新だから統計は問題ない」と判断すると、キャッシュに残った古い計画を見落とします。
RUNSTATS は実行計画を作り直さない
RUNSTATS が更新するのはカタログの統計情報だけです。すでにパッケージキャッシュに入っている動的SQLは、キャッシュから追い出されるまで古い計画のまま実行されます。「統計を取り直したのに何も変わらない」の多くはこれで、FLUSH PACKAGE CACHE DYNAMIC を打った途端に改善します。
TBSCAN が出ていても、必ずしも悪くない
実行計画に TBSCAN(表スキャン)が出ているだけで問題視されがちですが、行数の少ない表では索引経由より表スキャンのほうが速く、オプティマイザーの判断が正しいことがあります。調査すべきは数百万行の表に TBSCAN が選ばれているケースと、推定行数が実件数と大きくずれているケースです。
それでも切り分からないとき(実機ログつきの詳解)
統計の確認SQL、分布統計の取り方、パッケージキャッシュから遅いSQLを名指しする手順、db2exfmt の出力の読み方まで含めた詳しい解説は、Zennのフル記事にまとめています。
関連: RUNSTATSコマンド解説 / REORGコマンド解説 / MON_GET 表関数による常駐監視
統計情報を取り直しても遅いままのSQLは、原因が別の場所にあります。実行計画の読み方、統計が最新でも遅くなる原因パターン、パッケージキャッシュからのボトルネックSQL特定を、本書 第9章「SQLチューニングの極意」で扱っています。
現場で引ける Db2 の教科書
現場で引ける!Db2実践エンジニア・バイブル
Db2はRDBMS市場でシェア数%。情報が少なく、頼れるのは膨大な公式マニュアルだけ―― そんな現状を変えたくて、「なぜこの値にするのか」「この障害のとき次に何を見るのか」という、マニュアルに載っていない現場の文脈を1冊にまとめました。書いてあることはすべて実際の現場で経験したことです。
アーキテクチャ/構成パラメーター選定/セキュリティ/バックアップ・リカバリ/HADR/pureScale/日常点検の自動化/メモリ・ロック競合/SQLチューニング/RUNSTATS・REORG/MON_GET監視/現場の難題集 ―― 全12章+運用シェルスクリプト集。