統計情報の概要と最適化の仕組み
Oracle Databaseでクエリのパフォーマンスを最大限に引き出すには、コストベース最適化(CBO)を適切に機能させる必要があります。CBOはテーブルやインデックスの統計情報を参照し、実行コストが最小となる「実行計画」を選択します。
統計情報の収集には、主にANALYZEコマンドとDBMS_STATSパッケージの2つの手法が用いられます。かつて利用されていたルールベース最適化(RBO)とは異なり、CBOはデータの分布状況によって挙動が変わるため、常に最新の統計情報を維持することが重要です。
統計情報の収集手法
- 完全計算(Compute): テーブル全体をスキャンして正確な統計を取得します。データ量が多い場合は処理時間を要します。
- 推定計算(Estimate): 指定した割合や行数のみをサンプリングして統計を推測します。大規模なテーブルに適しています。
ANALYZEコマンドの使用シーンと制約
ANALYZEコマンドは、統計情報の収集以外にも、構造の検証や行移行の特定に使用されます。ただし、OracleはCBO向けの統計情報収集にはDBMS_STATSの使用を推奨しています。
ANALYZEの主な用途
- インデックス、テーブル、クラスターの統計情報の収集・削除
- データ構造の妥当性検証(VALIDATE STRUCTURE)
- 行移行(Row Migration)や行連結(Row Chaining)の調査
基本的な構文例
-- テーブル全体の統計情報を完全に収集
ANALYZE TABLE emp_records COMPUTE STATISTICS;
-- 25%のサンプリングによる推定統計の収集
ANALYZE TABLE sales_data ESTIMATE STATISTICS SAMPLE 25 PERCENT;
-- インデックスの構造検証
ANALYZE INDEX idx_sales_id VALIDATE STRUCTURE;
-- 収集した統計情報の削除
ANALYZE TABLE old_logs DELETE STATISTICS;
DBMS_STATSパッケージによる高度な管理
現代のOracle運用では、並列処理やパーティション管理に優れたDBMS_STATSが主流です。CBOが利用する統計情報のほとんどはこのパッケージによって生成されます。
主要なプロシージャの活用例
-- スキーマ全体の統計情報を収集
EXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'APP_USER');
-- 特定のテーブルとそのインデックスを並列度4で収集
EXEC DBMS_STATS.GATHER_TABLE_STATS( -
ownname => 'SALES_MANAGER', -
tabname => 'MONTHLY_REPORT', -
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, -
degree => 4, -
cascade => TRUE);
-- 特定のインデックスのみを収集
EXEC DBMS_STATS.GATHER_INDEX_STATS('APP_USER', 'IDX_PROD_CODE');
自動統計情報収集タスクの管理
Oracle 11g以降では、デフォルトで自動統計情報収集タスク(auto optimizer stats collection)がスケジュールされています。通常、平日夜間や週末に実行されますが、業務負荷に合わせて調整が必要な場合があります。
タスク状態の確認と制御
-- 自動収集タスクのステータス確認
SELECT client_name, status
FROM dba_autotask_client
WHERE client_name = 'auto optimizer stats collection';
-- 自動収集タスクの無効化
BEGIN
DBMS_AUTO_TASK_ADMIN.DISABLE(
client_name => 'auto optimizer stats collection',
operation => NULL,
window_name => NULL
);
END;
/
実行時間の変更
デフォルトの実行ウィンドウ(MAINTENANCE_WINDOW_GROUP)を変更することで、システムの閑散期にタスクを移動できます。
-- ウィンドウの繰り返し間隔と実行時間を変更(例:月曜の午前3時から3時間)
BEGIN
DBMS_SCHEDULER.SET_ATTRIBUTE(
name => 'SYS.MONDAY_WINDOW',
attribute => 'REPEAT_INTERVAL',
value => 'FREQ=WEEKLY;BYDAY=MON;BYHOUR=3;BYMINUTE=0;BYSECOND=0');
DBMS_SCHEDULER.SET_ATTRIBUTE(
name => 'SYS.MONDAY_WINDOW',
attribute => 'DURATION',
value => numtodsinterval(180, 'minute'));
END;
/
統計情報の確認用SQL
収集された統計情報が適切か、最後にいつ更新されたかを確認するために以下のビューを参照します。
-- テーブルの最終解析日時と行数の確認
SELECT table_name, num_rows, blocks, last_analyzed
FROM user_tables
WHERE table_name = 'PRODUCT_MASTER';
-- インデックスの統計情報の確認
SELECT index_name, blevel, leaf_blocks, clustering_factor, last_analyzed
FROM user_indexes
WHERE table_name = 'PRODUCT_MASTER';
-- パーティションごとの統計情報確認
SELECT partition_name, num_rows, last_analyzed
FROM user_tab_partitions
WHERE table_name = 'LOG_TABLE_PART';
統計情報のバックアップと復元
実行計画が予期せず変更されるリスクに備え、統計情報をテーブルにエクスポートして保存しておくことが可能です。
-- 統計情報保存用のテーブルを作成
EXEC DBMS_STATS.CREATE_STAT_TABLE('APP_USER', 'SAVED_STATS_TABLE');
-- テーブル統計をエクスポート
EXEC DBMS_STATS.EXPORT_TABLE_STATS('APP_USER', 'CRITICAL_TABLE', NULL, 'SAVED_STATS_TABLE');
-- 統計情報を以前の状態にインポート(復元)
EXEC DBMS_STATS.IMPORT_TABLE_STATS('APP_USER', 'CRITICAL_TABLE', NULL, 'SAVED_STATS_TABLE');
運用の注意点
ANALYZE TABLE ... VALIDATE STRUCTUREを実行すると、対象のテーブルにDML共有ロック(Sロック)がかかるため、トランザクションが頻繁に発生するオンライン時間帯での実行は避けるべきです。一方、DBMS_STATSによる通常の統計収集は、基本的にDML操作をブロックしませんが、リソース消費量(CPU・I/O)に留意して実行計画を立てる必要があります。