Amazon Redshift Serverless 性能検証（簡易ETL編）

### 本記事の目的
データプロバイダーとしてデータコンシューマに対して適切にフォーマットされたデータを供給する共通機能を設計するにあたり、候補となるAthenaを機能面、非機能面で評価したのが下記の記事。
[Amazon Athena 性能検証（簡易ETLケース）](https://qiita.com/thashi/items/cbbc33b03608be2da2d9)
本記事はその続編。
データの前処理という観点ではGlue Databrew、Glueという選択肢があるが、こちらは後日評価して記事を投稿する予定。Redshiftを使って実装しようという発想は私にはないが仮に実装したらどうなるのかを実験したのがこの記事。

#### 共通処理の例
- 余計な空白削除処理
- 日付のフォーマット変換(yyyymmdd⇒yyyy/mm/dd)
- 文字列結合
- コードの桁揃え、文字のパディング(数字コードなら0埋め)

#### 評価の観点
- データ変換処理のための標準関数の品ぞろえ
- 標準機能での性能
- 標準関数でカバーできない場合（があったとしてもの趣旨）のユーザ定義関数の開発容易性と性能
- データカタログ機能（Glue）との統合

#### テストデータの準備
公平性のためAthenaで利用したデータと同じものを利用。1000万レコードで生データが約700MBのテストデータ
[Amazon Athena 性能検証（簡易ETLケース）](https://qiita.com/thashi/items/cbbc33b03608be2da2d9)

### テスト環境
記事投稿時、Redshift Serverlessには$300の無償利用枠があり、この機会にPOCで評価しておくことをお勧めする。ベースキャパシティについてはデフォルト値の大きさに要注意。本記事では下記の設定値に下げてPOCを実施。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325048/a8271ce2-2e34-63b8-026d-384f4d0b9468.png)

### テスト内容
- 準備(テーブル作成)
```
CREATE TABLE test6
(
  col1        VARCHAR(30) NOT NULL,
  col2        VARCHAR(30) NOT NULL,
  col3        VARCHAR(30) NOT NULL,
  col4        VARCHAR(30) NOT NULL,
  col5        VARCHAR(30) NOT NULL,
  col6        VARCHAR(30) NOT NULL,
  col7        VARCHAR(30) NOT NULL
);
```  

<br>

- 準備（Python UDF関数）
```
create function dateconvert(a VARCHAR)
  returns VARCHAR
stable
as $$
  if a == "99999999":
    return "9999-12-31"
  elif a == "null":
    return "0001-01-01"
  else:
    return a[0:4] + '-' + a[4:6] + '-' + a[6:8]
$$ language plpythonu;
```
Athena検証で利用したJava版UDFより若干手抜きしている。

<br>



- データの投入（ファイル⇒Redshift）
```
copy test6 from 's3://xxx/test6/data.csv'
```
Athena同様に、実行時間のばらつきがある。初回実行時に遅いが平均すると約7秒

<br>




- 簡易ETL処理（Redshift⇒ファイル）ケース1(8RPU)

```
unload ('SELECT trim(col1) as col1, dateconvert(col2), dateconvert(col3), dateconvert(col4), lpad(col5,7,\'0\') as col5, concat(lpad(col6,7,\'0\'), lpad(col7,7,\'0\')) as col6 FROM test6')
to 's3://xxx/test7/' 
PARALLEL OFF
ALLOWOVERWRITE
```
エラー内容は下記の通り。何回実行しても同じエラーとなる。
```
ERROR: UDF working memory threshold exceeded Detail: --省略-- error: UDF working memory threshold exceeded code: 10002 context: query: 207803[child_sequence:1] location: stream.cpp:48 process: 
```

<br>


- 簡易ETL処理（Redshift⇒ファイル）ケース1(16RPU)
ケース1(8RPU)のエラーが解消された。実行時間は約10秒

<br>


- 簡易ETL処理（Redshift⇒ファイル）ケース2(8RPU)：ケース1と同じクエリーだがPARALLEL ON
```
unload ('SELECT trim(col1) as col1, dateconvert(col2), dateconvert(col3), dateconvert(col4), lpad(col5,7,\'0\') as col5, concat(lpad(col6,7,\'0\'), lpad(col7,7,\'0\')) as col6 FROM test6')
to 's3://xxx/test7/' 
ALLOWOVERWRITE
```
何回実行してもエラーなし。約3.8秒。デフォルトがPARALLEL OFF。デフォルトの挙動はunload結果が一つのファイルに出力される。PARALLEL ONにすると約5MBファイルが多数出力された。（パラレルにすることでメモリ領域の使い方が変わりメモリーエラーが回避できたと想像）

<br>


- 簡易ETL処理（Redshift⇒ファイル）ケース2(16RPU)
実行時間は約2秒に大幅短縮された。

<br>


- 簡易ETL処理（Redshift⇒ファイル）ケース3(8RPU)：ケース1からUDF呼び出し回数を1回減らしたクエリー
```
unload ('SELECT trim(col1) as col1, dateconvert(col2), dateconvert(col3), col4, lpad(col5,7,\'0\') as col5, concat(lpad(col6,7,\'0\'), lpad(col7,7,\'0\')) as col6 FROM test6')
to 's3://xxx/test7/' 
PARALLEL OFF
ALLOWOVERWRITE
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325048/86697b18-9422-be8e-5140-69a4f2c1f5dd.png)

ほとんどエラーなし。成功する場合の実行時間は約10秒。本記事での時間計測について、クエリエディタで表示される「Elapsed time」を採用している。
「ほとんど」と表現したのは連続して検証している時に2回上記のworking memoryエラーをみたため。

<br>


- 簡易ETL処理（Redshift⇒ファイル）ケース3(16RPU)
実行時間は約9秒。8RPUと16RPUでの単体レスポンス向上は僅か。

<br>


### 検証サマリ
- I/Oがパラレル環境下においてはRPUの値によって性能が向上するが、I/Oがシングルファイル構成においてはRPU値による性能向上がわずかとなる。
RPU値によってきまるメモリ利用可能サイズがあり、閾値を超えるとServerlessのメカニズムは働かずエラーとなる。

||UDF2回コール|UDF3回コール|UDF2回、パラレル出力|
|---|---|---|---|
|8RPU  |約10秒|エラー(メモリ閾値超)|約3.8秒|
|16RPU|約9秒|約10秒|約2秒|


因みに、ケース1(8RPU or 16RPU)はメモリ利用量に関する内部閾値を探るために後から試したもの。
この内部制約については下記ドキュメントには記載がないため注意が必要。RPUの変更は手動(not Serverless)のため望ましい挙動ではない。
[UDFの制約](https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/udf-constraints.html)
また、公式ドキュメント制約事項に記載のとおり、同時実行数に制約があるためAthenaのように多数UDFを利用した処理の多重処理ができず、大量データの簡易ETL処理においてRedshiftのUDFを使った処理が現実的に難しいと結論付けた。もちろんあらかじめメモリ使用量が計算でき、それに必要なRPU値を見積もることができれば可能だが。また、RPU値の常時高騰値がコストに影響することも無視できない。
[Serverlessのコスト=USD 0.494 (1 RPU 時間あたり)](https://aws.amazon.com/jp/redshift/pricing/)

また一部のユーザ定義関数においては処理場所がリーダーノードになることがドキュメントに記載されている点にも注意が必要。簡易ETL目的で関数多用すれば、スケールするコンピュートノードではなくスケールしないリーダーノードに負荷をかけることになり、結果スケーラブルでないRedshift環境を生むこととなるため避けるのが賢明な判断。

### その他
- memory thredhold errorの解消手段がないかはAWSサポート問い合わせして確認が必要。