はじめてのRedshift

Redshiftを使ってみる勉強会を社内で実施したのでその資料を公開します。

## Redshiftとは

AWSで提供される列指向データベースのマネージドサービス。
列指向のため、主に分析システムのカラムの集計などの利用に向いており、行レベルでの処理には向いていない。一般的なDBMSではフルスキャンとなるようなものもカラムが限定されていれば効率的に実施できる。

## Redshiftの料金

お試し利用を行う想定では、dc1.largeを使うことになるかと思います。
東京リージョンでは$0.314/hです。これで0.16TBまで利用可能です。

※最新の正確な料金は以下を参照ください。
　https://aws.amazon.com/jp/redshift/pricing/


## Redshiftへ接続するクライアント(EC2)のセットアップ

### role作成

| 設定箇所 | 設定値 |
|:--|:--|
| ロールタイプ | AWSアカウントロール > Amazon EC2 |
| アタッチするポリシー  | AmazonRedshiftFullAccess  |

### ec2の立ち上げ

無料利用枠のAmazon Linux(t2.micro)でインスタンスを立ち上げます。
rootになって、PostgreSQLのクライアント(psql)をインストールします

```
yum update -y
yum install postgresql95
```

## Redshiftのセットアップ

### マネジメントコンソールから実施

記事公開時点でまだ日本語化されていないようなので敷居が高いかもしれませんが、設定内容は多くないです。
ここからは以下の表の通り設定を行った前提で書きます。

| 設定箇所 | 設定値 |
|:--|:--|
| Cluster Identifier | firstredshift |
| Database Name | mydb |
| Database Port | 5439 |
| User Name | awsuser |
| Nord Type | dc1.large |
| Availability Zone | （立ち上げたEC2と同じAZ） |

立ち上げると、ClusterのConfigurationのページでEndpointが表示されているのでメモっておきます。
例：firstredshift.XXXXXXXXXX.ap-northeast-1.redshift.amazonaws.com:5439


### CLIで実施

[Redshiftハンズオン](https://speakerdeck.com/konpyu/redshifthanzuon)で紹介されていますのでそちらを参考に。

## Redshiftへの接続

以下で接続します。

```
psql -h firstredshift.XXXXXXXXXX.ap-northeast-1.redshift.amazonaws.com -U awsuser -p 5439 -d mydb
```

【10/19追記】
Redshiftの接続に失敗する場合は、Redshiftのセキュリティグループでインバウンドルールの許可する範囲に入っていない可能性が高いです。Redshiftの接続用のポート5439をすべてから許可するなど、設定変更をしてください。

## Redshiftへのデータ投入

### データ投入に利用するIAMユーザを用意

ユーザを作成して、アクセスキーを生成します。ユーザには以下のポリシーをアタッチしておきます。

* AmazonRedshiftFullAccess
* AmazonS3ReadOnlyAccess



### 投入するデータ

こんな形式のデータを利用します。簡単な[スクリプト](https://gist.github.com/nyasba/b7ec905a377bee8fb91586cdecfc353f)で作成した全く意味のないデータです。これをS3においておきます。

```
created_at,max,min
2016-10-01 00:00:00,351,6
2016-10-01 00:00:01,428,117
2016-10-01 00:00:02,736,81
2016-10-01 00:00:03,943,749
2016-10-01 00:00:04,883,113
2016-10-01 00:00:05,619,450
2016-10-01 00:00:06,580,475
2016-10-01 00:00:07,656,310
2016-10-01 00:00:08,737,552
2016-10-01 00:00:09,702,655
2016-10-01 00:00:10,855,698
```

### テーブルの作成

上記のデータを投入するためにテーブルを作成します。

```psql
CREATE TABLE sample1
(
  created_at     TIMESTAMP WITHOUT TIME ZONE NOT NULL,
  max_value      INTEGER NOT NULL,
  min_value      INTEGER NOT NULL
);
```

### データ投入

の前に、試しにinsertして、selectしてみましょう

```psql
mydb=# \timing
Timing is on.
mydb=# insert into sample1 values( '2016-10-10 12:34:56', 1000, 0 );
INSERT 0 1
Time: 113.910 ms
mydb=# select * from sample1;
     created_at      | max_value | min_value
---------------------+-----------+-----------
 2016-10-10 12:34:56 |      1000 |         0
(1 row)

Time: 6.514 ms
```

次は、1日分（86400行）のデータを一括投入してみます。
credentialsで先ほど作成したユーザのアクセスキーを使います。

```
copy sample1 from 's3://firstredshift/sample20161001.csv'
credentials 'aws_access_key_id=<access-key-id>;aws_secret_access_key=<secret-access-key>'
DELIMITER ','
IGNOREHEADER 1
REGION 'ap-northeast-1';
```

実際のシステムでは上記のような理想的なINPUTファイルではなく、固定長であったり、日付形式が違ったり、zip圧縮されていたり、と悩みがいろいろあると思いますが、ある程度対応しているようです。このあたりが参考になるかとおもいます

* [AWS Redshiftドキュメント: COPY コマンドを実行する](http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/tutorial-loading-run-copy.html)
* [【AWS】RedshiftにデータをロードするCOPYコマンドについて - TASK NOTES](http://www.task-notes.com/entry/20150907/1441594800)


## Redshiftからの参照

データ数をさらに30日分に増やして、いろいろやってみましょう。

```psql
mydb=# select count(*) from sample1;
  count
---------
 2592000
(1 row)

Time: 20.480 ms
mydb=# select sum(max_value) from sample1;
    sum
------------
 1728058004
(1 row)

Time: 1021.840 ms
mydb=# select sum(min_value) from sample1;
    sum
-----------
 863331248
(1 row)

Time: 972.636 ms
mydb=# select sum(max_value) from sample1 where created_at between '2016-10-10' AND '2016-10-12';
    sum
-----------
 115238185
(1 row)

Time: 1011.949 ms
mydb=# select sum(min_value) from sample1 where created_at between '2016-10-10' AND '2016-10-12';
   sum
----------
 57783916
(1 row)

Time: 1037.251 ms
```

うーん、こんなもんかという感じ。

### sortkeyの活用

Redshiftには実はsortkeyというものがあり、sortkeyに指定された順にデータを保持しています。
where句でcreated_atの期間指定をするようなケースでは読み込む対象データが限定することが可能です。

テーブルをsample2として作り直します。sortkeyにはcreated_atを指定します。

```psql
CREATE TABLE sample2
(
  created_at     TIMESTAMP WITHOUT TIME ZONE NOT NULL,
  max_value      INTEGER NOT NULL,
  min_value      INTEGER NOT NULL
)
SORTKEY (created_at);
```

先ほどと同じ処理を実行。

```psql
mydb=# select count(*) from sample2;
  count
---------
 2592000
(1 row)

Time: 20.242 ms
mydb=# select sum(max_value) from sample2;
    sum
------------
 1728058004
(1 row)

Time: 33.222 ms
mydb=# select sum(min_value) from sample2;
    sum
-----------
 863331248
(1 row)

Time: 33.114 ms
mydb=# select sum(max_value) from sample2 where created_at between '2016-10-10' AND '2016-10-12';
    sum
-----------
 115238185
(1 row)

Time: 14.508 ms
mydb=# select sum(min_value) from sample2 where created_at between '2016-10-10' AND '2016-10-12';
   sum
----------
 57783916
(1 row)

Time: 14.507 ms
```

確かに早くなっていますね。ただし、全件のsumも同様に早くなっているのが謎です・・
＃sortkeyは影響しているとは思うのですが試してみたらこうなった、くらいの結果です。その点はご了承ください。

### VACUUM

初回にCOPYで一括データ投入した今回のような場合は不要ですが、毎日データが生成されて取り込む必要がある場合は、性能を悪化させないために適宜[VACUUM処理](http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/t_Reclaiming_storage_space202.html)を行うことを検討しなければいけないようです。

と思っていたら機能拡張されていて、日付のソートキーの場合は問題なくなっていました。すばらしい。

[Redshiftアップデート：COPYやVACUUMの機能向上、SSLロギングの情報追加等](http://aws.typepad.com/aws_japan/2016/03/redshift-improvements-1-0-1040.html)
> 2, COPYで条件にそったデータを挿入した場合、ソート済の領域としてマージされるように

> Redshiftの中では表のデータは「ソート済領域」と「非ソート済領域」に分けて管理されています。VACUUMを使ってソートされたデータはソート済領域に保存され、追加データは非ソート領域に保存されます。
今回の機能拡張では、条件を満たした場合にCOPYで追加したデータがソート済領域に追加されるようになります。その条件はマニュアルの以下のページに記載されています。

> 条件は以下の通りで、これらを全て満たしている必要があります。
* 表がコンパウンドソートキー（Interleaved Sort Keyではなく）を使っていて、かつソートキー列が1つのみ
* ソートキーの列がNOT NULL
* 表が100%ソート済か、もしくは空（から）
* 新しく追加されるソートキー列の値が既存データよりソート順で大きい値を持つ

> これは、列に常に大きい値が挿入されるようなケース、つまり時刻がソートキーになっていて、そこに追加で新しいデータを追加し続けるような表構造（時系列でデータを入れ続ける）の場合に役に立ちます。

## QueryやLoadなどの状態確認

マシンリソースや接続数だけでなく、QueryやLoadのSQLごとの情報（実行時間など）もマネジメントコンソールから履歴が確認できる。これは便利ですね。


## 最後に

Redshift,EC2は削除しておくのを忘れずに。

## その他

参考になったサイトを紹介します。

* [Amazon Redshiftでよく使うけど忘れがちなコマンド(クエリ）まとめ](http://qiita.com/fukuiretu/items/dc45617ab23e7f62f722)
* [これからAmazon Redshiftを始める技術者が注意すべき１１つのポイント](http://dev.classmethod.jp/cloud/aws/redshift-point-to-notice/)
* [これからAmazon Redshiftを始める技術者が注意すべき２２つのポイント](http://dev.classmethod.jp/cloud/aws/redshift-point-to-notice-2/)