Amazon Redshift: 『パフォーマンスチューニングテクニック Top 10』手順の実践(9).非効率的なデータロード

Amazon Redshift

当エントリは先日投稿したシリーズ『『Amazon Redshiftのパフォーマンスチューニングテクニック Top 10』手順実践』の第9弾です。課題#9の『非効率的なデータロード』について内容を見て行きたいと思います。


データをS3から取り込む際にはCOPYコマンドで所定のファイル、もしくは所定のバケット・フォルダ配下のファイル一式を所定のテーブルに取り込みます。その際に推奨されている手順としては以下のものがある、とエントリでは紹介されています。

  • ファイルは圧縮しておく事
  • ファイルは分割しておく事(ノード構成からなるスライス数の倍数になるようなファイル数に分割)

手順実践

投入対象のテーブルは下記public.customerを利用します。これはRedshiftのチュートリアルで活用されているものです。

CREATE TABLE public.customer (
  c_custkey     	integer        not null sortkey,
  c_name        	varchar(25)    not null,
  c_address     	varchar(25)    not null,
  c_city        	varchar(10)    not null,
  c_nation      	varchar(15)    not null,
  c_region      	varchar(12)    not null,
  c_phone       	varchar(15)    not null,
  c_mktsegment      varchar(10)    not null)
diststyle all;
CREATE

初期投入データもこのチュートリアルから投入。

COPY public.customer FROM 's3://awssampledbuswest2/ssbgz/customer'
CREDENTIALS 'aws_access_key_id=XXXXXXXXXX;aws_secret_access_key=YYYYYYYYYY'
GZIP REGION 'us-west-2';

この投入済みデータを所定のバケットに吐き出したものを使いたいと思います。

# UNLOAD ('SELECT c_custkey,c_name,c_address,c_city,c_nation,c_region,c_phone,c_mktsegment FROM public.customer ORDER BY c_custkey')
TO 's3://xxxxxxx-xxxx-xxxxxx/customer/single-files/redshift-customer'
CREDENTIALS 'aws_access_key_id=XXXXXXXXXX;aws_secret_access_key=YYYYYYYYYY'
PARALLEL OFF;

上記内容のファイルは単一ファイルとして出力させたものです。

$ aws s3 ls 's3://xxxxxxx-xxxx-xxxxxx/customer/single-files/'
2016-09-20 21:11:18          0 
2016-09-20 21:12:17  286529327 redshift-customer000

redshift-performance-tuning-09_011

件数はちょうど300万件、中身はこのような形になっています。パイプ区切りのデータです。

$ mv redshift-customer000 redshift-customer.csv
$ wc redshift-customer.csv 
 3000000 7294613 286529327 redshift-customer.csv
$
$ vi redshift-customer.csv
1|Customer#000000001|IVhzIApeRb|MOROCCO  0|MOROCCO|AFRICA|25-989-741-2988|BUILDING
2|Customer#000000002|XSTf4,NCwDVaWNe6tE|JORDAN   1|JORDAN|MIDDLE EAST|23-768-687-3665|AUTOMOBILE
3|Customer#000000003|MG9kdTD|ARGENTINA7|ARGENTINA|AMERICA|11-719-748-3364|AUTOMOBILE
4|Customer#000000004|XxVSJsL|EGYPT    4|EGYPT|MIDDLE EAST|14-128-190-5944|MACHINERY
5|Customer#000000005|KvpyuHCplrB84WgAi|CANADA   5|CANADA|AMERICA|13-750-942-6364|HOUSEHOLD
6|Customer#000000006|sKZz0CsnMD7mp4Xd0YrBvx|SAUDI ARA2|SAUDI ARABIA|MIDDLE EAST|30-114-968-4951|AUTOMOBILE
7|Customer#000000007|TcGe5gaZNgVePxU5kR|CHINA    0|CHINA|ASIA|28-190-982-9759|AUTOMOBILE
8|Customer#000000008|I0B10bB0AymmC, 0PrRYBC|PERU     6|PERU|AMERICA|27-147-574-9335|BUILDING
9|Customer#000000009|xKiAFTjUsCuxfele|INDIA    6|INDIA|ASIA|18-338-906-3675|FURNITURE
10|Customer#000000010|6LrEaV6KR6PLVcgl2ArL |ETHIOPIA 9|ETHIOPIA|AFRICA|15-741-346-9870|HOUSEHOLD

S3ファイルのCOPY処理検証:単一ファイルの場合

まずは単一ファイルの場合、どれ位時間が掛かるか試してみる事にしましょう。

上記アップロード済の単一ファイルCOPY処理で取り込みます。

COPY public.customer
FROM 's3://xxxxxxx-xxxx-xxxxxx/customer/single-files/'
CREDENTIALS 'aws_access_key_id=XXXXXXXXXX;aws_secret_access_key=YYYYYYYYYY'
CSV DELIMITER AS '|';

都合5回実行してみましたが、およそ20秒近く掛かっていました。ちなみに今回検証に使っているRedshiftクラスタはdc1.large 2ノードと小さめのクラスタです。

# COPY
Time: 20315.496 ms
# TRUNCATE public.customer;
Time: 501.731 ms
# COPY
Time: 19349.804 ms
# TRUNCATE TABLE and COMMIT TRANSACTION
Time: 625.727 ms
# COPY
Time: 19449.845 ms
# TRUNCATE TABLE and COMMIT TRANSACTION
Time: 490.240 ms
# COPY
Time: 19502.525 ms
# TRUNCATE TABLE and COMMIT TRANSACTION
Time: 542.797 ms
# COPY
Time: 19785.045 ms
# 

S3ファイルのCOPY処理検証:ファイルを分割した場合

次いで、ファイルを複数個に分割した時の挙動を確認してみたいと思います。

AWSブログの過去エントリにちょうど良さげな『ファイル分割』のサンプルがありましたのでこれを使ってみたいと思います。splitコマンドを使って所定のファイル数ぶん、単一ファイルを分割するというものです。

AWSのドキュメントでは、クラスタが備えるスライス数の倍数にファイルを分割すると効率良くロードが出来る、との記述があります。

ファイルの数がクラスターのスライスの数の倍数になるようにデータをファイルに分割します。そうすることで、Amazon Redshift はスライス間でデータを均等に分割できます。ノードあたりのスライスの数は、クラスターのノードサイズによって決まります。たとえば、各 DS1.XL コンピューティングノードには 2 つのスライスがあり、各 DS1.8XL コンピューティングノードには 32 個のスライスがあります。それぞれのノードサイズにあるスライスの数については、Amazon Redshift Cluster Management Guide の「クラスターおよびノードについて」を参照してください。

ノードはいずれも並列クエリ実行に関与し、スライス間でできるだけ均等に分散されたデータを処理します。クラスターに DS1.XL ノードが 2 つある場合、データを 4 つのファイルまたは 4 の倍数のファイルに分割します。Amazon Redshift はワークロードを分割するときにファイルサイズを考慮しません。そのため、ファイルを概ね同じサイズにし、それぞれが圧縮後に 1 MB ~ 1 GB になるようにする必要があります。

上記エントリには以下のような記述でsplitコマンドを実行していましたので、

$ split -l `wc -l bigfile.txt | awk '{print $1/32}'` -v bigfile.txt “part-“

手元の環境ではこの様に実行してみました。(Mac上での実行だったので-dオプション(suffixに数字を付ける)が使えなかった)

$ split -l `wc -l redshift-customer.csv | awk '{print $1/8}'` redshift-customer.csv redshift-customer_

分割出来たファイルを所定のS3バケットにアップしたのが以下。

aws s3 ls 's3://xxxxxxx-xxxx-xxxxxx/customer/multiple-files/'
2016-09-20 21:39:53          0 
2016-09-20 21:40:10   35474029 redshift-customer_aa
2016-09-20 21:40:23   35580389 redshift-customer_ab
2016-09-20 21:40:41   35705628 redshift-customer_ac
2016-09-20 21:40:55   35950227 redshift-customer_ad
2016-09-20 21:41:21   35955098 redshift-customer_ae
2016-09-20 21:42:29   35950292 redshift-customer_af
2016-09-20 21:42:47   35954634 redshift-customer_ag
2016-09-20 21:43:02   35959030 redshift-customer_ah

この形で単一ファイル同様にCOPY処理を実行してみます。

COPY public.customer
FROM 's3://xxxxxxx-xxxx-xxxxxx/customer/multiple-files/'
CREDENTIALS 'aws_access_key_id=XXXXXXXXXX;aws_secret_access_key=YYYYYYYYYY'
CSV DELIMITER AS '|';

結果は以下の通り。単一ファイルの20秒に対して、複数ファイルでのCOPY処理はおよそ12秒となり、早くなっています。

INFO:  Load into table 'customer' completed, 3000000 record(s) loaded successfully.
COPY
Time: 13013.339 ms
# TRUNCATE TABLE and COMMIT TRANSACTION
Time: 626.829 ms
# COPY
Time: 12738.892 ms
# TRUNCATE TABLE and COMMIT TRANSACTION
Time: 550.695 ms
# COPY
Time: 12444.918 ms
# TRUNCATE TABLE and COMMIT TRANSACTION
Time: 586.002 ms
# COPY
Time: 13029.853 ms
# TRUNCATE TABLE and COMMIT TRANSACTION
Time: 790.610 ms
# COPY
Time: 12755.876 ms
# 

まとめ

以上、『Amazon Redshiftのパフォーマンスチューニングテクニック Top 10』トピック9つめ、"非効率的なデータロード"に関する対処方法のご紹介でした。ファイルのCOPY処理(ロード)に時間が掛かっているという場合、『ファイルの圧縮』及び『ファイルをスライス数の倍数に分割』して取り込む事でロード時間の短縮が図れるかと思いますので是非お試しください。次はいよいよ最終トピックです。