メニュー
Amazon Redshift
データベース開発者ガイド (API Version 2012年12月1日)

テーブルを分析する

クエリプランナーで最適な計画の構築と選択に使用される統計メタデータを定期的に更新することをお勧めします。それを行うには、テーブルを分析します。

ANALYZE コマンドを明示的に実行することで、テーブルを分析できます。表示するには

概要

COPY コマンドでデータをロードするとき、STATUPDATE オプションを ON に設定し、分析を自動的に実行できます。デフォルトでは、COPY コマンドは空のテーブルにデータをロードした後に分析を実行します。STATUPDATE を ON に設定すれば、テーブルが空であるかどうかに関係なく、分析を強制できます。STATUPDATE に OFF を指定した場合、分析は実行されません。

テーブルの所有者とスーパーユーザーのみが ANALYZE コマンドまたは STATUPDATE を ON に設定した COPY コマンドを実行できます。

データの初回ロード後に分析されなかった新しいテーブルにクエリを実行する場合、警告メッセージが表示されます。ただし、後続の更新またはロード後にテーブルにクエリを実行した場合、警告は発生しません。分析されていないテーブルを含むクエリに EXPLAIN コマンドを実行すると、同じ動作が発生します。

空ではないテーブルにデータを追加し、テーブルのサイズが大幅に変わった場合、ANALYZE コマンドを実行するか、STATUPDATE オプションを ON に設定して COPY コマンドを実行することで統計を更新することをお勧めします。最後の ANALYZE 以降に挿入または削除された行数の詳細を表示するには、PG_STATISTIC_INDICATOR システムカタログテーブルに対してクエリを実行します。

非効率なデータストレージまたはデータの統計プロファイルの大幅な変更の結果としてパフォーマンスが低下した場合、分析を実行し、統計を更新すれば問題が解消されるかどうかを確認します。

統計を構築または更新する必要がある場合、次のいずれかに対して ANALYZE コマンドを実行します:

  • 現在のデータベース全体

  • 1 つのテーブル

  • 1 つのテーブルの 1 つまたは複数の特定の列

  • クエリの述語として使用される可能性が高い列

ANALYZE コマンドを実行すると、テーブルからサンプルの行が取得され、いくつかの計算が行われ、結果的に生成される列の統計が保存されます。デフォルトでは、Amazon Redshift は DISTKEY 列にサンプルパスを実行し、テーブルのその他すべての列に別のサンプルパスを実行します。列のサブセットの統計を生成するには、カンマ区切りの列リストを指定します。述語として使用されていない ANALYZE スキップ列を持つこともできます。

ANALYZE 操作はリソースを集中的に使います。そのため、実際に統計更新を必要とするテーブルと列にのみ実行します。定期的に、または同じスケジュールですべてのテーブルのすべての行を分析する必要はありません。データが大幅に変更される場合、次で頻繁に使用される列を分析します。

  • ソートおよびグループ化の操作

  • 結合

  • クエリ述語

処理時間を短縮し、システム全体のパフォーマンスを向上させるために、Amazon Redshift は、変更された行の割合が低いテーブルの ANALYZE をスキップします。この動作は analyze_threshold_percent パラメータで決定されます。デフォルトでは、分析のしきい値は 10 パーセントに設定されます。SET コマンドを実行して、現在のセッションの分析しきい値を変更できます。

頻繁に分析する必要のない列は、大きな VARCHAR 列など、実際に問い合わされることがない事実、単位、関連属性を表す列です。たとえば、TICKIT データベースの LISTING テーブルについて考えてみます。

Copy
select "column", type, encoding, distkey, sortkey from pg_table_def where tablename = 'listing'; column | type | encoding | distkey | sortkey ---------------+--------------------+----------+---------+--------- listid | integer | none | t | 1 sellerid | integer | none | f | 0 eventid | integer | mostly16 | f | 0 dateid | smallint | none | f | 0 numtickets | smallint | mostly8 | f | 0 priceperticket | numeric(8,2) | bytedict | f | 0 totalprice | numeric(8,2) | mostly32 | f | 0 listtime | timestamp with... | none | f | 0

このテーブルに大量の新しいレコードが毎日ロードされる場合、結合キーとしてクエリで頻繁に使用される LISTID 列を定期的に分析する必要があります。TOTALPRICE と LISTTIME が頻繁に使用されるクエリの制約である場合、平日は毎日、それらの列と分散キーを分析できます。

Copy
analyze listing(listid, totalprice, listtime);

アプリケーションの販売者とイベントが非常に静的であり、日付 ID がわずか 2 年または 3 年をカバーする固定日数セットを参照する場合、これらの列の一意の値は大幅には変更されません。ただし、一意の各値のインスタンス数は着実に増加します。さらに、NUMTICKETS および PRICEPERTICKET メジャーが TOTALPRICE 列とまれに比較され、クエリされるという場合を考えてみてください。この場合、毎週末に 1 回、テーブル全体で ANALYZE コマンドを実行して、毎日分析されていない 5 つの列の統計を更新することができます。

述語列

列リストを指定する、便利な代替方法として、述語として使用される可能性が高い列のみを分析するように選択できます。クエリを実行すると、結合、フィルタ条件、または GROUP BY 句で使用される列は、システムカタログの述語列としてマークされます。PREDICATE COLUMNS 句を指定して ANALYZE を実行すると、ANALYZE 操作には次の基準を満たす列のみが含まれます。

  • 列は述語列としてマークされます。

  • 列が分散キーです。

  • 列はソートキーの一部です。

表の列のいずれも述部としてマークされていない場合、PREDICATE COLUMNS が指定されていても、ANALYZE にはすべての列が含まれます。述語の列としてマークされている列がない場合は、表がまだクエリされていない可能性があります。

ワークロードのクエリパターンが比較的安定している場合は、PREDICATE COLUMNS の使用を選択できます。クエリパターンが可変で、さまざまな列が頻繁に述語として使用される場合、PREDICATE COLUMNS を使用すると一時的に古い統計が返される場合があります。古い統計により、最適でないクエリ実行プランと長い実行時間につながる可能性があります。ただし、次に PREDICATE COLUMNS を使用して ANALYZE を実行すると、新しい述語の列が含まれます。

述語の列の詳細を表示するには、次の SQL を使用して PREDICATE_COLUMNS という名前のビューを作成します。

Copy
CREATE VIEW predicate_columns AS WITH predicate_column_info as ( SELECT ns.nspname AS schema_name, c.relname AS table_name, a.attnum as col_num, a.attname as col_name, CASE WHEN 10002 = s.stakind1 THEN array_to_string(stavalues1, '||') WHEN 10002 = s.stakind2 THEN array_to_string(stavalues2, '||') WHEN 10002 = s.stakind3 THEN array_to_string(stavalues3, '||') WHEN 10002 = s.stakind4 THEN array_to_string(stavalues4, '||') ELSE NULL::varchar END AS pred_ts FROM pg_statistic s JOIN pg_class c ON c.oid = s.starelid JOIN pg_namespace ns ON c.relnamespace = ns.oid JOIN pg_attribute a ON c.oid = a.attrelid AND a.attnum = s.staattnum) SELECT schema_name, table_name, col_num, col_name, pred_ts NOT LIKE '2000-01-01%' AS is_predicate, CASE WHEN pred_ts NOT LIKE '2000-01-01%' THEN (split_part(pred_ts, '||',1))::timestamp ELSE NULL::timestamp END as first_predicate_use, CASE WHEN pred_ts NOT LIKE '%||2000-01-01%' THEN (split_part(pred_ts, '||',2))::timestamp ELSE NULL::timestamp END as last_analyze FROM predicate_column_info;

LISTING テーブルに対して次のクエリを実行するとします。LISTID、LISTTIME、および EVENTID は、結合、フィルタ、および GROUP BY 句で使用されることに注意してください。

Copy
select s.buyerid,l.eventid, sum(l.totalprice) from listing l join sales s on l.listid = s.listid where l.listtime > '2008-12-01' group by l.eventid, s.buyerid;

次の例に示すように、PREDICATE_COLUMNS ビューをクエリすると、LISTID、EVENTID、および LISTTIME が述語の列としてマークされていることがわかります。

Copy
select * from predicate_columns where table_name = 'listing';
schema_name | table_name | col_num | col_name | is_predicate | first_predicate_use | last_analyze ------------+------------+---------+----------------+--------------+---------------------+-------------------- public | listing | 1 | listid | true | 2017-05-05 19:27:59 | 2017-05-03 18:27:41 public | listing | 2 | sellerid | false | | 2017-05-03 18:27:41 public | listing | 3 | eventid | true | 2017-05-16 20:54:32 | 2017-05-03 18:27:41 public | listing | 4 | dateid | false | | 2017-05-03 18:27:41 public | listing | 5 | numtickets | false | | 2017-05-03 18:27:41 public | listing | 6 | priceperticket | false | | 2017-05-03 18:27:41 public | listing | 7 | totalprice | false | | 2017-05-03 18:27:41 public | listing | 8 | listtime | true | 2017-05-16 20:54:32 | 2017-05-03 18:27:41

テーブルの現在の統計を維持するには、以下の手順を実行します。

  • クエリを実行する前に ANALYZE コマンドを実行します。

  • 定期的なロードまたは更新サイクルが終わるたびに、データベースに ANALYZE コマンドを定期的に実行します。

  • 作成した新しいテーブルと大幅に変更された既存のテーブルまたは列に ANALYZE コマンドを実行します。

  • クエリでの使用と変更傾向に基づき、異なるタイプのテーブルおよび列に対し、異なるスケジュールで ANALYZE 操作を実行することを考慮します。

  • 時間とクラスターリソースを節約するには、ANALYZE を実行するときに PREDICATE COLUMNS 句を使用します。