コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
統計情報とカーディナリティ
統計情報とカーディナリティ
このレッスンで分かること
- 統計情報がプランナにとって何を意味するか
- カーディナリティ・選択率・ヒストグラムの関係
- 統計を最新化する運用テクニック
統計情報とカーディナリティ とは
プランナがコストを計算するために使う統計の仕組み。本レッスンでは、統計情報とカーディナリティ の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
カーディナリティと選択率
カーディナリティ (Cardinality) とは、あるカラムに含まれる 異なる値の数 です。
| カラム | カーディナリティ | 例 |
|---|---|---|
| user_id | 1,000,000 | 全ユーザー数 |
| 1,000,000 | 1 ユーザー 1 メアド | |
| age | 100 | 0 〜 100 歳 |
| gender | 2 | M / F |
| is_deleted | 2 | true / false |
カーディナリティが高いほど、インデックスが効きます。gender = 'F' は半分の行を返す可能性があり、インデックスを使うより全表走査の方が速い、ということもあります。
選択率 (Selectivity) はカーディナリティの逆数で、「フィルタを通過する行の割合」を意味します。
email = ?の選択率は1 / 1,000,000≒ 0gender = ?の選択率は1/2 = 0.5
選択率が低い(数字が小さい)ほど、インデックスが速くなります。
プランナが行数を見積もる仕組み
SELECT * FROM users WHERE age > 30 AND country = 'JP' を考えます。プランナは次の手順で行数を見積もります。
- テーブルの総行数 を統計から取得 → 1,000,000
country='JP'の選択率 をヒストグラムから推定 → 0.3age > 30の選択率 をヒストグラムから推定 → 0.5- 独立と仮定して掛け算 → 1,000,000 × 0.3 × 0.5 = 150,000
これがプランナの推定行数です。実際の行数とこれが大きくズレていると、悪い計画が選ばれます。
ヒストグラム
カラムの値分布を粗く記録したものが ヒストグラム です。例えば age カラムの分布を 10 階級に分けて、各階級の頻度を保持します。
プレーンテキスト
age 0-10 : 5000 行
age 10-20 : 30000 行
age 20-30 : 80000 行
age 30-40 : 50000 行
...WHERE age > 30 ならその右側を合計して推定値を出します。ヒストグラムがないとカラムの最小・最大から線形補間するため、偏りに弱くなります。
統計情報の中身
主要 DB が持つ統計情報の例です。
- テーブル行数
- カラムごとのカーディナリティ (
distinct count) - カラムごとの最小・最大値
- NULL 率
- ヒストグラム (PostgreSQL は標準、MySQL も 8.0 でサポート)
- 複数カラムの相関 (PostgreSQL の
CREATE STATISTICS)
これらは pg_stats (PostgreSQL) や、ヒストグラムは INFORMATION_SCHEMA.COLUMN_STATISTICS・インデックスのカーディナリティは INFORMATION_SCHEMA.STATISTICS (いずれも MySQL) で確認できます。
統計が古くなると何が起きるか
統計は テーブル作成時、ANALYZE 実行時、自動メンテナンス時 に更新されます。データが大幅に変わった後で統計が古いと、悲惨な計画が選ばれます。
実例として「テーブル作成直後に大量挿入したが ANALYZE を忘れた」場合、プランナは「テーブルは空」と勘違いし、Nested Loop で巨大テーブルをスキャンします。クエリが数時間止まる事故もよく起きます。
大量データ挿入の 直後 に
ANALYZEを実行する習慣をつけよう。
統計更新の運用
PostgreSQL
autovacuum が VACUUM と一緒に ANALYZE も実行します。デフォルトはテーブルの 10% が変更されたら走ります。テーブルが極端に大きい / 小さい場合はテーブル単位で閾値を調整します。
SQL クエリ
ALTER TABLE big_table SET (autovacuum_analyze_scale_factor = 0.01); -- 1% で起動MySQL InnoDB
innodb_stats_auto_recalc で自動再計算します。サンプリングが粗いことがあるので、重要テーブルは ANALYZE TABLE を明示的に実行することもあります。
SQL クエリ
ANALYZE TABLE users;サンプリングの落とし穴
統計は 全行スキャン ではなく サンプリング で取られます。デフォルトでは少なくとも 1 万〜数十万行が対象で、極端な偏りはサンプルから漏れることがあります。
サンプル外れ値が多い場合は、PostgreSQL の default_statistics_target を上げます(デフォルト 100 → 1000)。これでヒストグラムのビン数が増え、精度が上がります。
複数カラムの相関問題
(country, city) を独立と仮定すると、country='JP' AND city='Tokyo' の推定が外れます。city='Tokyo' の行は ほぼ確実に country='JP' だからです。
PostgreSQL は CREATE STATISTICS で相関も記録できます。
SQL クエリ
CREATE STATISTICS stat_geo (dependencies) ON country, city FROM users;
ANALYZE users;これで「city が決まれば country もほぼ決まる」という関係を統計に含められます。
EXPLAIN で統計のズレを発見
EXPLAIN ANALYZE の rows=80 (actual rows=8000) のような乖離は統計エラーのサインです。
| 推定 / 実測 | 解釈 |
|---|---|
| 推定 << 実測 | 統計が古い、または相関を見落とし |
| 推定 >> 実測 | データが減ったのに統計が古い |
| ほぼ一致 | 統計は健全 |
統計の精度がクエリ性能を決める。「インデックスを貼ったのに遅い」の犯人は統計が古いことが多い。
やってみよう
- PostgreSQL で
SELECT * FROM pg_stats WHERE tablename = 'users'を実行し、自分のテーブルのヒストグラムを観察する - MySQL で
ANALYZE TABLE前後のEXPLAIN出力を比較する - 1 万件挿入直後に
ANALYZEを実行しないと推定がどう外れるか実験する
よくある質問
Q. このトピックは実務でどう役立ちますか?
A. DB のクエリ最適化、API 設計、データ構造の選択など、設計判断の根拠になります。表面的にライブラリを使うだけでなく「なぜそれが速いのか」を理解できると、性能問題を未然に防げます。コーディング面接でも頻出のテーマです。
Q. 計算量はどう求めれば良いですか?
A. ループのネストごとに掛け算する、再帰なら漸化式から解く、というのが基本です。Big-O 表記は定数倍と低次の項を無視するため、n の指数(n²、n log n など)に注目してください。最悪・平均・最良の 3 つを意識すると説得力が増します。
Q. 覚えるべき定番アルゴリズムは何ですか?
A. 二分探索、クイックソート/マージソート、BFS/DFS、ダイクストラ、DP の基本問題(フィボナッチ・ナップサック)が必修です。これらを「白紙から書ける」状態にすると、応用問題が一気に解けるようになります。
次のレッスン
次は レプリケーション で、1台のDBの変更を複数のコピーへ反映する仕組みと、同期方式の違い・レプリカラグが生む整合性問題を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 統計情報とカーディナリティ の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 統計情報とカーディナリティ とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン