SELECTSELECT

SELECT

dbt Threads:Snowflakeでdbtプロジェクトを最速化する

By Niall Woodward & Ian WhitestoneJan 29, 20246 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

dbtプロジェクトの実行時間を見直すとき、多くのユーザーが最初に着目する設定がthreadsです。なぜこのパラメータが実行時間に大きく影響するのか、そして適切な値はいくつなのか。本記事で簡潔に解説します。

結論を先にお伝えすると、threadsの値は「キューイングが発生するのに十分な高さ」に設定するのがおすすめです。一般的なdbtプロジェクトであれば16で十分で、実行中にSnowflakeのクエリタブを確認すれば妥当性を検証できます。

dbtのthreadsパラメータは何を制御するのか

Pythonにおけるマルチスレッドの基礎

dbtの実体はPythonプロセスで、SQL(「モデル」)ファイルを読み込み、利用中のデータウェアハウス(本記事の文脈ではSnowflake)に対して実行します。

Pythonはデフォルトではシングルスレッドで動作し、一度に1つの処理しか行えません。一方dbtでは、データ処理の「重たい部分」はすべてSnowflake側で実行されます。そのためdbtのPythonプロセスはほぼI/Oバウンドで、送信したSQLの完了を待っている時間が大半を占めます。こうしたI/Oバウンドなアプリケーションでは、Python開発者はマルチスレッドを使って処理を高速化できます。マルチスレッドでは複数のスレッドを起動し、それぞれが独立した処理を担うことで、逐次ではなく並列に実行できるようになります。

dbtのthreadsとは

dbtはこの仕組みをthreadsというパラメータとして提供しています。このパラメータは、プロジェクトに必要なSQL操作を並列実行するためにdbtが起動するスレッド数を制御します。

もっと端的に言えば、threadsパラメータは、ある時点でSnowflakeに同時送信されるモデルの最大数を制御するということです。

Snowflake仮想ウェアハウスの設計と同時実行性への影響

Snowflake仮想ウェアハウスのコンピュートアーキテクチャを簡単におさらいすると、Snowflakeウェアハウスの各ノードにはクエリ処理用の8コア/スレッドが搭載されています。X-SMALLは1ノード=8コア/スレッド、Smallはその倍(2ノード、16コア)、Mediumはさらにその倍(4ノード、32コア)、というように増えていきます。

Snowflake virtual warehouses compute layer

X-SMALLウェアハウスであれば、ある時点で最大8クエリを同時に処理できます。ウェアハウスのサイズが大きくなるほど、同時実行可能なクエリ数(同時実行性)も増加します。

つまり、dbtのthreadsパラメータがSnowflakeへ並列送信するクエリ数を決めるのに対し、実際に並列処理されるクエリ数はSnowflake仮想ウェアハウス側が決めるということです。ウェアハウスがそれ以上のクエリを処理しきれない場合、超過分はキューに入れられ、コンピュート容量に空きが出次第、自動的に処理されます。

dbtのthreadsは実行時間にどう影響するのか

dbtプロジェクトはDAG(有向非巡回グラフ)であり、規模が大きく構造も複雑になりがちです。1つのモデルが複数のモデルから依存され、またその逆もあります。

threadsの影響を理解するには、両極端なケースを思い浮かべるとわかりやすいでしょう。

まず、スレッドを1つだけに設定した場合を考えてみます。500個のモデルを持つプロジェクトの実行時間は、各モデルの実行時間の単純な合計になります。当然ながら非常に時間がかかり、モデルを実行する仮想ウェアハウスも、その大半の時間で能力を持て余すことになります(ウェアハウスの使用率についてはこちら)。結果としてプロジェクトは極端に遅くなり、それに見合わない仮想ウェアハウスコストが発生します。

逆に、DAG内のモデル数と依存関係から見て同時実行可能なモデル数をはるかに上回るthreadsを指定した場合はどうでしょうか。この設定では、dbtはその時点で実行可能なクエリをすべて投入しようとします。これにより、dbtプロジェクト全体の実行時間のボトルネックは、個々のモデルの実行時間(単一ノードの依存関係を除く)から、仮想ウェアハウスの計算スループットへとシフトし、リソース利用効率が最大化されます。結果として次の2つが実現します。

  1. キューイングの発生(=ウェアハウスのコンピュートを使い切っている状態)
  2. エンドツーエンドのプロジェクト実行時間の最小化

このシナリオではリソース競合が起きるため、個々のモデルの実行時間は増えるかもしれません。しかし、ここでの目的はあくまでエンドツーエンドの実行時間です。バッチ処理においてキューイングが発生している状態は、コストパフォーマンスが最大化されている証拠であり、むしろ望ましい状態と言えます。

Snowflakeではdbtのthreadsをいくつに設定すべきか

ここまでの議論を踏まえると、dbtのthreadsパラメータは、ウェアハウスにキューイングが発生する程度に高く設定するのがおすすめです。ほとんどのdbtプロジェクトでは16で十分ですが、dbt用の仮想ウェアハウスがしっかり飽和しているかどうかを、クエリのキューイングの有無で確認するとよいでしょう。

以下は、threadsを16以上に設定した際のdbtモデルタイミングチャートの例です。Snowflakeが多数のdbtリソースを並列処理できていることがわかります。

Snowflake dbt model timing chart

キューイングの確認方法

クエリのキューイングを確認する方法はいくつかあります。手軽な方法の一つは、Snowsight UIのウェアハウスページにあるWarehouse Activityグラフを使うことです。ウェアハウスでキューイングがどの程度発生しているかをひと目で把握できます。下の画像は当社のdbtプロジェクトでのスクリーンショットで、dbt用ウェアハウスで継続的にキューイングが発生していることがわかります。これはウェアハウスを最大限活用できている証であり、まさに狙いどおりの状態です。プロジェクトの実行時間がSLAの範囲内に収まっている限り、キューイングを心配する必要はありません

Snowflake warehouse load history

dbtの実行時間に影響するその他の要素

threadsを適切に設定してボトルネックをSnowflake側の計算スループットに寄せた後は、さらにdbtの実行時間を短縮するためのテクニックがいくつかあります。詳しくは以下のリンクをご覧ください。

  1. モデルのインクリメンタル化
  2. Snowflakeクエリプロファイルでボトルネックを把握し、クエリを最適化する
  3. ウェアハウスサイズとマルチクラスタの設定

Niall Woodward・SELECT共同創業者兼CTO

NiallはSaaS型Snowflakeコスト管理・最適化プラットフォーム「SELECT」の共同創業者兼CTOです。SELECT創業以前は、Brooklyn Data Companyや複数のスタートアップでデータエンジニアとして活躍。オープンソースにも積極的に関わっており、SQLFluffのメンテナーであり、3つのdbtパッケージ(dbt_artifactsdbt_snowflake_monitoringdbt_query_tags)の作者でもあります。

Ian Whitestone・SELECT共同創業者兼CEO

IanはSaaS型Snowflakeコスト管理・最適化プラットフォーム「SELECT」の共同創業者兼CEOです。SELECT創業以前は、ShopifyとCapital Oneで6年間にわたりフルスタックのデータサイエンス&エンジニアリングチームを率いてきました。Shopifyでは、データウェアハウスの最適化とコスト可視化の取り組みをリードしました。