🦵

Snowflake + dbt におけるELT処理の私的ベストプラクティス

2022/12/20に公開

これは何?

この記事は Snowflake Advent Calendar 2022 の19日目および dbt Advent Calendar 2022
の22日目の記事です。

(本当は別々の日でそれぞれ記事で上げたかったのですが、同じような内容になるので統合させていただきました。恐縮です。)

私はちょうど一年前くらいからdbtをSnowflake上で利用しています。
その中で色々と我流のTipsが貯まりはじめたので、私なりの利活用やベストプラクティスについてざっくりとまとめつつ、今後のSnowflake + dbtのデータエンジニアリングの展望についても軽く触れていきたいと思います。

Snowflakeとdbtの使い分け

2022/12/19時点での結論構成、こんな使い分けの構成がよいと思っています!
理由は後述します。

  • 可能な限り上流に近いデータソースからdbtに登録してしまってTransformはできるだけdbtで実装してしうのが吉
    • Snowpipeでロードをして構造化したようなDWH層のテーブルをソーステーブルとする
    • troccoやFivetran等のマネージドなELを実施してくれるサービスでロードされたテーブルをソーステーブルとする
    • s3等のクラウドストレージに配置されたデータは外部テーブルをソーステーブルにしてしまう
      • 外部テーブル登録するには以下のような便利なパッケージもあります

https://github.com/dbt-labs/dbt-external-tables

  • リアルタイム性が必要だったり複雑なUDFやSnowparkでMLワークロードを動かすTransformが必要な場合はSnowflakeにデータパイプラインを実装する

図にすると、このくらいの気持ちでSnowflakeとdbtを使い分けるのがいいと考えています。

dbtはELT処理のT(Transform)を司るツールとして、ここ1〜2年でも様々な企業でかなり利用されてきていると思います。
一方でSnowflakeの機能においてもめちゃくちゃ便利で皆さんが大好きな機能が沢山あります。

例えば↓のような機能です。

  • Snowpipe
    • ファイルをクラウドストレージ等に配置したタイミングでファイルをロードしてくれるマネージドサービス
  • Stream + Task のCDCデータパイプライン
    • ソーステーブルへの追加・変更・削除を追跡したテーブルストリームを利用してタスク実行をすることで連続的なデータパイプラインの実装が可能になる

https://docs.snowflake.com/ja/user-guide/data-load-snowpipe-intro.html

https://docs.snowflake.com/ja/user-guide/streams.html

開発のしやすさや中間層のモデル定義や影響をうまく吸収できる点においてはdbtは本当に優秀であり、SQLさえ書けてしまえばデータエンジニアリングができるのは本当に魅力的で強大なパワーを発揮してくれます。
私もできれば全部dbtでやりきってしまいたいと思うくらいには本当に大好きです。

一方で、dbtを使っていない方や業務要件に応じてSnowflakeの機能やDWHの機能だけでデータパイプラインを実装することは、選択肢の一つとしては引き続き大きくあると思います。

実際に、私の会社でもSnowflakeのこれらの機能のみで構築されたデータパイプラインはスループットも高く、かつコストパフォーマンスも優れた状態で運用されており、とても高い価値を出せるものになっています。

Lambda View

そうはいえ、dbt側でも現時点で不得意なこともあります。
例えばリアルタイムなデータでモデリングをしたい場合等はソーステーブルの更新頻度や性能・コストを天秤にかけるような意思決定が必要になってきます。

SnowflakeであればリアルタイムとなればSnowpipeを使ってロードしてしまうのも手ではありますが、ロードするデータのサイズやファイル数によっては思わぬ形でコストがかかってくる可能性もあります。(https://docs.snowflake.com/ja/user-guide/data-load-considerations-prepare.html#label-snowpipe-file-size)

  • リアルタイムなデータをうまくdbtでモデリングしたい
  • (Snowflakeの場合に)うまくSnowpipeの要件にあうようなデータロードができなさそう

こういった場合のHackが↓の記事で紹介されています。
これがいわゆるLambda Viewです!!!

https://discourse.getdbt.com/t/how-to-create-near-real-time-models-with-just-dbt-sql/1457

実テーブルで構築された過去データと最新データを相手にしたデータをうまく分けることで、すでにモデリングされた大規模な過去データに対しての性能面のメリットを活かしつつ、最新データに対してのデータ量を最小限に抑えながら同様のモデリングを行った上でリアルタイム性も担保することが可能になります。

上記の記事のまんまですが、クエリにするとこんな感じですかね。

with current_view as (
  select * from {{ref('current_view')}}
  where created_at >= (select max(created_at) from {{this}})
),
historical_table as (
  select * from {{ref('hitorical_table'}}
  where created_at < (select max(created_at) from {{this}})
),
final as (
  select * from current_view
  union all
  select * from historical_table
)
select * from final

ただしこれらのLambda Viewは、historical tableとcurrent Viewに対して同一のモデリングを実装しなければいけないことや、これらのモデルが上流にあればあるほど、複雑なDAGが必要となること等の問題もあるので、一長一短があることを理解しながら使っていくことが求められます。

Dynamic Table

ようやくきました。
今回はリアルタイム性を担保したdbtのモデリングはLambda Viewがあるよ!という紹介をしたかったのですが、もう一つ紹介したかったのです。

最近徐々に話題になってきたSnowflakeの新機能、Dynamic Tableです。(2022-12-19時点ではPrivate Preview)
このDynamic Tableこそ私が、最初にできるだけdbtに寄せてしまってよい!という話をした理由になります。

Dynamic Tableの概要については Snowflake Advent Calendar 2022 の17日目にutaさんが執筆してくださった 【Snowflake】初めてStreamを使ったデータパイプライン構築をした話や、Dynamic Tables: Delivering Declarative Streaming Data Pipelines with Snowflake に詳細がありますのでそちらをご覧ください。

https://www.snowflake.com/blog/dynamic-tables-delivering-declarative-streaming-data-pipelines/

ここでは簡単にざっくりというと、ctas的なノリで宣言的なニアリアルタイムなデータパイプラインを記述できるよ!(ちょっとしたオプションも設定してね!)という感じでご容赦いただければと思います。

これまで、SnowflakeではStreamを利用してTaskをバッチ実行することでデータパイプラインを実装をしていたのですが、それらの処理をすべて宣言的に記述をするだけで実装ができてしまうということになります。

この機能、dbtも対応するよ!ということを2022年6月に開催されたSnowflake Summitでもお話していました。

以下にSummitのSessionがあるので、ご興味のある方はどうぞ(46分からのBuilding Streaming Data Pipelinesが対象のセッションです)
https://www.snowflake.com/summit-sessions/?lang=ja

そこで紹介されていたのですが、先程のLambda Viewも↓のような記述に変更するだけで、dbtでもニアリアルタイムのデータパイプラインが実現可能になる未来がもうすぐ来ます。

{{
  config(
    materialized = 'materialized_table'
  )
}}
select * from {{ref('realtime_source_table')}}

…これ、すごくないですか?

これまでdbtで管理してきたモデルも materialized = 'materialized_table' と変更するだけでSnowflakeが透過的にデータリフレッシュを自動的に実施してくれるようになります。(バッチ実行とかも必要なくなるのかな?)

ニアリアルタイムなデータモデリングが可能になるなけでなく、コードもDAGもシンプルになるのでよさそうですよね。

まとめ

いかがでしたか?

dbt目線では、Python modelやSemantic Layerなどの話題もありますが、dbtの未来の別の側面が徐々にに見えてきたのではないでしょうか?

Snowflake目線では、これまた新しいデータエンジニアリングの味方が増えたので積極的に使い倒していきたいですね!

現場からは以上です!ありがとうございました!

Snowflake Data Heroes

Discussion