Snowflake をお使いの皆さん、これまでに 4XL のウェアハウスを OutOfMemoryError で止めた ことがありますか? 私はあります!
テラバイト級のデータ量も涼しい顔で捌くあの Snowflake を落とすなんて、一体どれだけ膨大なデータを食わせたんだ!? と思うところですが、実際に止めたのは わずか10行の CSV ファイル x 100ファイル です。
そのデータというのは、だいたい以下のような感じです。
col_1 | col_2 | col_3 | ... | col_15000 | |
---|---|---|---|---|---|
1 | 0.8 | 1.0 | 1.1 | ... | 0.4 |
2 | 1.0 | 1.2 | 0.9 | ... | 0.3 |
: | : | : | : | ... | : |
10 | 0.9 | 1.2 | 1.0 | ... | 0.4 |
そうです。わずか10行しかないデータですが、 15,000列 あります。
「それデータモデリングをミスってるやん」というツッコミはあると思いますが、今回はそのあたりについてはノーコメントです。諸事情ありこのようなデータを扱うことになりました。
今回は Snowflake でもなかなか扱うことが難しい (横方向に) クソデカデータの世界 をご案内したいと思います。
クソデカテーブルを作る
まずテーブルを作ります。
CREATE TABLE foo (
col_1 number,
col_2 number,
col_3 number,
:
col_15000 number
)
この時点でエラーが出てくれるならまだ良かった。。。
なんとこのクエリは問題なく実行でき、テーブルは作成できます。 作成できてしまいます。 作成できてしまったが故に、「さすが Snowflake、列数が多いテーブルも簡単に扱えるんだね!!」と高をくくってしまいます。
ちなみに PostgreSQL の場合は1テーブルあたり最大 1,600 列だそうです。まぁそれだけあれば普通は困らないですよね。
クソデカテーブルを扱うということ
クソデカテーブルを扱うということはどういうことなのか、みなさんイメージがついていないでしょう。こういうことです。
(1) 全カラムを列挙するようなストアドプロシージャを作ろうとすると「関数定義が長すぎます」と怒られる
ストアドプロシージャの定義は (圧縮後のサイズで) 100KB までという制限があるらしいです。そんな制限があることすら知らない人が大半じゃないでしょうか。
(2) データ1000件 INSERT のクエリコンパイルが1時間経っても終わらずタイムアウト
「クエリ実行がタイムアウト」ではないですよ? 「クエリコンパイルがタイムアウト」です。
詳しくはわからないですが、プルーニングのための何らかの処理が行われており、クエリコンパイルそのものではなくそちらの方に時間がかかるらしいです。
どうしても取り込みたい
兎にも角にもデータが入れれないことには何もはじまりません。
ちなみに、なんやかんや試行錯誤の結果、データを入れさえすれば SELECT クエリは案外普通に通ることがわかりました。つまりはデータを入れてしまえば勝ちです。
しかし上述のとおりで MERGE だの INSERT だのを使用して他テーブルを結合してクソデカテーブルにデータ追加することは難しく、 残す手段は「外部ステージからのデータロード」のみ となりました。
あらかじめ結合しておいた15,000列の CSV ファイルを外部ステージ (Amazon S3) に100個ほど配置、COPY INTO を試みます。これが最後の頼みの綱です。
たのむ、成功してくれ・・・!
OutOfMemoryError
おしまいです。
メモリ不足だというなら金で殴ればいいじゃない、ということで当時の最大サイズであった 4XL のウェアハウスを使ってやってみましたが、それでもダメでした。
たったひとつのマッチョな解決策
もうこれでデータを投入するためのすべての手段が絶たれたかのように思われましたが、ここであることに気が付きました。
「1ファイルだけなら COPY INTO が成功するっぽいぞ?」
その当時はまだ理由がわかっていませんでした、1ファイルずつなら取り込んでいけることが判明しました。しかし取り込みたいファイルは100万以上あったため、1ファイルずつ取り込んでいくことは現実的ではありません。
ここで妙案を思いつきます。
「1ファイルに固めてしまえるなら、10行でも100万行でも取り込めるのでは?」
大量の行データを扱うのには強いはずの Snowflake です。可能性はあります。
試しに「10行 x 15,000列 x 10,000ファイル」を「100,000行 x 15,000列 x 1ファイル」に 気合いで結合 したので、これを先ほど同様に COPY INTO してみます。
。。。
成功しました。
かくして1.5万列データをテーブルに投入することができ、事なきを得ました。
あとからわかったことですが、Snowflake ではデータロードの際に複数スレッドで各ファイルを処理しており、その際に1.5万列分の巨大なメタデータがスレッドの数だけ読み込まれることによりメモリ不足に陥っていたようです。1ファイルに固めた場合は1スレッドしか動かずメタデータが1スレッド分しか読み込まれないため、メモリ不足に陥らずにデータロード処理を続行できたということですね。
まとめ
Snowflake で1.5万列のテーブルを扱うのは不可能ではないっぽいですが、どう考えてもしんどいので真似しないほうがいいと思います。