#はじめに
Redshiftにも他のDBMSと同様、統計情報を取得するためのAnalyzeコマンドがある。
ただ、AWSさんの「運用機能を可能な限り自動化し、開発者が本来注力すべき業務処理に注力できるように」との思いによって各種機能が年々Auto化(個人的には分散キーのAuto化が最後の砦だと思っていたので、去年のre:Inventはインパクトが大きかった)されたことで、Analyzeコマンドを発行する機会は減ってきていると思われる。
本記事では2021/4現在における自動で発行されるAnalyzeを整理するとともに、Analyzeを手動で発行した方がよいケースについて個人的な見解を整理する。
#実機環境
リージョン:東京
インスタンス:dc2.large × 1ノード
※Redshiftのバージョンは2021/4/3時点での最新バージョンを使用
#自動的に発行されるAnalyeze
Redshiftで自動的に発行されるAnalyzeには大きく2種類存在。
①コマンドとセットで発行されるAnalyze
②AutoAnalyze機能により発行されるAnalyze
#コマンドとセットで発行されるAnalyze
Analyzeがセットで(自動的に)発行されるコマンドは下記4つ。
①COPY(STATUPDATEオプションとCOPY先テーブルのデータ状態により発行有無が変わる)
②CRETATE TALBE AS
③CREATE TEMP TABLE AS
④SELECT INTO
参考:Redshift開発者ガイド内「テーブルを分析する」
また上記の派生として、下記コマンドについてもAnalyzeが発行される。
⑤CREATE MATERIALIZED VIEW
⑥REFRESH MATERIALIZED VIEW(増分更新の場合は発行されない)
※細かな挙動については記事の後半の「MATERIALIZED VIEWに関する挙動について」に記載
#AutoAnalyze機能により発行されるAnalyze
COPYコマンドでtbl1テーブルにデータを格納し、AutoAnalyze機能の動きをSTL_ANALYZEテーブルから観察。
db1=# select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, CONVERT_TIMEZONE('JST',a.starttime) as starttime_j,is_auto,is_background,substring(auto_analyze_phase,1,20) from STL_ANALYZE a join stv_tbl_perm t on t.id=a.table_id order by starttime;
xid | name | status | rows | modified_rows | starttime_j | is_auto | is_background | substring
-------+--------+-----------------+----------+---------------+----------------------------+---------+---------------+----------------------
7027 | tbl1 | Full | 20000000 | 20000000 | 2021-03-28 08:30:45.877363 | t | f | Not Applicable ---COPYコマンドでデータをLoad
7711 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 08:43:08.084746 | t | t | (Phase 1) Processing
7711 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 08:43:08.085832 | t | t | (Phase 2) Processing
7711 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 08:43:08.08619 | t | t | (Phase 3) Processing
10959 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 09:43:09.346016 | t | t | (Phase 1) Processing
10971 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 09:43:25.863039 | t | t | (Phase 2) Processing
10971 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 09:43:25.863496 | t | t | (Phase 3) Processing
14338 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 10:43:10.401768 | t | t | (Phase 1) Processing
14351 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 10:43:20.258824 | t | t | (Phase 2) Processing
14351 | tbl1 | Skipped | 20000000 | 0 | 2021-03-28 10:43:20.259437 | t | t | (Phase 3) Processing
8時から10時の間だけではあるが、各時刻の43分にAutoAnalyzeが発行されており、おそらく1時間に1回のペースでAutoAnalyzeが稼働している模様。
上記ログはCOPYコマンド以降、当該テーブルにクエリを発行していないケースだが、別途COPYコマンド後にテーブルに更新クエリを発行するケースも試行したが1時間に1回のペースは変わらなかった。(ただ、数回程度の試行のためAutoAnalyzeが稼働するケースがある可能性はあり)
#Analyzeを明示的に発行した方がよいケース
端的にまとめると「Analyzeが自動的に発行されないコマンドにより作成したテーブルを、1時間以内に参照し、かつ、統計情報が最新でないとパフォーマンスが出ないような参照クエリがある場合」。
(全然端的じゃない・・・)
基本的にRedshiftへ大量データを投入するときはCopyコマンド、CTASあたりかと思うが、他にあるとすればINSERT INTO SELECTでのデータ投入か。
※上記で記載した「SELECT INTO」とは別のコマンド。
※ISERT INTO SELECTでデータ投入した場合には、自動でAnalyzeが発行されないことも実機確認済み
#MATERIALIZED VIEWに関する挙動について
以下の流れでコマンドを発行し、Analyze関連で内部的に発行されたコマンドをsvl_statementtextテーブルから、テーブルへのAnalyze実績をstl_analyzeテーブルから確認。
①tbl1にCOPYコマンドで3000万件をLoad
②CREATE MATERIALIZED VIEW
③tbl1にCOPYコマンドで2000万件を追加Load(STATUPDATEオプション設定無し)
④REFRESH MATERIALIZED VIEW(増分更新)
⑤tbl1にCOPYコマンドで2000万件を追加Load(STATUPDATEオプションON)
⑥tbl1をtruncata
⑦tbl1にCOPYコマンドで3000万件をLoad
⑧REFRESH MATERIALIZED VIEW(全量更新)
###①tbl1にCOPYコマンドで3000万件をLoad
空テーブルに対しCOPYコマンドを発行するとセットでAnalyzeが発行される。
db1=# select xid, CONVERT_TIMEZONE('JST',starttime) as starttime,datediff(sec,starttime,endtime ) as secs, substring(text, 1, 300) from svl_statementtext where sequence = 0 and xid in (select xid from svl_statementtext s where s.text like 'padb_fetch_sample%' ) order by xid,starttime;
xid | starttime | secs | substring
------+----------------------------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2616 | 2021-04-03 19:47:24.273663 | 64 | copy tbl1 from 's3://xxx';
2616 | 2021-04-03 19:48:28.974304 | 20 | Analyze tbl1
2616 | 2021-04-03 19:48:28.975073 | 14 | padb_fetch_sample: select * from tbl1
2616 | 2021-04-03 19:48:42.26644 | 6 | padb_fetch_sample: select * from tbl1
2616 | 2021-04-03 19:48:48.413595 | 0 | COMMIT
db1=# select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, CONVERT_TIMEZONE('JST',a.starttime) as starttime_j ,is_auto,is_background,substring(auto_analyze_phase,1,20) as phase from stl_analyze a join stv_tbl_perm t on t.id=a.table_id order by starttime;
xid | name | status | rows | modified_rows | starttime_j | is_auto | is_background | phase
------+------+-----------------+----------+---------------+----------------------------+---------+---------------+----------------
2616 | tbl1 | Full | 30000000 | 30000000 | 2021-04-03 19:48:28.974304 | t | f | Not Applicable
###②CREATE MATERIALIZED VIEW
MATERIALIZED VIEWをCreateしたタイミングでAnalyzeコマンドが発行される。
db1=# select xid, CONVERT_TIMEZONE('JST',starttime) as starttime,datediff(sec,starttime,endtime ) as secs, substring(text, 1, 300) from svl_statementtext where sequence = 0 and xid in (select xid from svl_statementtext s where s.text like 'padb_fetch_sample%' ) order by xid,starttime;
xid | starttime | secs | substring
------+----------------------------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2798 | 2021-04-03 19:50:50.25773 | 37 | Create MATERIALIZED VIEW mv_tbl1\nas select * from tbl1;
2798 | 2021-04-03 19:51:27.151279 | 0 | Create MATERIALIZED VIEW mv_tbl1\nas select * from tbl1;
2798 | 2021-04-03 19:51:27.15358 | 0 | Create MATERIALIZED VIEW mv_tbl1\nas select * from tbl1;
2798 | 2021-04-03 19:51:27.165518 | 16 | Analyze mv_tbl__mv_tbl1__0
2798 | 2021-04-03 19:51:27.165717 | 16 | padb_fetch_sample: select * from mv_tbl__mv_tbl1__0
2798 | 2021-04-03 19:51:43.301068 | 0 | padb_fetch_sample: select * from mv_tbl__mv_tbl1__0
2798 | 2021-04-03 19:51:43.847555 | 0 | COMMIT
db1=# select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, CONVERT_TIMEZONE('JST',a.starttime) as starttime_j ,is_auto,is_background,substring(auto_analyze_phase,1,20) as phase from stl_analyze a join stv_tbl_perm t on t.id=a.table_id order by starttime;
xid | name | status | rows | modified_rows | starttime_j | is_auto | is_background | phase
------+--------------------+-----------------+----------+---------------+----------------------------+---------+---------------+----------------
2616 | tbl1 | Full | 30000000 | 30000000 | 2021-04-03 19:48:28.974304 | t | f | Not Applicable
2798 | mv_tbl__mv_tbl1__0 | Full | 30000000 | 0 | 2021-04-03 19:51:27.165518 | t | f | Not Applicable
###③tbl1にCOPYコマンドで2000万件をLoad(STATUPDATEオプション設定無し)
Analyzeの発行無し。
データ有りのテーブルにSTATUPDATEオプション無しでCOPYを実行した場合はAnalyzeは発行されない。
###④REFRESH MATERIALIZED VIEW(増分更新)
Analyzeの発行無し。
増分更新の場合はAnalyzeは発行されない模様。
また、増分更新だったかどうかはリターンメッセージで確認可能。
db1=# REFRESH MATERIALIZED VIEW mv_tbl1;
INFO: Materialized view mv_tbl1 was incrementally updated successfully.
ちなみに、全量更新の場合のメッセージは下記。
db1=# REFRESH MATERIALIZED VIEW mv_tbl1;
INFO: Materialized view mv_tbl1 was recomputed successfully.
###⑤tbl1にCOPYコマンドで2000万件をLoad(STATUPDATEオプションON)
STATUPDATEオプションONでCOPYコマンドを実行すると想定通りAnalyzeが自動で実行。
db1=# select xid, CONVERT_TIMEZONE('JST',starttime) as starttime,datediff(sec,starttime,endtime ) as secs, substring(text, 1, 300) from svl_statementtext where sequence = 0 and xid in (select xid from svl_statementtext s where s.text like 'padb_fetch_sample%' ) order by xid,starttime;
xid | starttime | secs | substring
------+----------------------------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3503 | 2021-04-03 20:02:57.423461 | 46 | copy tbl1 from 's3://xxx';
3503 | 2021-04-03 20:03:43.525802 | 33 | Analyze tbl1
3503 | 2021-04-03 20:03:43.526022 | 28 | padb_fetch_sample: select * from tbl1
3503 | 2021-04-03 20:04:11.532643 | 5 | padb_fetch_sample: select * from tbl1
3503 | 2021-04-03 20:04:16.641362 | 0 | COMMIT
db1=# select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, CONVERT_TIMEZONE('JST',a.starttime) as starttime_j ,is_auto,is_background,substring(auto_analyze_phase,1,20) as phase from stl_analyze a join stv_tbl_perm t on t.id=a.table_id order by starttime;
xid | name | status | rows | modified_rows | starttime_j | is_auto | is_background | phase
------+--------------------+-----------------+----------+---------------+----------------------------+---------+---------------+----------------
2616 | tbl1 | Full | 30000000 | 30000000 | 2021-04-03 19:48:28.974304 | t | f | Not Applicable
2798 | mv_tbl__mv_tbl1__0 | Full | 30000000 | 0 | 2021-04-03 19:51:27.165518 | t | f | Not Applicable
3503 | tbl1 | Full | 70000000 | 40000000 | 2021-04-03 20:03:43.525802 | t | f | Not Applicable
###⑥tbl1をtruncata
(MATERIALIZED VIEWを全量更新するため一度tbl1をtruncate)
###⑦tbl1にCOPYコマンドで3000万件をLoad
再度、tbl1に3000万件を投入。
db1=# select xid, CONVERT_TIMEZONE('JST',starttime) as starttime,datediff(sec,starttime,endtime ) as secs, substring(text, 1, 300) from svl_statementtext where sequence = 0 and xid in (select xid from svl_statementtext s where s.text like 'padb_fetch_sample%' ) order by xid,starttime;
xid | starttime | secs | substring
------+----------------------------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3782 | 2021-04-03 20:08:08.798301 | 60 | copy tbl1 from 's3://xxx';
3782 | 2021-04-03 20:09:08.044916 | 12 | Analyze tbl1
3782 | 2021-04-03 20:09:08.045211 | 10 | padb_fetch_sample: select * from tbl1
3782 | 2021-04-03 20:09:18.211502 | 2 | padb_fetch_sample: select * from tbl1
3782 | 2021-04-03 20:09:20.301491 | 0 | COMMIT
db1=# select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, CONVERT_TIMEZONE('JST',a.starttime) as starttime_j ,is_auto,is_background,substring(auto_analyze_phase,1,20) as phase from stl_analyze a join stv_tbl_perm t on t.id=a.table_id order by starttime;
xid | name | status | rows | modified_rows | starttime_j | is_auto | is_background | phase
------+--------------------+-----------------+----------+---------------+----------------------------+---------+---------------+----------------
2616 | tbl1 | Full | 30000000 | 30000000 | 2021-04-03 19:48:28.974304 | t | f | Not Applicable
2798 | mv_tbl__mv_tbl1__0 | Full | 30000000 | 0 | 2021-04-03 19:51:27.165518 | t | f | Not Applicable
3503 | tbl1 | Full | 70000000 | 40000000 | 2021-04-03 20:03:43.525802 | t | f | Not Applicable
3782 | tbl1 | Full | 30000000 | 30000000 | 2021-04-03 20:09:08.044916 | t | f | Not Applicable
###⑧REFRESH MATERIALIZED VIEW(全量更新)
元テーブルがまるっと入れ替わった状態でREFRESHコマンドを発行すると、CTASで内部テーブルを作成し当該テーブルをMATERIALIZED VIEWに置き換えるような内部クエリが発行され、合わせてAnalyzeも発行。
db1=# select xid, CONVERT_TIMEZONE('JST',starttime) as starttime,datediff(sec,starttime,endtime ) as secs, substring(text, 1, 300) from svl_statementtext where sequence = 0 and xid in (select xid from svl_statementtext s where s.text like 'padb_fetch_sample%' ) order by xid,starttime;
xid | starttime | secs | substring
------+----------------------------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3903 | 2021-04-03 20:10:24.456465 | 50 | REFRESH MATERIALIZED VIEW mv_tbl1;
3903 | 2021-04-03 20:10:24.457537 | 50 | CALL public.mv_sp__mv_tbl1__0_1(0, 3902, 1, '(0)');
3903 | 2021-04-03 20:10:24.46982 | 39 | CREATE TABLE "public"."mv_tbl__mv_tbl1__0_recomputed" BACKUP YES DISTSTYLE KEY DISTKEY(2) COMPOUND SORTKEY(1, 2) AS (SELECT "tbl1".xxx
3903 | 2021-04-03 20:11:03.50325 | 11 | Analyze mv_tbl__mv_tbl1__0_recomputed
3903 | 2021-04-03 20:11:03.503481 | 11 | padb_fetch_sample: select * from mv_tbl__mv_tbl1__0_recomputed
3903 | 2021-04-03 20:11:14.327198 | 0 | padb_fetch_sample: select * from mv_tbl__mv_tbl1__0_recomputed
3903 | 2021-04-03 20:11:14.825666 | 0 | CREATE OR REPLACE VIEW "public"."mv_tbl1" AS (SELECT "derived_table1".xxx
3903 | 2021-04-03 20:11:14.827848 | 0 | DROP TABLE "public"."mv_tbl__mv_tbl1__0"
3903 | 2021-04-03 20:11:14.831234 | 0 | ALTER TABLE "public"."mv_tbl__mv_tbl1__0_recomputed" RENAME TO "mv_tbl__mv_tbl1__0"
3903 | 2021-04-03 20:11:14.832263 | 0 | CREATE OR REPLACE VIEW "public"."mv_tbl1" AS (SELECT "derived_table1".xxx
3903 | 2021-04-03 20:11:14.835738 | 0 | COMMIT
db1=# select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, CONVERT_TIMEZONE('JST',a.starttime) as starttime_j ,is_auto,is_background,substring(auto_analyze_phase,1,20) as phase from stl_analyze a join stv_tbl_perm t on t.id=a.table_id order by starttime;
xid | name | status | rows | modified_rows | starttime_j | is_auto | is_background | phase
------+--------------------+-----------------+----------+---------------+----------------------------+---------+---------------+----------------
2616 | tbl1 | Full | 30000000 | 30000000 | 2021-04-03 19:48:28.974304 | t | f | Not Applicable
3503 | tbl1 | Full | 70000000 | 40000000 | 2021-04-03 20:03:43.525802 | t | f | Not Applicable
3782 | tbl1 | Full | 30000000 | 30000000 | 2021-04-03 20:09:08.044916 | t | f | Not Applicable
3903 | mv_tbl__mv_tbl1__0 | Full | 30000000 | 0 | 2021-04-03 20:11:03.50325 | t | f | Not Applicable
#終わりに
記載が誤っている点や、わかりづらい点がありましたらコメントいただけますと幸いです。