ある案件で外部データをSnowflakeに取り込む作業を担当しました。手順書どおりにSQLを実行するだけなら難しくないのですが、「なぜこの順番なのか」「なぜ開発と本番で手順が違うのか」が最初はわかりませんでした。
この記事では、作業の中で「なぜこうするんだろう」と調べて納得したことや、実際に手を動かして初めて気づいたことを5つまとめます。Snowflakeでのデータ投入に関わる方の参考になれば幸いです。
なお、この記事ではAWSの各サービスを以下の略称で表記します。
まず、データがSnowflakeに届くまでの全体像を簡単に示します。
外部S3 → (何らかの方法で)自社S3 → Snowflake外部ステージ → Snowflake内部テーブル
外部S3から自社S3への転送方法はいくつかあります。今回の案件ではEC2上にEBS(Elastic Block Store)を介してデータを受け取り、そこから自社S3にアップロードする方法をとりました。この記事では、自社S3にデータが到着した後のSnowflakeへの取り込み部分に焦点を当てます。
開発環境では外部ステージから直接取り込みますが、本番環境では「クローン → 作業 → 入れ替え」という安全策を挟みます。この違いの理由が、今回の記事の核心です。
Snowflakeにおける外部ステージとは、自社のS3バケットをSnowflakeから参照する仕組みです。データの実体はS3上にあり、クエリのたびにS3を読みに行きます。
一方、COPY INTOで内部テーブルに取り込むと、データはSnowflakeが管理するストレージにコピーされます。
| 項目 | 外部ステージ | 内部テーブル(COPY INTO後) |
|---|---|---|
| データの場所 | 自社S3 | Snowflake内部ストレージ |
| S3を削除した場合 | データが見えなくなる | 影響なし |
| クエリ速度 | 遅い(毎回S3を読む) | 速い |
作業中にふと「外部ステージのデータって、S3を消したらどうなるんだろう」と疑問に思い調べたところ、外部ステージはS3への参照にすぎないため、S3を削除するとデータが見えなくなることがわかりました。「Snowflakeにデータがある」と思っていても、実際にはS3を参照しているだけ、というケースがありえます。COPY INTOで内部テーブルに取り込んで初めて、S3から独立したデータになるという点は、仕組みを理解していないと見落としやすいポイントだと感じました。
データの入れ替え作業において、手順書ではTRUNCATEとDROPが使い分けられていました。これらは似ているようで、影響範囲がまったく異なります。
| 操作 | データ | テーブル定義 | 権限(GRANT) |
|---|---|---|---|
| TRUNCATE | 消える | 残る | 残る |
| DROP | 消える | 消える | 消える |
最初は「なぜ全部TRUNCATEで統一しないのだろう?」と疑問でした。理由は**「データの入れ替えと同時に、テーブル定義の変更(カラムの追加や、データ型の大幅な変更など)を行うかどうか」**の違いでした。
テーブルの定義変更がない場合は、箱を残したまま中身だけを空にするTRUNCATEだけで十分です。 しかし、カラム構造が変わるなど「箱の形そのもの」を作り直す必要があるテーブルについては、一度DROPして新しい定義でCREATEし直す(箱ごと捨てて、新しい箱を作る)必要があります。
※なお、調べてわかったのですが、単なるVARCHAR(8)からVARCHAR(10)への文字数拡張であれば、わざわざDROPせずともALTER TABLEコマンドで瞬時に変更可能でした。そのため、DROPが本当に必要なのはデータ型そのものを変えるような破壊的変更を伴う場合になります。
ここで一番の注意点は、単純にDROPすると権限(GRANT)も一緒に消滅してしまうということです。再作成後に権限を付与し忘れると、ユーザーがデータにアクセスできなくなってしまいます。
本番環境でのデータ投入では、まずスキーマをクローンします。
CREATE SCHEMA TEMP_SCHEMA CLONE PROD_SCHEMA;
Snowflakeのクローンは「ゼロコピークローン」と呼ばれ、物理的にデータを複製しません。同じデータへの参照を共有し、変更があった部分だけ別に保存されます。そのため、一瞬で完了し、ストレージコストもほぼかかりません。
ここで気になったのは、クローンをどこに作るのかという点です。調べてみると、クローンは本番スキーマと同じデータベース内に作成し、すべての作業はクローン側で行います。本番スキーマには一切触れません。
MY_DATABASE(データベース)
├── PROD_SCHEMA(本番スキーマ) ← 触らない
└── TEMP_SCHEMA(クローン) ← こちらで作業
この設計により、作業中に問題が起きても本番には影響しません。
クローン側での作業が完了したら、スキーマ名のリネームで入れ替えます。
ALTER SCHEMA PROD_SCHEMA RENAME TO OLD_PROD_SCHEMA; -- 旧本番を退避
ALTER SCHEMA TEMP_SCHEMA RENAME TO PROD_SCHEMA; -- クローンを本番に昇格
ここで重要なのは、旧本番をすぐに削除しないことです。OLD_PROD_SCHEMAとして残しておけば、新本番に問題が見つかった場合にリネームだけで即座に戻せます。
-- 問題発生時のロールバック
ALTER SCHEMA PROD_SCHEMA RENAME TO TEMP_SCHEMA;
ALTER SCHEMA OLD_PROD_SCHEMA RENAME TO PROD_SCHEMA;
旧本番の削除(DROP SCHEMA OLD_PROD_SCHEMA)は、新本番の動作確認が完了してから行います。
DROP → CREATEでテーブルを再作成した場合、テーブルへの権限が消えるためGRANTで再設定が必要です。
-- テーブルへの権限(データの読み書き)
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE my_table TO etl_role;
GRANT SELECT ON TABLE my_table TO readonly_role;
さらに、スキーマをリネームした後にはスキーマレベルの権限も設定が必要です。
-- スキーマへの権限(構造の操作)
GRANT USAGE, CREATE TABLE, CREATE VIEW, MODIFY ON SCHEMA PROD_SCHEMA TO etl_role;
GRANT USAGE ON SCHEMA PROD_SCHEMA TO readonly_role;
最初はテーブルにGRANTすれば十分だと思っていたのですが、実際にはスキーマにも別途権限が必要でした。テーブル権限はデータの読み書きを制御し、スキーマ権限はテーブルやビューの作成といった構造の操作を制御します。この2つが別のレイヤーで管理されていることは、手を動かして初めて実感しました。
| Tips | ポイント |
|---|---|
| 1. 外部ステージのデータはS3を消すと見えなくなる | 外部ステージはS3への参照にすぎず、COPY INTOで内部テーブルに取り込んで初めてS3から独立する |
| 2. テーブル定義を変えないならTRUNCATE、変えるならDROP → CREATE | DROPすると権限(GRANT)も一緒に消滅するため、再作成後の権限付与を忘れない |
| 3. 本番スキーマは直接触らず、クローンで作業する | クローンは本番と同じDB内に作成し、本番スキーマには一切触れない |
| 4. 旧本番はすぐ消さず、リネームで入れ替えて戻せるようにする | 旧本番を残しておけば、問題発生時にリネームだけで即座に戻せる |
| 5. DROP → CREATE後はGRANTの再設定を忘れずに | テーブル権限とスキーマ権限は別レイヤーで管理されており、それぞれ設定が必要 |
Snowflakeのデータ投入は、SQLの実行だけ見ればシンプルです。ただ、「なぜこの手順なのか」「なぜ開発と本番で方法が違うのか」は、実際に手を動かして調べてみないとなかなかわからないものでした。今回まとめた内容は私が担当した案件での学びですが、Snowflakeでデータ投入に関わる方にとって、何かしらのヒントになれば幸いです。