テーブル設計の実践とそのポイント
ここまでは割と机上論で進めてきましたが、ぼちぼち実践作業の方も進めていきましょう。
上記の過程で、取り込むべきファイル(テーブル)は絞り込めました、次に取り組むべき作業はそのテーブル設計を行い、ファイルをそのテーブルの型に合わせて投入すべく必要な処理(加工・編集・除去など)を行うことです。
この作業については、当セクションで話を進める『テーブル設計』および、この後に説明する『ファイル加工』をそれぞれ同時進行で進めて行って、より最適な形へと歩み寄ることで1つのゴールを迎えるイメージで考えていただくと良いかも知れません。テーブル設計をまず定め、それに対するファイルの内容を確認し、それらの内容をテーブル設計にフィードバックさせていく、というイメージです。
Redshiftにおけるテーブル設計は、主に以下の要素について定めていく必要があります。RedshiftはPostgreSQLに準拠していますので、基本的にはテーブル構成もPostgreSQLの構文で定めていくことになりますが、Redshift固有の設定情報も含まれています。それぞれの内容についても簡単に解説を加えていきます。
データの型
データが格納される項目の型。大きく『数値型』『文字型』『日付型』『ブール型』の4種類に分類され、内容や上限値などによってさらに細かく分類されています。項目値の内容によって、適切と思われる型を適宜当てはめていきます。数値型であればその上限値を見極めてSMALLINT/INTEGER/BIGINTなど、文字列系の値であればマルチバイト文字の有無でCHAR/VARCHARを、日付項目やタイムスタンプであればDATE/TIMESTAMPなど、という風に。設計書ベースですでに情報がある場合、それらの情報を元にして項目のデータ型や桁数を定めて行きます。
データの桁数
この部分は主に文字列型の項目が該当するでしょうか。Redshiftではファイルエンコードがutf8であることが前提となっていますので、文字数のカウントや項目長定義もこの形で進めていく必要があります。
VARCHAR(可変長文字列)型の項目については、桁数がバイト数換算となるので桁数割り出しは若干面倒です。文字を1つずつカウントするのも手間なので、私の場合は以下のような簡易スクリプトを使い、項目列ごとのバイト数カウントを算出しています。項目が区切り文字で正しく分割されていることが前提ですが、所定の位置の情報を抽出し、バイト数を算出、そのバイト数がこれまでのものより大きいものであればその値を登録しておく、という非常にシンプルなものです。テーブル項目数が多くなってくるとこの辺りの確認作業工数は馬鹿にならない労力となってきますので、地味ではありますが重宝しています。
以下はGroovyで書いたものですが、他の言語でも同様のスクリプトでコードを用意し、内容確認作業を効率化させていくと良いでしょう。
File file = new File("/path/to/csvfiles/sample_table.csv"); Map columnLengthMap = [:]; int count = 0; file.eachLine { line -> String[] columns = line.tokenize(","); if (count != 0) { String user_name = columns[1]; if (user_name.getBytes().size() > columnLengthMap.get("user_name")) { columnLengthMap.put("user_name", user_name.getBytes().size()) } } count++; } println columnLengthMap;
列圧縮タイプ
列指向型(カラムナ型)を掲げているRedshiftでは、列ごとにデータ圧縮処理を施すことでストレージスペースの節約を行い、ディスクI/Oを減少させることによってクエリパフォーマンスの向上を図ることができます。
テーブル作成の時点で任意の列圧縮タイプを定めることも可能ですが、RedshiftではCOPY時にオプション指定を行うことで、Redshiftが推奨する列圧縮タイプの情報を知ることもできます。以下の『自動圧縮ありでテーブルをロードする』がその手順です。今回はこの『自動圧縮あり』ロードを使ってみることにします。なので現時点では特にCREATE TABLE文での設定は行いません。
分散キー
Redshift固有の設定値。クラスタ内に配置されたすべてのノードに対してデータを分散させ、並列処理を指示しているAmazon Redshiftでは、いかにその分散したデータに対して迅速に処理を行わせるか、また分散したデータを移動させずに処理させるか(移動させるような設定にしてしまうとその移動に伴うコストがパフォーマンス低下を招いてしまう)がポイントとなります。
この『分散キー』については、単一テーブルを構成する時点では明確に『この分散スタイル、分散キーが最善である』という情報は見えてこないかも知れません。テーブルの使用用途や、他の要因との関連性(そのテーブルに対してどういうクエリが投げられる想定なのか、また他テーブルと結合して利用される場合、どういったキーで結合されるのか)に左右されてくる部分も多々あるからです。この点についてはAWS公式ドキュメントにその判断基準なども詳細に解説されていますので、現時点で良いと思われる設定値を借り決めで行っておく、で良いと思います(必要に応じて変更を行いましょう)。
ソートキー
従来のRDBMSでもおなじみソートキーですが、Redshiftのクエリパフォーマンスを最適化する上でも重要な要素です。最適なパフォーマンスに合わせたソートキー指定を行っておくことで、大量のデータアクセスに対しても迅速なレスポンスを返すことができます。この点についてもAWS公式ドキュメントでベストプラクティスが公開されていますので参考にすると良いでしょう。
制約
制約については主に『主キー(PRIMARY KEY)制約』『外部キー制約』『NOT NULL制約』が挙げられるでしょう。前者については、Redshiftでは形として定めることはできますが、実はRedshiftでは主キーや外部キーはその役目を果たしてはくれません(参照:制約の定義 - Amazon Redshift)。統計情報を取得する際のヒントとして情報を用いることはありますが、機能としての役割は持っていません。例えば、テーブル項目でPRIMARY KEYを設定していた項目に対し、重複登録を行うようなINSERT文/COPY文を実行しても、そのまま重複登録できてしまいます。従来のRDBMSの考えで進めてしまうと相違が発生し得るので、この点は注意する必要があります。
NOT NULL制約については、既存のRDBMSと同様の制約が適用されます。