データエンジニアリングチームがいかにして大規模な「リフトアンドシフト(lift and shift)」を達成したか。
これを想像してください:
それは正午頃の月曜日です、我々のETLは夜通しいくつかの問題に対処した後にちょうど前日の負荷を完了しました。 私達は85%のディスクフル状態にあり、Redshift VACUUMは先週からすべてのソフト削除をクリーンアップするためにキックインしました – この間に重要なことは何も実行できません。 VACUUMにその仕事を完了させるために、私たちはすべてのプロセスを一時停止します。 週1でデータをクリーンアップするために予約されているクラスターに対して行っています…
一方、社内外の顧客は、データの鮮度と全体的な低さについて、懸念を示しています。 データエンジニアリングチームは、新しい機能を開発する代わりに、バックログを埋めて運用上の問題に対処することで、進めています。 別のクラスタ構成への移行には48時間かかることがありますが、速度の問題が解決するかどうかはわかりません。
これはInstacartのデータエンジニアリングチームにとっては普通の1日でした。 Instacart全体(機械学習、カタログ、データサイエンス、マーケティング、ファイナンスなど)が協力して当社の業務についてより多くを学び、より優れた製品を構築するための分析プラットフォームを構築し、維持します。 私たちは数年前からクラウドデータストレージウェアハウスとしてRedshiftを使っていましたが、当時はその目的を達成していました…しかし、成長とともに安定性とスケーリングの問題に直面し始めたため、プラットフォームをSnowflakeに移行することにしました。 私たちは、あなた自身で始められるようにするために私達は主要なマイグレーション観察と学習のいくつかの要点をまとめました。
戦略
Redshiftで作成したスキーマとテーブルを、データ構造を大きく変更することなくコピーすることにしました。(構造を変更することなく)マイグレーション(移行)のみに焦点を当てることが、依存関係に満ちた、大規模で複雑なシステムのマイグレーションを成功させるための鍵となりました。 このアプローチによって、古いシステムと新しいシステムの間でデータを簡単に比較し、それらを並行して実行して、公式な切り替え前にその安定さを確認することもできました。
ストレージとコンピューティングの分離
Snowflakeのアーキテクチャがもたらす大きな利点の1つは、ストレージとコンピューティングの分離です。 主要なアプリケーション用に専用のウエアハウス(倉庫)を作成し、そのウエアハウス(倉庫)に必ず名前を付けて、組織内の誰が使用しているのかを簡単に識別できるようにしました。 チームがウェアハウスを使用するための許可を与えた後は、各アプリケーションおよび事業単位に関連するコストを特定するのは簡単でした。 これはとても役に立ち、そしてこれはRedshiftではできなかったことです。
コード変更
Redshiftが使っていたgitリポジトリを fork(フォーク:プロセスのコピーを生成するもの)し、新しいブランチをSnowflakeで動作するように修正しました。最終的な移行中に多くの問題が起こさないため、数日ごとにマスターブランチを新しいブランチにマージ (併合:merge)しました。
データとDDLの移行
最初のステップとして、Redshiftですべてのスキーマを取り、Snowflakeで同じスキーマを作成しました。 特定のスキーマまたはテーブルについてRedshift information Schemaをスキャンする自動スクリプトを使用しました。次に、vw_admin_generate_table_ddl(Redshiftビュー)を使ってRedshiftからDDL(データ定義言語)を抽出し、DDLをSnowflakeに拡張し、RedshiftからS3にデータをアンロードし、新しいテーブルでSnowflakeにロードし直しました。 SnowflakeデータがRedshiftと整合するように、移行中にこのプロセスを数回実行しました。
以下は、RedshiftからDDLオブジェクトを取得するために使用したコードサンプルの一部です。
def run_data_migration(self, schema_name, table_name): self.logger.info("querying redshift metadata") pg_conn = pg.connect(cfg.REDSHIFT_DB_URL) print "pg_conn:", pg_conn pg_cursor = pg_conn.cursor() if table_name != "none": sql = """ select table_schema,table_name from information_schema.tables where table_schema= '{0}' and table_type='BASE TABLE' and table_name like '{1}%' """.format( schema_name, table_name ) else: sql = """ select table_schema,table_name from information_schema.tables where table_schema= '{0}' and table_type='BASE TABLE' """.format( schema_name ) pg_cursor.execute(sql)
次に、Redshift DDLを生成しました。
def get_table_ddl(self, table_name, schema_name): self.logger.info("querying redshift metadata") pg_conn = pg.connect(cfg.REDSHIFT_DB_URL) pg_cursor = pg_conn.cursor() sql = """ select ddl from vw_admin_generate_table_ddl where tablename='{0}' and schemaname='{1}' """.format( table_name, schema_name ) pg_cursor.execute(sql) ddl = pg_cursor.fetchall() pg_cursor.close() print "ddl:", ddl return ddl
その後、DDLを取り、それをSnowflakeと互換性のあるものにしました。 SnowflakeのサポートチームがDDLをSnowflakeに移行するための this scriptを提供してくれました。 次に、RedshiftデータをS3にアンロードし、それをS3からSnowflakeにロードしました。
SQLの挑戦
RedshiftとSnowflakeは、少し異なるSQL syntax (構文)の変種を使用します。 コードを移行する際に考える必要がある可能性のある主な違いは次のとおりです。
Redshift | Snowflake |
select GETDATE() | select CURRENT_TIMESTAMP() |
select TRUNC(CURRENT_TIMESTAMP()) | select DATE_TRUNC(DAY,CURRENT_TIMESTAMP()) |
select SYSDATE | select TO_TIMESTAMP_NTZ(CONVERT_TIMEZONE(‘UTC’,CURRENT_TIMESTAMP())) |
select DATE(CURRENT_TIMESTAMP()) | select TO_DATE(CURRENT_TIMESTAMP()) |
date_time_utc > GETDATE() – 7 | date_time_utc > dateadd(‘DAY’, -7, TO_TIMESTAMP_NTZ(LOCALTIMESTAMP)) |
select coalesce(‘a’) | select coalesce(‘a’, ‘b’) –you will get error message if you specify only one argument “SQL compilation error: error line 1 at position 7 not enough arguments for function [COALESCE(‘a’)], expected 2, got 1” |
DISTSTYLE | not used |
DISTKEY | not used |
SORTKEY | not used |
SORTKEY(col) | not used |
ENCODE | not used |
interleaved | not used |
bpchar | char |
character varying | varchar |
‘now’::character varying | current_timestamp |
identity(start, 0, ([0-9],[0-9])::text) | identity(start, 1) |
DMLの変更:Redshiftはテーブルを削除するためのより緩やかなsyntax
(構文)を持っています。 一方、Snowflakeは正しいsyntax (構文)でより厳密になりますが、Redshiftは「from」キーワードなしで削除table_nameだけを受け入れることができます。 (個人的には、私はSnowflakeアプローチを好みますが、Redshiftの構文を使用しているケースがいくつあることに驚いています。)
Redshift | Snowflake |
DELETE customers | DELETE from customers |
半構造化データ:SnowflakeとRedshiftはどちらも半構造化データの解析機能を提供します。Snowflakeのsyntax (構文)が異なるため、RedshiftでJSONを解析している場合は、SQLを以下の形式に変更する必要があります。
Redshift | Snowflake |
json_extract_path_text (properties,’name’) | parse_json(properties):name |
select json_extract_array_element_text(‘[111,112,113]’, 2); | select parse_json(‘[111,112,113]’)[2]; |
Snowflakeは、JSON、Avro、ORC、Parquet、またはXMLのために価値があるバリアントデータ型を提供し、Snowflakeはこれらの型をドキュメントの効率的な圧縮円柱バイナリ表現で保存します。
データディクショナリ
スノーフレーク情報スキーマは大文字として保存されます。 それらの情報スキーマを使用していない場合は(おそらくそうではありませんが)問題ありませんが、情報スキーマを参照している場合は、クエリで小文字または大文字に変更する必要があります。
注:Tableauから参照すると、これは大きな問題になります。 Tableauメタデータでは大文字と小文字が区別され、大文字と小文字が同じフィールドは2つの異なるフィールドとして扱われ、その結果、レポートがブレイクします。 Interworksのコンサルティングチームの助けを借りて、すべてのTableauデータソースを手動で移行しました。
手動のTableau移行の利点:いくつかの主要なクリーンアップを実行しました。 未使用のデータソースとダッシュボードの大部分を発見し、3,000以上のワークブックのうち180だけを移行しました。
UPPER機能を使ってSELECTを実行しているときにも問題が発生しました。
select * from information_schema.tables where table_name= upper('table_name'); # Instead make sure you send the string in UPPER case. Dont use the upper function. select * from information_schema.tables where table_name= 'TABLE_NAME';
情報スキーマクエリの選択性が十分でない場合、Snowflakeは次のエラーを返します。
情報スキーマクエリが多すぎるデータを返しました。 より選択的な述語 (selective predicates)でクエリを繰り返してください
Snowflake情報スキーマ(別名「Data Dictionaryデータディクショナリ」)は、システム定義のビューとテーブル関数のセットで構成されています…docs.snowflake.net
スノーフレークのデフォルトのタイムスタンプ
SnowflakeのデフォルトのアカウントのタイムゾーンはAmerica / Los_Angelesに設定されています。つまり、データベースのクロックタイムはPSTになります。 PSTで長い間使用した後、UTCに変更しましたが、何が壊れるのかわからないので、この変更は少し怖いものでした。 (私のアドバイス:実装を始める前にそれをチェックし、あなたのニーズに基づいて時間を設定してください。私はUTCを強くお勧めします。)
show parameters like '%TIMEZONE%' in account; #change your account setup in case you want a different timezone alter account SET TIMEZONE = 'UTC';
パラメータ – スノーフレークドキュメンテーション
Snowflakeはあなたのアカウント、個々のユーザーセッション、そしてオブジェクトの振る舞いを制御することを可能にするパラメータを提供します…docs.snowflake.net
スノーフレークコピーコマンド
Redshiftの方が日付を入れて寛容であることがわかりました。 データの移行中に、Redshiftが将来の日付をタイムスタンプ列に格納できた多くのケースが見つかりましたが、Snowflakeはその日付を拒否しました。 「11457-11-11」のような現実的ではない将来の日付を拒否することには賛成しますが、Snowflakeのエラーメッセージは私たちが望んでいたほど詳細ではなかったため、日付を見つけて整理するのは困難でした。
今回の例では、下記の例のように、copyコマンドでNULL_IFを使用することにしました。 Snowflakeでは、「if date>「2030-01-01」の代わりにnullを使用する」のような条件は提供されていません。 nullとマークするには、正確な日付を指定しなければなりません。
copy into {0}{1} from {2} truncatecolumns = true file_format = ( field_optionally_enclosed_by='"' escape_unenclosed_field='NONE' type = csv TRIM_SPACE = TRUE field_delimiter = '|' null_if = ('', '11457–11–09') );
データ比較ツール
私たちのETLはRDS DBから何千ものテーブルを消費し、何十ものファクトテーブル(flat tables)を生成します。 移行時の総ストレージは1.2PB圧縮されていました。 毎日何百ものETLを実行しており、ピーク時には50以上のETLを同時に実行できます。 また、レポート作成ツールで使用されている約800のビューも移行しました。
RedshiftとSnowflakeの結果を手動で比較することは不可能でした。 以下の結果が得られる比較ツールを作成することにしました。
各テーブルの数を選択してRedshiftと結果を比較します。
各文字列列を区別してカウントを選択し、Redshiftと比較します。
各テーブルの構造比較
テーブルが存在するかどうかの簡単な確認
各数値列の合計を選択してRedshiftと比較します。
結果
2つのシステム間でデータを比べるために、両方のシステムを数週間並行して実行しました。 その後、移行が成功したと確信した後は、Snowflakeを主なデータソースとして使用するようにアプリケーションをゆっくり切り替えました。 Redshiftクラスターは移行後1か月間有効のままにし、Redshift stl_queryでアクティビティがゼロであることを確認した後にシャットダウンしました。
この大規模な移行プロジェクトを上層部の管理者の支援なしで始めること自体は失敗です。 Snowflakeと互換性を持たせるために、各グループがコードの移行に関与する可能性があるため、プロジェクトを始める前に、リソースの割り当てについて管理者からのコミットメントを得てください。
この記事では、SnowflakeとRedshiftの主な違いや相違点をすべて見つけることはできませんが、移行を検討している場合は、上記の項目によって多くの時間が節約されることを願います。
移行が完了しても、作業は終わらない。 Instacartのデータエンジニアリングチームでこのようなプロジェクトに取り組み、あなたの調査結果を共有したいですか。 Instacartの求人情報を是非チェックしてみてください。
原文タイトル:
Migrating from Redshift to Snowflake
How our Data Engineering team completed the massive “lift and shift”
原文作者:Tamir Rozenberg
原文リンク:https://tech.instacart.com/migration-from-redshift-to-snowflake-the-path-for-success-4caaac5e3728