Amazon Redshift: テーブル列圧縮定義の診断・テーブル間データ移行ツール『Amazon Redshift Column Encoding Utility』

2015.06.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

先日投稿した下記2本のエントリでは、Amazon Redshiftの管理・作業を行なっていく上で便利なクエリを『amazon-redshift-utils』というGitHubプロジェクトのコンテンツを紹介しました。

当エントリでは、その『amazon-redshift-utils』で展開されている『ColumnEncodingUtility』というツールについてご紹介したいと思います。どちらかと言えばこのツールこそがご紹介したい内容ではあったのですが、前述のスクリプトも非常に有用なものであったので(プロジェクト内の)ひと通りのコンテンツを展開した形となりました。:-)

目次

Amazon Redshift Column Encoding Utilityとは

このツールは『テーブルの適切な列圧縮エンコーディングを分析し、必要に応じてテーブルの再設計・データ移行を行う』ものとなります。

Amazon Redshiftでは、テーブル定義の際に『列圧縮タイプ』を選択する事が出来ます。データの格納時にそのサイズを小さくする列レベルの操作で、この圧縮によってストレージスペースが節約され、ストレージから読み込まれるデータのサイズが小さくなり、ディスク I/O の量が減少するので、クエリパフォーマンスが向上します。

この設定については、最初から適切なエンコーディングを一から全ての項目に割り当てるというのはなかなかに難しいものです。慣れてくればテーブル定義設計の段階で『これが良かろう』という内容を決める事も出来ますが、カラム数が多いテーブルであればその作業自体も面倒な作業だったりして来ます。(カラム数10個のテーブルとカラム数500個のテーブルに際してこれらの作業を行う事を想定してみてください。)

ちなみに、推奨されるテーブル定義に於ける列圧縮タイプを割り出すには、ANALYZE COMPRESSIONというコマンドを使うことで実現が可能です。

ColumnEncodingUtilityはこの辺りの作業をコマンド一発で実行してくれるものとなります。

環境導入

環境構築については、GitHubプロジェクトのドキュメントではAWS EC2を元に行われていましたのでここでもそれを踏襲しようと思います。適当なAmazon Linux AMIを用意しておいてください。

$ ssh -i xxxxxxxxxxx.pem ec2-user@xxx.xx.xx.xxx
$ sudo yum -y update

プログラム実行にはPythonが必要となります。Python2.7でテストをしているよう(他のバージョンでも動くと思うよ、とはドキュメントにも記載あり)なのでEC2導入時の環境をそのまま利用したいと思います。PostgreSQL周り、及びPyGreSQLをインストールしておきます。

$ python --version
Python 2.7.9
$ sudo easy_install pip
$ sudo yum -y install postgresql postgresql-devel gcc python-devel
$ sudo pip install PyGreSQL
You are using pip version 6.1.1, however version 7.0.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting PyGreSQL
  Downloading PyGreSQL-4.1.1.tgz (100kB)
    100% |████████████████████████████████| 102kB 1.9MB/s 
Installing collected packages: PyGreSQL
  Running setup.py install for PyGreSQL
Successfully installed PyGreSQL-4.1.1
$

コンテンツを取得するため、gitもインストール。そして指定のプロジェクトからgit cloneコマンドを使ってプロジェクト一式の内容を取得します。

$ sudo yum -y install git
$ git --version
git version 2.1.0

$ git clone https://github.com/awslabs/amazon-redshift-utils.git
Cloning into 'amazon-redshift-utils'...
remote: Counting objects: 264, done.
remote: Total 264 (delta 0), reused 0 (delta 0), pack-reused 264
Receiving objects: 100% (264/264), 64.29 KiB | 0 bytes/s, done.
Resolving deltas: 100% (110/110), done.
Checking connectivity... done.
$

プログラム実行。以下のように利用ヘルプが出ていれば導入完了です。

$ pwd
/home/ec2-user/amazon-redshift-utils/src/ColumnEncodingUtility
$ ls
README.md  analyze-schema-compression.py
$ 
$ python analyze-schema-compression.py 
Usage: analyze-schema-compression.py
       Generates a script to optimise Redshift column encodings on all tables in a schema

Missing Parameter 'db'

Arguments: --db             - The Database to Use
           --db-user        - The Database User to connect to
           --db-host        - The Cluster endpoint
           --db-port        - The Cluster endpoint port (default 5439)
           --analyze-schema - The Schema to be Analyzed (default public)
           --analyze-table  - A specific table to be Analyzed, if --analyze-schema is not desired
           --target-schema  - Name of a Schema into which the newly optimised tables and data should be created, rather than in place
           --threads        - The number of concurrent connections to use during analysis (default 2)
           --output-file    - The full path to the output file to be generated
           --debug          - Generate Debug Output including SQL Statements being run
           --do-execute     - Run the compression encoding optimisation
           --slot-count     - Modify the wlm_query_slot_count from the default of 1
           --ignore-errors  - Ignore errors raised in threads when running and continue processing
           --force          - Force table migration even if the table already has Column Encoding applied
           --drop-old-data  - Drop the old version of the data table, rather than renaming
           --comprows       - Set the number of rows to use for Compression Encoding Analysis
           --query_group    - Set the query_group for all queries
$

テーブル分析&データ移行実践

では実際にこのツールを使ってみましょう。以下テーブルを用意しました。Tableauでデモ用データとしても良く使われている『Superstore Sample』のデータとなります。このデータをCSVとして取り込む時に使っているテーブル定義を活用したいと思います。ご覧の様に、『列圧縮タイプ』についてはいずれの項目も未設定です。

# CREATE TABLE public.orders_rsutil (
  order_id INT NOT NULL,
  order_date DATE NOT NULL,
  priority VARCHAR(12) NOT NULL,
  quantity SMALLINT NOT NULL,
  sales DOUBLE PRECISION,
  discount_rate DOUBLE PRECISION,
  ship_mode VARCHAR(20) NOT NULL,
  profit INT NOT NULL,
  unit_price INT NOT NULL,
  ad_expenses INT NOT NULL,
  shipping_cost INT NOT NULL,
  customer_name VARCHAR(50) NOT NULL,
  prefecture VARCHAR(12) NOT NULL,
  city VARCHAR(20) NOT NULL,
  area VARCHAR(12) NOT NULL,
  shop_name VARCHAR(20) NOT NULL,
  customer_segment VARCHAR(30) NOT NULL,
  product_category VARCHAR(30) NOT NULL,
  product_sub_category VARCHAR(100) NOT NULL,
  product_id VARCHAR(10) NOT NULL,
  product_name VARCHAR(100) NOT NULL,
  product_description VARCHAR(200) NOT NULL,
  product_container VARCHAR(100) NOT NULL,
  base_margin DOUBLE PRECISION,
  supplier VARCHAR(30) NOT NULL,
  deliver_date DATE NOT NULL,
  ship_date DATE NOT NULL,
  PRIMARY KEY (order_id)
)
  distkey(order_id)
  sortkey(order_id,order_date)
;

テーブル定義を確認してみます。『encoding』は全て未設定(none)となっています。

# SELECT * FROM pg_table_def WHERE schemaname = 'public' AND tablename = 'orders_rsutil';
 schemaname |   tablename   |        column        |          type          | encoding | distkey | sortkey | notnull 
------------+---------------+----------------------+------------------------+----------+---------+---------+---------
 public     | orders_rsutil | order_id             | integer                | none     | t       |       1 | t
 public     | orders_rsutil | order_date           | date                   | none     | f       |       2 | t
 public     | orders_rsutil | priority             | character varying(12)  | none     | f       |       0 | t
 public     | orders_rsutil | quantity             | smallint               | none     | f       |       0 | t
 public     | orders_rsutil | sales                | double precision       | none     | f       |       0 | f
 public     | orders_rsutil | discount_rate        | double precision       | none     | f       |       0 | f
 public     | orders_rsutil | ship_mode            | character varying(20)  | none     | f       |       0 | t
 public     | orders_rsutil | profit               | integer                | none     | f       |       0 | t
 public     | orders_rsutil | unit_price           | integer                | none     | f       |       0 | t
 public     | orders_rsutil | ad_expenses          | integer                | none     | f       |       0 | t
 public     | orders_rsutil | shipping_cost        | integer                | none     | f       |       0 | t
 public     | orders_rsutil | customer_name        | character varying(50)  | none     | f       |       0 | t
 public     | orders_rsutil | prefecture           | character varying(12)  | none     | f       |       0 | t
 public     | orders_rsutil | city                 | character varying(20)  | none     | f       |       0 | t
 public     | orders_rsutil | area                 | character varying(12)  | none     | f       |       0 | t
 public     | orders_rsutil | shop_name            | character varying(20)  | none     | f       |       0 | t
 public     | orders_rsutil | customer_segment     | character varying(30)  | none     | f       |       0 | t
 public     | orders_rsutil | product_category     | character varying(30)  | none     | f       |       0 | t
 public     | orders_rsutil | product_sub_category | character varying(100) | none     | f       |       0 | t
 public     | orders_rsutil | product_id           | character varying(10)  | none     | f       |       0 | t
 public     | orders_rsutil | product_name         | character varying(100) | none     | f       |       0 | t
 public     | orders_rsutil | product_description  | character varying(200) | none     | f       |       0 | t
 public     | orders_rsutil | product_container    | character varying(100) | none     | f       |       0 | t
 public     | orders_rsutil | base_margin          | double precision       | none     | f       |       0 | f
 public     | orders_rsutil | supplier             | character varying(30)  | none     | f       |       0 | t
 public     | orders_rsutil | deliver_date         | date                   | none     | f       |       0 | t
 public     | orders_rsutil | ship_date            | date                   | none     | f       |       0 | t
(27 rows)

ツールを実行する上で推奨されている対象行数はデフォルト指定で10万行以上となっているため、既存のデータを複製する形で適当な件数(今回は200万件としてみました)に増やしておきます。(※本来適した形では無いのですが、"任意の項目を分析し、指定のエンコーディングでテーブルが再作成される"という部分を確認したいのでこれで多目にみてください。)

# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders);
INSERT 0 8369
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 8369
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 16738
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 33476
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 66952
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 133904
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 267808
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 535616
# INSERT INTO public.orders_rsutil (SELECT * FROM public.orders_rsutil);
INSERT 0 1071232
# SELECT COUNT(*) FROM public.orders_rsutil;
  count  
---------
 2142464
(1 row)

--debug trueオプションを使って、ひとまず動くかどうかを確認してみます。一連の作業がログとして出力されていますね。

$ python analyze-schema-compression.py \
--db <DB名> \
--db-user <DB接続ユーザー名> \
--db-host <DBホスト名> \
--db-port 5439 \
--analyze-schema public \
--analyze-table orders_rsutil \
--output-file /home/ec2-user/amazon-redshift-utils/src/ColumnEncodingUtility/migrate.log \
--debug true
Password <DB接続ユーザー名>: 
-- [28540] Connect [28540] <DBホスト名>:5439:<DB名>:<DB接続ユーザー名>
-- [28540] set search_path = '$user',public,public
-- [28540] set statement_timeout = '1200000'
-- [28540] Connected to <DBホスト名>:5439:<DB名> as <DB接続ユーザー名>
-- [28540] Analyzing Table 'orders_rsutil' for Columnar Encoding Optimisations with 1 Threads...
/* [28540]
select trim(a.name) as table, b.mbytes, a.rows, decode(pgc.reldiststyle,0,'EVEN',1,'KEY',8,'ALL') dist_style
from (select db_id, id, name, sum(rows) as rows from stv_tbl_perm a group by db_id, id, name) as a
join pg_class as pgc on pgc.oid = a.id
join pg_namespace as pgn on pgn.oid = pgc.relnamespace
join (select tbl, count(*) as mbytes
from stv_blocklist group by tbl) b on a.id=b.tbl
and pgn.nspname = 'public' and pgc.relname = 'orders_rsutil'        
        
*/

-- [28540] Query Execution returned 1 Results
-- [28540] Analyzing 1 table(s)
-- [28540] [('orders_rsutil', 965L, 2142464L, 'KEY')]
-- [28541] analyze compression public.orders_rsutil
-- [28541] Analyzing Table 'orders_rsutil'
-- [28541] Connect [28541] <DBホスト名>:5439:<DB名>:<DB接続ユーザー名>
-- [28541] set search_path = '$user',public,public
-- [28541] set statement_timeout = '1200000'
-- [28541] Query Execution returned 27 Results
-- [28541] creating migration table for orders_rsutil
/* [28541]
select "column", type, encoding, distkey, sortkey, "notnull", ad.adsrc
 from pg_table_def de, pg_attribute at LEFT JOIN pg_attrdef ad ON (at.attrelid, at.attnum) = (ad.adrelid, ad.adnum)
 where de.schemaname = 'public'
 and de.tablename = 'orders_rsutil'
 and at.attrelid = 'public.orders_rsutil'::regclass
 and de.column = at.attname

*/

-- [28541] Query Execution returned 27 Results
/* [28541]
SELECT       
  att.attname
FROM pg_index ind, pg_class cl, pg_attribute att, pg_namespace pgn
WHERE 
  cl.oid = 'orders_rsutil'::regclass 
  AND ind.indrelid = cl.oid 
  AND att.attrelid = cl.oid
  and cl.relnamespace = pgn.oid
  and pgn.nspname = 'public'
  and (ind.indkey[0] = att.attnum or 
       ind.indkey[1] = att.attnum or
       ind.indkey[2] = att.attnum or
       ind.indkey[3] = att.attnum or
       ind.indkey[4] = att.attnum
      )
  and attnum > 0
  AND ind.indisprimary
order by att.attnum;

*/

-- [28541] Query Execution returned 1 Results
-- [28541] migrating data to new structure for table orders_rsutil
/* [28541]
SELECT conname,
  pg_catalog.pg_get_constraintdef(cons.oid, true) as condef
 FROM pg_catalog.pg_constraint cons,
 pg_namespace pgn,
 pg_class pgc
 WHERE cons.conrelid = pgc.oid
 and pgn.nspname = 'public'
 and pgc.relnamespace = pgn.oid
 and pgc.oid = 'orders_rsutil'::regclass
 AND cons.contype = 'f'
 ORDER BY 1

*/

-- [28541] Query Execution returned 0 Results
begin;
lock table public.orders_rsutil;
create table public.orders_rsutil_$mig(
order_id integer  NOT NULL encode RAW DISTKEY
,order_date date  NOT NULL encode lzo 
,priority varchar(12)  NOT NULL encode lzo 
,quantity smallint  NOT NULL encode lzo 
,sales double precision   encode runlength 
,discount_rate double precision   encode bytedict 
,ship_mode varchar(20)  NOT NULL encode lzo 
,profit integer  NOT NULL encode lzo 
,unit_price integer  NOT NULL encode lzo 
,ad_expenses integer  NOT NULL encode lzo 
,shipping_cost integer  NOT NULL encode lzo 
,customer_name varchar(50)  NOT NULL encode lzo 
,prefecture varchar(12)  NOT NULL encode lzo 
,city varchar(20)  NOT NULL encode lzo 
,area varchar(12)  NOT NULL encode lzo 
,shop_name varchar(20)  NOT NULL encode lzo 
,customer_segment varchar(30)  NOT NULL encode lzo 
,product_category varchar(30)  NOT NULL encode lzo 
,product_sub_category varchar(100)  NOT NULL encode lzo 
,product_id varchar(10)  NOT NULL encode lzo 
,product_name varchar(100)  NOT NULL encode lzo 
,product_description varchar(200)  NOT NULL encode lzo 
,product_container varchar(100)  NOT NULL encode lzo 
,base_margin double precision   encode bytedict 
,supplier varchar(30)  NOT NULL encode lzo 
,deliver_date date  NOT NULL encode lzo 
,ship_date date  NOT NULL encode lzo 
)
 SORTKEY(order_id,order_date)
 ;
alter table public.orders_rsutil_$mig add primary key (order_id);
insert into public.orders_rsutil_$mig  select * from public.orders_rsutil;
analyze public.orders_rsutil_$mig;
alter table public.orders_rsutil rename to orders_rsutil_$old;
alter table public.orders_rsutil_$mig rename to orders_rsutil;
commit;
-- [28540] Processing Complete
Connection already closed
$

--do-execute trueオプションで実際に実行が為されます。

$ python analyze-schema-compression.py \
--db <DB名> \
--db-user <DB接続ユーザー名> \
--db-host <DBホスト名> \
--db-port 5439 \
--analyze-schema public \
--analyze-table orders_rsutil \
--output-file /home/ec2-user/amazon-redshift-utils/src/ColumnEncodingUtility/migrate.log \
--do-execute true
Password <DB接続ユーザー名>: 
-- [28545] Connected to <DBホスト名>:5439:<DB名> as <DB接続ユーザー名>
-- [28545] Analyzing Table 'orders_rsutil' for Columnar Encoding Optimisations with 1 Threads...
-- [28545] Recommended encoding changes will be applied automatically...
-- [28545] Analyzing 1 table(s)
-- [28546] Analyzing Table 'orders_rsutil'
-- [28546] creating migration table for orders_rsutil
-- [28546] migrating data to new structure for table orders_rsutil
/* [28546]
[28546] Running begin;
lock table public.orders_rsutil;
create table public.orders_rsutil_$mig(
order_id integer  NOT NULL encode RAW DISTKEY
,order_date date  NOT NULL encode lzo 
,priority varchar(12)  NOT NULL encode lzo 
,quantity smallint  NOT NULL encode lzo 
,sales double precision   encode runlength 
,discount_rate double precision   encode bytedict 
,ship_mode varchar(20)  NOT NULL encode lzo 
,profit integer  NOT NULL encode lzo 
,unit_price integer  NOT NULL encode lzo 
,ad_expenses integer  NOT NULL encode lzo 
,shipping_cost integer  NOT NULL encode lzo 
,customer_name varchar(50)  NOT NULL encode lzo 
,prefecture varchar(12)  NOT NULL encode lzo 
,city varchar(20)  NOT NULL encode lzo 
,area varchar(12)  NOT NULL encode lzo 
,shop_name varchar(20)  NOT NULL encode lzo 
,customer_segment varchar(30)  NOT NULL encode lzo 
,product_category varchar(30)  NOT NULL encode lzo 
,product_sub_category varchar(100)  NOT NULL encode lzo 
,product_id varchar(10)  NOT NULL encode lzo 
,product_name varchar(100)  NOT NULL encode lzo 
,product_description varchar(200)  NOT NULL encode lzo 
,product_container varchar(100)  NOT NULL encode lzo 
,base_margin double precision   encode bytedict 
,supplier varchar(30)  NOT NULL encode lzo 
,deliver_date date  NOT NULL encode lzo 
,ship_date date  NOT NULL encode lzo 
)
 SORTKEY(order_id,order_date)
 ;
*/

-- [28546] Success.
-- [28546] [28546] Running alter table public.orders_rsutil_$mig add primary key (order_id);
-- [28546] Success.
-- [28546] [28546] Running insert into public.orders_rsutil_$mig  select * from public.orders_rsutil;
-- [28546] Success.
-- [28546] [28546] Running analyze public.orders_rsutil_$mig;
-- [28546] Success.
-- [28546] [28546] Running alter table public.orders_rsutil rename to orders_rsutil_$old;
-- [28546] Success.
-- [28546] [28546] Running alter table public.orders_rsutil_$mig rename to orders_rsutil;
-- [28546] Success.
-- [28546] [28546] Running commit;
-- [28546] Success.
begin;
lock table public.orders_rsutil;
create table public.orders_rsutil_$mig(
order_id integer  NOT NULL encode RAW DISTKEY
,order_date date  NOT NULL encode lzo 
,priority varchar(12)  NOT NULL encode lzo 
,quantity smallint  NOT NULL encode lzo 
,sales double precision   encode runlength 
,discount_rate double precision   encode bytedict 
,ship_mode varchar(20)  NOT NULL encode lzo 
,profit integer  NOT NULL encode lzo 
,unit_price integer  NOT NULL encode lzo 
,ad_expenses integer  NOT NULL encode lzo 
,shipping_cost integer  NOT NULL encode lzo 
,customer_name varchar(50)  NOT NULL encode lzo 
,prefecture varchar(12)  NOT NULL encode lzo 
,city varchar(20)  NOT NULL encode lzo 
,area varchar(12)  NOT NULL encode lzo 
,shop_name varchar(20)  NOT NULL encode lzo 
,customer_segment varchar(30)  NOT NULL encode lzo 
,product_category varchar(30)  NOT NULL encode lzo 
,product_sub_category varchar(100)  NOT NULL encode lzo 
,product_id varchar(10)  NOT NULL encode lzo 
,product_name varchar(100)  NOT NULL encode lzo 
,product_description varchar(200)  NOT NULL encode lzo 
,product_container varchar(100)  NOT NULL encode lzo 
,base_margin double precision   encode bytedict 
,supplier varchar(30)  NOT NULL encode lzo 
,deliver_date date  NOT NULL encode lzo 
,ship_date date  NOT NULL encode lzo 
)
 SORTKEY(order_id,order_date)
 ;
alter table public.orders_rsutil_$mig add primary key (order_id);
insert into public.orders_rsutil_$mig  select * from public.orders_rsutil;
analyze public.orders_rsutil_$mig;
alter table public.orders_rsutil rename to orders_rsutil_$old;
alter table public.orders_rsutil_$mig rename to orders_rsutil;
commit;
$

上記実行ログの末尾で記載されている様に、スキーマを同じ場所で指定(--target-schemaオプション)すると、同一スキーマ内で以下の様な順番でテーブル再作成・データ移行が行われるようです。

  • 分析され・再作成される事になるテーブルは_$migという名前で生成される
  • 既存テーブルから移行先テーブル($mig)へのデータ移行(INSERT〜SELECT...)
  • 既存のテーブルは_$oldという名前にリネーム
  • 最適化された_$migテーブルはmy_tableにリネーム

移行先のスキーマを--target-schema オプションで指定すると、データ移行はターゲットのスキーマになされ、全てのテーブルは元の名前を保持したままとなります。分析されている送信元のスキーマは変更される事はありません。

処理実行後、対象テーブルの内容を確認してみます。『encoding』が指定されたテーブルに内容が置き換わっていますね!

# SELECT * FROM pg_table_def WHERE schemaname = 'public' AND tablename = 'orders_rsutil';
 schemaname |   tablename   |        column        |          type          | encoding  | distkey | sortkey | notnull 
------------+---------------+----------------------+------------------------+-----------+---------+---------+---------
 public     | orders_rsutil | order_id             | integer                | none      | t       |       1 | t
 public     | orders_rsutil | order_date           | date                   | lzo       | f       |       2 | t
 public     | orders_rsutil | priority             | character varying(12)  | lzo       | f       |       0 | t
 public     | orders_rsutil | quantity             | smallint               | lzo       | f       |       0 | t
 public     | orders_rsutil | sales                | double precision       | runlength | f       |       0 | f
 public     | orders_rsutil | discount_rate        | double precision       | bytedict  | f       |       0 | f
 public     | orders_rsutil | ship_mode            | character varying(20)  | lzo       | f       |       0 | t
 public     | orders_rsutil | profit               | integer                | lzo       | f       |       0 | t
 public     | orders_rsutil | unit_price           | integer                | lzo       | f       |       0 | t
 public     | orders_rsutil | ad_expenses          | integer                | lzo       | f       |       0 | t
 public     | orders_rsutil | shipping_cost        | integer                | lzo       | f       |       0 | t
 public     | orders_rsutil | customer_name        | character varying(50)  | lzo       | f       |       0 | t
 public     | orders_rsutil | prefecture           | character varying(12)  | lzo       | f       |       0 | t
 public     | orders_rsutil | city                 | character varying(20)  | lzo       | f       |       0 | t
 public     | orders_rsutil | area                 | character varying(12)  | lzo       | f       |       0 | t
 public     | orders_rsutil | shop_name            | character varying(20)  | lzo       | f       |       0 | t
 public     | orders_rsutil | customer_segment     | character varying(30)  | lzo       | f       |       0 | t
 public     | orders_rsutil | product_category     | character varying(30)  | lzo       | f       |       0 | t
 public     | orders_rsutil | product_sub_category | character varying(100) | lzo       | f       |       0 | t
 public     | orders_rsutil | product_id           | character varying(10)  | lzo       | f       |       0 | t
 public     | orders_rsutil | product_name         | character varying(100) | lzo       | f       |       0 | t
 public     | orders_rsutil | product_description  | character varying(200) | lzo       | f       |       0 | t
 public     | orders_rsutil | product_container    | character varying(100) | lzo       | f       |       0 | t
 public     | orders_rsutil | base_margin          | double precision       | bytedict  | f       |       0 | f
 public     | orders_rsutil | supplier             | character varying(30)  | lzo       | f       |       0 | t
 public     | orders_rsutil | deliver_date         | date                   | lzo       | f       |       0 | t
 public     | orders_rsutil | ship_date            | date                   | lzo       | f       |       0 | t
(27 rows)

# SELECT COUNT(*) FROM public.orders_rsutil;
  count  
---------
 2142464
(1 row)

そして、既存のテーブルは_$oldという文字列が末尾に付与された形でリネームされています。

# SELECT * FROM pg_table_def WHERE schemaname = 'public' AND tablename = 'orders_rsutil_$old';
 schemaname |     tablename      |        column        |          type          | encoding | distkey | sortkey | notnull 
------------+--------------------+----------------------+------------------------+----------+---------+---------+---------
 public     | orders_rsutil_$old | order_id             | integer                | none     | t       |       1 | t
 public     | orders_rsutil_$old | order_date           | date                   | none     | f       |       2 | t
 public     | orders_rsutil_$old | priority             | character varying(12)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | quantity             | smallint               | none     | f       |       0 | t
 public     | orders_rsutil_$old | sales                | double precision       | none     | f       |       0 | f
 public     | orders_rsutil_$old | discount_rate        | double precision       | none     | f       |       0 | f
 public     | orders_rsutil_$old | ship_mode            | character varying(20)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | profit               | integer                | none     | f       |       0 | t
 public     | orders_rsutil_$old | unit_price           | integer                | none     | f       |       0 | t
 public     | orders_rsutil_$old | ad_expenses          | integer                | none     | f       |       0 | t
 public     | orders_rsutil_$old | shipping_cost        | integer                | none     | f       |       0 | t
 public     | orders_rsutil_$old | customer_name        | character varying(50)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | prefecture           | character varying(12)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | city                 | character varying(20)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | area                 | character varying(12)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | shop_name            | character varying(20)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | customer_segment     | character varying(30)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | product_category     | character varying(30)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | product_sub_category | character varying(100) | none     | f       |       0 | t
 public     | orders_rsutil_$old | product_id           | character varying(10)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | product_name         | character varying(100) | none     | f       |       0 | t
 public     | orders_rsutil_$old | product_description  | character varying(200) | none     | f       |       0 | t
 public     | orders_rsutil_$old | product_container    | character varying(100) | none     | f       |       0 | t
 public     | orders_rsutil_$old | base_margin          | double precision       | none     | f       |       0 | f
 public     | orders_rsutil_$old | supplier             | character varying(30)  | none     | f       |       0 | t
 public     | orders_rsutil_$old | deliver_date         | date                   | none     | f       |       0 | t
 public     | orders_rsutil_$old | ship_date            | date                   | none     | f       |       0 | t
(27 rows)

# SELECT COUNT(*) FROM public.orders_rsutil_$old;
  count  
---------
 2142464
(1 row)

その他利用についてのポイント

ドキュメントに記載されているその他のポイントで幾つか気になったものを以下に列挙しておきます。

  • 上記記載にもあるように、--do-execute trueオプションを使うことで実際のデータベースに反映がなされるようになります。実際にデータベースに反映を行う際は、開発及びテストシステムに対してユーティリティを徹底的にテストする事をお勧めします。
  • そして、生成されたスクリプトを実行する前に、運用しているシステムの手動スナップショットを取るようにしておいてください。
  • 大量データを移行する際は、移行時にクラスタ利用ユーザー・顧客に悪影響を及ぼす事の無いように。実施時間を避ける等の対応を行うようにしてください。
  • AWSは徹底的に様々なシステム上でこのソフトウェアをテストしていますが、データベースに対してこのユーティリティを実行する事による影響に対して責任は追いません。自己責任でお願いします。

また、上記実行時のものとは別に、利用可能なオプションも幾つかありますので適宜ご確認の上、色々試してみてください。

まとめ

以上、『Amazon Redshift Column Encoding Utility』に関するご紹介でした。テーブル定義の見直しやデータ移行作業については非常に手間の掛かる、面倒なものです。その作業負荷をこのツールである程度軽減出来るのではないでしょうか。こちらからは以上です。