新機能 Amazon Redshift Late-Binding ビューを試してみました

Amazon Redshift のテーブルやビューと Redshift Spectrum の 外部テーブルを含んだビューを作成できる Late-Binding ビューがサポートされましたので、その仕組と利用方法について解説します。

Late-Binding ビュー とは

Late-Binding ビューでは、実行時にテーブルや他のビューとの関連付けが行われ、ユーザーおよびアプリケーションにクエリーデータへのシームレスなアクセスが提供されます。Late-bindingビューを使用すると、ビューに影響を与えずに参照先テーブルを削除したり変更したりすることができます。この機能を使用すると、Amazon Redshiftクラスタの頻繁にアクセスされるデータと、頻繁にアクセスされないAmazon S3のデータを1つのビューで照会できます。頻繁にアクセスされないデータをAmazon S3にアーカイブし、関連するファイルを参照する外部テーブルを作成し、Amazon RedshiftとRedshift Spectrum外部テーブルの両方を参照するビューを作成することも可能です。

Late-Binding ビューの作成とクエリー、その挙動

Late-Binding ビューの作成とクエリー

これまでのビュー定義の最後にwith no schema binding指定することで、Late-Binding ビューとして作成できます。

cmdb=> -- testtab テーブルの作成、データロード、クエリー
cmdb=> create table cm_user.testtab(id int);
CREATE TABLE
cmdb=> insert into cm_user.testtab(id) values(1),(2),(3);
INSERT 0 3
cmdb=> select * from cm_user.testtab;
 id
----
  3
  2
  1
(3 rows)

cmdb=> -- testtab テーブルを参照する testview_lb_1ビューの作成、クエリー
cmdb=> create view cm_user.testview_lb_1 
cmdb=> as select * from cm_user.testtab with no schema binding;
CREATE VIEW
cmdb=> select * from cm_user.testview_lb_1;
 id
----
  3
  2
  1
(3 rows)

cmdb=> -- testview_1ビューを参照する testview_lb_2ビューの作成、クエリー
cmdb=> create view cm_user.testview_lb_2 
cmdb=> as select * from cm_user.testview_lb_1 with no schema binding;
CREATE VIEW
cmdb=> select * from cm_user.testview_lb_2;
 id
----
  3
  2
  1
(3 rows)

参照先テーブルやビュー存在しない Late-Binding ビューの作成とクエリー

実行時にテーブルや他のビューとの関連付けが行われますので、参照先テーブルやビュー存在しなくても作成可能です。参照先テーブルやビュー存在しないビューを参照すると実行時エラーになります。

cmdb=> -- 存在しないテーブルを参照する testview_lb_3ビューの作成、クエリー
cmdb=> create view cm_user.testview_lb_3 
cmdb=> as select * from cm_user.nonetab with no schema binding;
CREATE VIEW
cmdb=> select * from cm_user.testview_lb_3;
ERROR:  relation "cm_user.nonetab" does not exist

cmdb=> -- 存在しないビューを参照する testview_lb_3ビューの作成、クエリー
cmdb=> create view cm_user.testview_lb_4 
cmdb=> as select * from cm_user.noneview with no schema binding;
CREATE VIEW
cmdb=> select * from cm_user.testview_lb_4;
ERROR:  relation "cm_user.noneview" does not exist

参照先テーブルやビューの削除

逆に、参照先テーブルやビューを削除してもエラーになりません。

cmdb=> -- 参照されているテーブル・ビューの削除
cmdb=> drop table cm_user.testtab;
DROP TABLE
cmdb=> drop view cm_user.testview_lb_1;
DROP VIEW

参照先テーブルのスワップ

つまり、 Late-Binding ビューは再作成せずに参照先のテーブルを置き換えできます。

cmdb=> select * from cm_user.testview_1;
 id
----
  1
  3
  2
(3 rows)

cmdb=> alter table testtab rename to testtab_old;
ALTER TABLE
cmdb=> alter table testtab_new rename to testtab;
ALTER TABLE
cmdb=> select * from cm_user.testview_1;
 id
----
 33
 22
 11
(3 rows)

実行プランの確認

実行プランに影響があるか気になりましたが、何れもこれまでのビューと実行プランは変わりません。実行時バインディングなので実行プランやコンパイルまでの初期コストの増加は予想されますが、ビックデータを処理するDWH用途では性能面で余り問題にならないのではないかと予想します。

cmdb=> explain select * from cm_user.testtab;
                                  QUERY PLAN
------------------------------------------------------------------------------
 XN Seq Scan on testtab  (cost=0.00..0.03 rows=3 width=4)
 ----- Tables missing statistics: testtab -----
 ----- Update statistics by running the ANALYZE command on these tables -----
(3 rows)

cmdb=> explain select * from cm_user.testview_lb_1;
                                  QUERY PLAN
------------------------------------------------------------------------------
 XN Seq Scan on testtab  (cost=0.00..0.03 rows=3 width=4)
 ----- Tables missing statistics: testtab -----
 ----- Update statistics by running the ANALYZE command on these tables -----
(3 rows)

cmdb=> explain select * from cm_user.testview_lb_2;
                                  QUERY PLAN
------------------------------------------------------------------------------
 XN Seq Scan on testtab  (cost=0.00..0.03 rows=3 width=4)
 ----- Tables missing statistics: testtab -----
 ----- Update statistics by running the ANALYZE command on these tables -----
(3 rows)

参照先テーブルやビューはスキーマ名の指定が必須

Late-Binding ビューは、スキーマのオブジェクトに対して動的に関連付けするので、スキーマ名の指定が必要になります。スキーマ名を指定しないと以下のエラー表示されますのでご注意ください。

cmdb=> create view cm_user.testview_lb_1 
cmdb=> as select * from testtab with no schema binding;
ERROR:  All the relation names inside should be qualified when creating VIEW WITH NO SCHEMA BINDING.

これまでのビューにおいてもスキーマ名を指定しないとトラブルのもとなので、常にスキーマ名を指定することをおすすめします。

ユースケース

ロックフリーなテーブルメンテナンス

参照先テーブルのスワップの応用例1です。別名のテーブルにてディープコピー、UPSERTなど更新系クエリ等の時間のかかるデータ操作した結果、テーブル名をスワップすることでテーブルロックをほとんどかけずにテーブルデータを更新可能になります。

参照先テーブルの定義変更に伴うビュー再作成の回避

参照先テーブルのスワップの応用例2です。参照してるテーブルのソートキーや分散キーを変更するには、テーブルの再作成が必要になりますが、参照先のテーブルを再作成するには関連するビューも再作成が必要でした。アクセス制御を兼ねたビューでは、テーブルの所有者がビューの所有者であるとは限らず、ビューを再作成でない場合があります。別名のテーブルを作成してディープコピー、ANALYZE、テーブル名をスワップすることでこの問題を解決できます。

Redshift Spectrum の外部テーブルを含んだビューを作成

ついに、Redshift Spectrum の 外部テーブルを含んだビューを作成できるようになりました。外部テーブルを直接クエリーするのではなく、頻繁にアクセスする形式やカラム名に柔軟に変更できるようになります。また、外部テーブルのデータファイルの形式は一つとは限らないので、異なるファイルフォーマットをUNION ALLなど、1つのビューにまとめて表現することも可能です。

頻繁にアクセスされる/されないデータを1つのビューで照会

Amazon Redshiftクラスタの頻繁にアクセスされるデータと、頻繁にアクセスされないAmazon S3のデータを1つのビューで照会できます。頻繁にアクセスされないデータをAmazon S3にアーカイブし、関連するファイルを参照する外部テーブルを作成し、Amazon RedshiftとRedshift Spectrum外部テーブルの両方を参照するビューを作成します。

create view sales_vw as
select * from public.sales
union all
select * from spectrum.sales
with no schema binding;

2つのビューの内部的な違い

理解を深めるために少し内部的な話を補足します。これまでのビューは参照しているテーブルやビューのOIDと、作成したビューのOIDを静的に関連付けて管理しています。テーブルやビューの静的な依存関係を pg_depend から参照すると、testtabはtestview_1から参照され、testview_1はtestview_2から参照されていることが確認できます。

cmdb=> -- testtab テーブルの作成
cmdb=> create table testtab(id int);
CREATE TABLE

cmdb=> -- testtab テーブルを参照する testview_1ビューの作成
cmdb=> create view testview_1 as select * from testtab;
CREATE VIEW
cmdb=> -- testview_1ビューを参照する testview_2ビューの作成
cmdb=> create view testview_2 as select * from testview_1;
CREATE VIEW

cmdb=> -- テーブル・ビュー間の依存関係を確認
cmdb=> select distinct
cmdb->   c_p.oid as tbloid,
cmdb->   n_p.nspname as schemaname,
cmdb->   c_p.relname as name,
cmdb->   n_c.nspname as refbyschemaname,
cmdb->   c_c.relname as refbyname,
cmdb->   c_c.oid as viewoid
cmdb-> from
cmdb->   pg_catalog.pg_class c_p
cmdb->     join pg_catalog.pg_depend d_p on c_p.relfilenode = d_p.refobjid
cmdb->     join pg_catalog.pg_depend d_c on d_p.objid = d_c.objid
cmdb->     join pg_catalog.pg_class c_c on d_c.refobjid = c_c.relfilenode
cmdb->     left outer join pg_namespace n_p on c_p.relnamespace = n_p.oid
cmdb->     left outer join pg_namespace n_c on c_c.relnamespace = n_c.oid
cmdb-> where
cmdb->   d_c.deptype = 'i'::"char"
cmdb->   and c_c.relkind = 'v'::"char"
cmdb->   and schemaname = 'cm_user'
cmdb-> ;
 tbloid | schemaname |    name    |  refbyschemaname   | refbyname  | viewoid
--------+------------+------------+--------------------+------------+---------
 812329 | cm_user    | testtab    | cm_user | testview_1 |  812331
 812331 | cm_user    | testview_1 | cm_user | testview_1 |  812331
 812331 | cm_user    | testview_1 | cm_user | testview_2 |  812334
 812334 | cm_user    | testview_2 | cm_user | testview_2 |  812334
(4 rows)

一方、Late-Binding ビューは実行時に動的に関連付けられます。先ほどと同様に、テーブルを作成〜テーブルを参照するビューを作成〜ビューを参照するビューを作成します。テーブルやビューの静的な依存関係がないことが確認できます。

<br />cmdb=> -- testtab テーブルの作成
cmdb=> create table testtab(id int);
CREATE TABLE
cmdb=> -- testtab テーブルを参照する testview_lb_1ビューの作成
cmdb=> create view testview_lb_1 
cmdb=> as select * from cm_user.testtab with no schema binding;
CREATE VIEW
cmdb=> -- testview_1ビューを参照する testview_lb_2ビューの作成
cmdb=> create view testview_lb_2 
cmdb=> as select * from cm_user.testview1 with no schema binding;
CREATE VIEW
cmdb=> -- テーブル・ビュー間の依存関係を確認
cmdb=> select distinct
cmdb->   c_p.oid as tbloid,
cmdb->   n_p.nspname as schemaname,
cmdb->   c_p.relname as name,
cmdb->   n_c.nspname as refbyschemaname,
cmdb->   c_c.relname as refbyname,
cmdb->   c_c.oid as viewoid
cmdb-> from
cmdb->   pg_catalog.pg_class c_p
cmdb->     join pg_catalog.pg_depend d_p on c_p.relfilenode = d_p.refobjid
cmdb->     join pg_catalog.pg_depend d_c on d_p.objid = d_c.objid
cmdb->     join pg_catalog.pg_class c_c on d_c.refobjid = c_c.relfilenode
cmdb->     left outer join pg_namespace n_p on c_p.relnamespace = n_p.oid
cmdb->     left outer join pg_namespace n_c on c_c.relnamespace = n_c.oid
cmdb-> where
cmdb->   d_c.deptype = 'i'::"char"
cmdb->   and c_c.relkind = 'v'::"char"
cmdb->   and schemaname = 'cm_user'
cmdb-> ;
 tbloid | schemaname |     name      |  refbyschemaname   |   refbyname   | viewoid
--------+------------+---------------+--------------------+---------------+---------
 812339 | cm_user    | testview_lb_1 | cm_user | testview_lb_1 |  812339
 812342 | cm_user    | testview_lb_2 | cm_user | testview_lb_2 |  812342
(2 rows)

OIDを持たないRedshift Spectrumの外部テーブルが、Late-Binding ビューによってビュー作成可能になっています。

最後に

これまでのビューは、pg_dependテーブル によってOIDを静的に関連付けを管理していました。このメリットは参照先がないビュー存在しないことを保証できることにあります。一方、今回ご紹介した Late-Binding ビューでは、指定したスキーマ配下のオブジェクト名に対して関連付けされています。この仕組みによってRedshift Spectrumの外部テーブルのようにOIDを持たない外部テーブルに対してビューを作成したり、ビューが参照しているテーブル名をスワップしたり柔軟な運用が可能になります。

執筆時点では、公式ドキュメントは日本語化されていませんので、英語版をご覧ください。

公式ドキュメント(英語) - CREATE_VIEW