データベースでは、オプティマイザーは入力されたSQLクエリごとに、最適な実行計画を生成しようとします。最適な実行計画を生成するには、リアルタイムで有効な統計情報と正確な行数推定が不可欠です。統計情報は実際にはオプティマイザー統計情報(optimizer statistics)を指し、データベース内のテーブルと列の情報を記述するデータセットです。これは、コストモデルが最適な実行計画を選択するにあたり非常に重要な部分です。オプティマイザーコストモデル(optimizer cost model)は、クエリで扱われるテーブル、列、述語といったオブジェクトの統計情報に基づいて、プランの選択および最適化を行います。正確で有効な統計情報は、オプティマイザーが最適な実行計画を選択するのに役立ちます。
OceanBaseは、テーブルレベル、列レベルの基本的な統計情報とヒストグラムなどの統計情報カテゴリに対応しており、定期的な自動収集、手動収集、オンライン収集、統計情報の期限切れが深刻な場合の自動非同期収集といった収集戦略も提供しています。オプティマイザーは、更新が必要なテーブルの統計情報を定期的に収集するタスクを実行するため、通常、ほとんどのシステムにおいて、ユーザーが統計情報の具体的な問題について意識する必要はありません。しかし、APシナリオでは、非常に大きなテーブルや、大量の更新を行った後にリアルタイムクエリを提供するテーブルが存在する可能性があります。このような場合、デフォルトの統計情報収集戦略では、統計情報の収集が速やかに完了せず、実行計画の生成に影響を与える可能性があります。以下の記事では、AP部分のシナリオにおける統計情報の収集方法について、的を絞って説明を行います。
統計情報の概要
統計情報の分類
統計情報は、主に以下のカテゴリに分類することができます:
テーブル(インデックステーブルを含む)統計情報:行数、マクロブロック数、マイクロブロック数、平均行長など、テーブルのスキャンコストを推定するために使用します。
列レベルの統計情報
- 列値の分布:最大値、最小値、平均列長、異なる値の数(NDV)。
- データの偏り:ヒストグラム(Histogram)を用いてデータの分布状況を記述します。
- NULL値の割合:オプティマイザーがNULL値に関するクエリを処理するのを支援します。
OceanBaseが対応している統計情報収集方法
自動収集:オプティマイザーのスケジュールタスクは、テーブルの統計情報更新が必要かどうかを毎日分析するようデフォルトで設定されています。
手動収集:SQLコマンドを使用して統計情報の収集をトリガーすることができます。これは、非常に大きなテーブルや特定のクエリの最適化を行う際に適しています。さらに、統計情報を収集する際に、収集の並列度、収集の粒度、収集ヒストグラムのバケット数の設定といった収集戦略を指定することができます。
オンライン収集:バッチインポート、PDML、
CREATE TABLE ... ASなどのシナリオでは、GATHER_OPTIMIZER_STATISTICSヒントとシステム変数_optimizer_gather_stats_on_load(デフォルトで有効)を使用してオンライン統計情報を収集することができます。また、ダイレクトロード機能のAPPENDヒントを使用することで、オンラインでの統計情報収集を可能にすることもできます。
統計情報の更新メカニズム
しきい値トリガーの更新:テーブルデータの変化量が一定の割合(デフォルトでは10倍)を超えると、統計情報の非同期更新がトリガーされます。
パーティションテーブルへの対応:OceanBaseは、パーティションレベルでの統計情報の更新および管理に対応しています。
APシナリオにおける統計情報の最適化戦略
カスタマイズされた収集戦略
- 選択的収集:APシナリオにおいてコアクエリテーブルまたはキー列に対し、個別に収集タスクを設定します。
- パーティション優先:使用頻度が最も高い、または変化量が最も大きいパーティションを優先的に更新します。
並列度の設定
- 非常に大きなテーブルの統計情報を収集する場合、収集の並列度の設定を適切に行います。
更新頻度の動的な調整
- テーブルデータに基づく更新モードにより、統計情報の更新頻度を柔軟に設定し、不要なオーバーヘッドを回避することができます。
シナリオの例
統計情報収集ウィンドウの調整
OceanBaseオプティマイザーは、統計情報の反復的な更新を保証するために、メンテナンスウィンドウを使用して毎日自動的に統計情報の収集を行うようデフォルトで設定されています。月曜日から日曜日までのタスクのデフォルト設定は、以下の表のとおり、22:00開始で、最大収集時間は4時間です。
| メンテナンスウィンドウ名 | 開始時刻/頻度 | 最大収集時間 |
|---|---|---|
| MONDAY_WINDOW | 22:00/per week | 4 hours |
| TUESDAY_WINDOW | 22:00/per week | 4 hours |
| WEDNESDAY_WINDOW | 22:00/per week | 4 hours |
| THURSDAY_WINDOW | 22:00/per week | 4 hours |
| FRIDAY_WINDOW | 22:00/per week | 4 hours |
| SATURDAY_WINDOW | 22:00/per week | 4 hours |
| SUNDAY_WINDOW | 22:00/per week | 4 hours |
説明
- OceanBaseデータベースV4.3.5では、V4.3.5 BP1バージョンから、メンテナンスウィンドウ
SATURDAY_WINDOW及びSUNDAY_WINDOWのタスクにおけるデフォルトの開始時刻と最大収集時間が、デフォルト開始時刻6:00、最大収集時間20時間 から デフォルト開始時刻22:00、最大収集時間4時間に変更されました。 - OceanBaseデータベースV4.2.5では、V4.2.5 BP2バージョンから、メンテナンスウィンドウ
SATURDAY_WINDOW及びSUNDAY_WINDOWのタスクにおけるデフォルトの開始時刻と最大収集時間が、デフォルト開始時刻6:00、最大収集時間20時間 から デフォルト開始時刻22:00、最大収集時間4時間に変更されました。
ビジネスの実際の状況に応じて、適切なメンテナンスウィンドウを設定する必要があります。例えば、メンテナンスウィンドウがちょうどビジネスピークと重なる場合は、メンテナンスウィンドウの開始時刻を調整したり、特定の日に統計情報の収集を行わないようにすることができます。ビジネス環境によりテーブル数が非常に多い場合や、非常に大きなテーブルが多数存在する場合は、メンテナンスウィンドウの収集時間を調整することができます。
以下に設定例を示します。
-- 月曜日の自動統計情報収集を無効にする
CALL DBMS_SCHEDULER.DISABLE('MONDAY_WINDOW');
-- 月曜日の自動統計情報収集を有効にする
CALL DBMS_SCHEDULER.ENABLE('MONDAY_WINDOW');
-- 月曜日の自動統計情報収集の開始時刻を午後8時に設定する
CALL DBMS_SCHEDULER.SET_ATTRIBUTE('MONDAY_WINDOW', 'NEXT_DATE', '2022-09-12 20:00:00');
-- 月曜日の自動統計情報収集の持続時間を6時間に設定する
-- 6時間 <=> 6 * 60 * 60 * 1000 * 1000 <=> 21600000000 us
CALL DBMS_SCHEDULER.SET_ATTRIBUTE('WEDNESDAY_WINDOW', 'JOB_ACTION', 'DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC(21600000000)');
非常に大きなテーブルに対する統計情報収集戦略
非常に大きなテーブルが存在する場合、オプティマイザーのデフォルトの統計情報収集戦略では、1回のメンテナンスウィンドウでテーブルの統計情報を収集しきれない可能性があります。そのため、非常に大きなテーブルに対しては、適切な収集戦略を設定する必要があります。非常に大きなテーブルの統計情報を収集する際に、時間を要する部分として主に以下の3つがあります:
- テーブルのデータ量が膨大で、収集にはテーブルの全体スキャンが必要となり、時間がかかる。
- ヒストグラムの収集に複雑な計算が伴い、時間の消費により追加のコストが生じる。
- 大規模パーティションテーブルでは、デフォルトでサブパーティション、パーティション、テーブル全体の統計情報とヒストグラムが収集され、そのコストは3 * (cost (テーブル全体スキャン) + cost (ヒストグラム))となります。
上記の時間消費ポイントに基づき、テーブルの実際の状況および関連するクエリ状況に応じて最適化することができます。以下の方法を推奨します:
- 収集の並列度について適切なデフォルト値を設定します。なお、ビジネスへの影響を避けるため、並列度を設定した後は、関連する自動収集タスクをビジネスのオフピーク時に行うように調整する必要があります。並列度は8以下にすることを推奨します。設定方法は以下のとおりです。
-- OracleまたはMySQLの業務テナントが同一の場合:
CALL DBMS_STATS.SET_TABLE_PREFS('database_name', 'table_name', 'degree', '8');
- デフォルトの列のヒストグラム収集方法を設定します。データが均等に分布している列に対しては、ヒストグラム収集を無効にする設定とすることを検討します。
-- OracleまたはMySQLの業務テナントが同一です
-- 1. テーブルの全ての列のデータ分布が均等な場合、以下の方法を使用して全ての列のヒストグラム収集を無効に設定することができます:
CALL DBMS_STATS.SET_TABLE_PREFS('database_name', 'table_name', 'method_opt', 'for all columns size 1');
-- 2. テーブルの列のデータ分布のうち不均等なものがごくわずかである場合、ヒストグラムを収集する必要があるのはその列のみで、他の列については不要です。この場合、以下の方法により設定することができます(c1、c2はヒストグラムを収集し、c3、c4、c5は収集しません)
CALL DBMS_STATS.SET_TABLE_PREFS('database_name', 'table_name', 'method_opt', 'for columns c1 size 254, c2 size 254, c3 size 1, c4 size 1, c5 size 1');
- デフォルトのパーティションテーブルの収集粒度を設定します。hashパーティションやkeyパーティションなど、一部のパーティションテーブルについては、グローバルの統計情報のみ収集するようにするか、またはパーティションからグローバルを算出する収集方法を設定することもできます。
-- OracleまたはMySQLの業務テナントが同一です
-- 1. グローバルの統計情報のみを収集するように設定する
CALL DBMS_STATS.SET_TABLE_PREFS('database_name', 'table_name', 'granularity', 'GLOBAL');
-- 2. パーティションからグローバルを算出する収集方法を設定する
CALL DBMS_STATS.SET_TABLE_PREFS('database_name', 'table_name', 'granularity', 'APPROX_GLOBAL AND PARTITION');
- 大きなテーブルからサンプリング方式により統計情報を収集するように設定する場合、古いバージョンの場合はヒストグラムのサンプル数が大きくなり、逆効果となる可能性があるため、慎重に設定する必要があります。サンプリング方式による収集は、ヒストグラムを収集せず、基本的な統計情報のみを収集するように設定する場合にのみ適しています。
-- OracleまたはMySQLの業務テナントが同一であり、granularityを削除する場合
-- 1. すべての列のヒストグラム収集を無効に設定する:
CALL DBMS_STATS.SET_TABLE_PREFS('database_name', 'table_name', 'method_opt', 'for all columns size 1');
-- 2. サンプリング率を10%に設定する
CALL DBMS_STATS.SET_TABLE_PREFS('database_name', 'table_name', 'estimate_percent', '10');
さらに、設定済みのデフォルト収集戦略をクリア/削除する必要がある場合は、クリアする属性 {attribute} を指定するだけで、以下のように行うことができます。
-- OracleまたはMySQLの業務テナントが同一であり、granularityを削除する場合
CALL DBMS_STATS.delete_table_prefs('database_name', 'table_name', 'granularity');
関連する収集戦略を設定した後、設定に成功したかどうかを確認する必要がある場合は、以下の方法で確認することができます。
-- OracleまたはMySQLの業務テナントが同一であり、所定の並列度degreeを取得する場合
SELECT DBMS_STATS.GET_PREFS('degree', 'database_name','table_name') from dual;
上記の方式のほか、大きなテーブルの統計情報を手動で収集し終えた後、関連する統計情報をロックすることを検討することもできます。なお、テーブルの統計情報がロックされると自動収集が更新されなくなるため、データの特徴の変化がさほど大きくなく、データ値がそれほどセンシティブではないシナリオに適しています。ロックされた統計情報を再収集する必要がある場合は、まずロックを解除する必要があります。
-- OracleまたはMySQLの業務テナントが同一であり、テーブルの統計情報をロックする場合
CALL DBMS_STATS.LOCK_TABLE_PREFS('database_name', 'table_name');
-- OracleまたはMySQLの業務テナントが同一であり、テーブルの統計情報のロックを解除する場合
CALL DBMS_STATS.UNLOCK_TABLE_PREFS('database_name', 'table_name');
関連ドキュメント
統計情報に関する詳細な説明と使用ガイドについては、以下のドキュメントを参照してください:
統計情報には、テーブル統計情報(Table Level Statistics)と列統計情報(Column Level Statistics)の2つのタイプがあります。統計情報のタイプの詳細については、統計情報の概要を参照してください。
OceanBaseデータベースオプティマイザーは、統計情報の手動収集と自動収集の両方に対応しています。統計情報の収集方法に関する詳細な説明と操作ガイドについては、統計情報の収集方法の概要を参照してください。
統計情報の管理に関する詳細な操作ガイドについては、統計情報管理の章を参照してください。
簡単な例を用いて統計情報の使用方法を理解するには、例を見るを参照してください。