SELECTSELECT

SELECT

Snowflakeクエリタグでモニタリングを強化

By Ian WhitestoneFeb 7, 20237 min read

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

Snowflakeのクエリタグを使えば、各クエリに任意のメタデータを付与できます。本記事では、Snowflakeのクエリコストとパフォーマンスの可視化・モニタリングを高めるクエリタグの活用法を解説します。

Snowflakeのクエリタグとは

クエリタグは、任意のSnowflake SQLステートメントに文字列を付与できるオプションのセッションレベルパラメータです。最大2,000文字まで、任意の文字を含めることができます。各クエリに付与されたクエリタグの値はSnowflakeのquery_historyビューの出力で確認でき、さまざまなユースケースで活用できます。

なお、クエリタグはオブジェクトタグとは別物です。どちらもSnowflakeアカウント内でより構造化されたモニタリングと可視性向上を目的とする点は共通していますが、オブジェクトタグはユーザー、ロール、テーブル、ビュー、関数など、永続的なアカウントオブジェクトに対して使用されます。

Snowflakeでクエリタグを使うメリット

ほとんどのSnowflakeユーザーにとって、仮想ウェアハウスで実行されるクエリのコンピュートコストが、Snowflake支出の大半を占めます。クエリ単位のコスト算出でウェアハウス内のコンピュート支出をユーザー別に按分することは可能ですが、本番環境では単一のユーザーアカウントがクエリとコストの大半を生み出すケースも多く、この粒度では十分とは言えません。

クエリタグを使えば、よりきめ細かいコスト配賦が可能になります。パイプライン内のあるデータモデルに紐づく1本のSQL、あるいは一連のSQLに、同じクエリタグを割り当てておけば、そのタグに紐づくすべてのクエリにコストを簡単に紐づけられます。代替策としてquery_textでグループ化する方法もありますが、これでは関連する複数のSQLステートメントをまとめられません。さらに、データモデルのSQLテキストが変更されれば、この方法は途端に破綻します。

クエリタグは、より粒度の高いクエリパフォーマンスのモニタリングにも役立ちます。先ほどの例で言えば、関連クエリすべての合計経過時間を集計することで、データモデルごとの総実行時間を追跡できます。あるいは、ユーザー向けアプリケーションのダッシュボードを駆動する一連のクエリがある場合も、クエリタグを使えば対象を絞ったパフォーマンスモニタリングが可能です。

最後に、クエリタグは他システムのメタデータとクエリを結びつける手段にもなります。たとえばクエリタグにdashboard_idを含めておけば、特定のダッシュボードに関するコストを集計したうえで、BIツールのメタデータからそのダッシュボードの利用頻度を確認するといった分析が可能になります。

Snowflakeでクエリタグを使う方法

デフォルトタグを設定する

クエリタグはセッションレベルのパラメータですが、アカウントレベルとユーザーレベルでデフォルト値を設定できます。例:

1alter user shauna set query_tag = '{"team": "engineering", "user": "shauna"}';

これで、このユーザーが発行するすべてのクエリにデフォルトタグが付与されるようになります。

セッションレベルでクエリタグを設定する

クエリタグの設定にはalter sessionコマンドを使います。このコマンドを実行すると、同じセッション内でその後に実行されるすべてのクエリに、指定した文字列がタグとして付与されます。

alter session set query_tag='users_model';

-- this query will be tagged with 'users_model'
create or replace table users_tmp as (
  select *
  from raw_users
  where
    not deleted
    and created_at > current_date - 1
);

-- this will also be tagged with 'users_model'
insert into users
from users_tmp
;

コードを展開

alter sessionはクエリ実行全体にレイテンシを上乗せするため、頻繁な呼び出しを避け、可能な限りユーザーデフォルトを利用することをおすすめします。

Pythonでクエリタグを設定する

Pythonからクエリを実行する場合、クエリタグを設定する方法は2つあります。

接続作成時に一度だけ設定する

Snowflake Python Connectorで接続オブジェクトを作成する際に、セッションパラメータをまとめて指定できます。以下の例では、このconオブジェクトから実行されるすべてのクエリにDATA_MODELLING_PIPELINEというタグが付きます。

con = snowflake.connector.connect(
    user='XXXX',
    password='XXXX',
    account='XXXX',
    session_parameters={
        'QUERY_TAG': 'DATA_MODELLING_PIPELINE',
    }
)

alter sessionで手動で切り替える

セッション内のすべてのクエリに同じタグを付けたくない場合は、実際のクエリを実行する直前にalter session set query_tag = 'XXX'を実行すればよいでしょう。

con.cursor().execute("alter session set query_tag='users_model'")

query = """
create or replace table users_tmp as (
  select *
  from raw_users
  where
    not deleted
    and created_at > current_date - 1
)
"""

con.cursor().execute(query) # tagged with 'users_model'
con.cursor().execute("insert into users from users_tmp") # tagged with 'users_model'

コードを展開

dbtでクエリタグを設定する

dbtを使用している場合、クエリタグの設定方法は3通りあります。

  1. profiles.ymlでまとめて指定する方法(参考)。dbtプロジェクトで実行されるすべてのクエリに、その値がタグとして付与されます。
  2. dbt_project.yml+query_tagを追加することで、特定のresource_path配下のすべてのモデル、あるいは個別のモデルに対してタグを設定できます。個別のモデルでは、モデルconfigで指定することも可能です(例: {{ config(query_tag = 'XXX') }})。profiles.ymlでデフォルトのクエリタグが設定されていても、これらより詳細なタグがあれば上書きされます。
  3. set_query_tagマクロを作成すれば、プロジェクト内のすべてのモデルに対して、クエリタグを自動的にモデル名へ設定できます。

各オプションの具体例についてはdbtのドキュメントを参照してください。なお、上流で特定の障害が発生した場合にクエリへ誤ったタグが付く可能性がある旨もドキュメントに記載されているので、あわせてご確認ください。

先日、dbtが発行するすべてのクエリに包括的なメタデータを付与する新しいdbtパッケージdbt-snowflake-query-tagsをリリースしましたので、ぜひお試しください。

JSON文字列を活用する

クエリタグを設定する際は、扱いやすさと一貫性の観点からJSONオブジェクトの利用をおすすめします。先ほどのデータモデルへのタグ付けを例にとると、JSONオブジェクトを使えば、モデルが実行された環境、バージョン、実行のトリガー(スケジュール実行か手動起動か)など、さらに多くの情報を付加できます。

import json

query_tag = {
  'app_name': 'pipeline',
  'model_name': 'users',
  'environment': 'prod',
  'version': 'v1.2',
  'trigger': 'schedule'
}
con.cursor().execute(f"alter session set query_tag='{json.dumps(query_tag)}'")

con.cursor().execute(model_sql)

Snowflakeのコスト・パフォーマンスモニタリングでクエリタグを活用する

クエリタグは、各query_idに対応するquery historyビューで確認できます。以下はquery_tag別の平均クエリパフォーマンスを取得するクエリ例です。

query_tagにJSONオブジェクトが格納されている場合、任意のキーでパースしてセグメント化できます。先ほどの例を使うと、次のようになります。

select
    query_tag,
    count(*) as num_executions,
    avg(total_elapsed_time/1000) as avg_total_elapsed_time_s
from snowflake.account_usage.query_history
where
    start_time > current_date - 7
group by 1

dbt-snowflake-monitoringパッケージを使う

SELECTのコスト・パフォーマンスモニタリング用dbtパッケージを導入していれば、パフォーマンスに加えてクエリタグ別のコスト分析も行えます。

select
    try_parse_json(query_tag)['model_name']::string as model_name,
    count(*) as num_executions,
    avg(total_elapsed_time/1000) as avg_total_elapsed_time_s
from snowflake.account_usage.query_history
where
    try_parse_json(query_tag)['app_name']::string = 'pipeline'
    and start_time > current_date - 7
group by 1

このテーブルはstart_timeでソートされたうえでマテリアライズされており、適切にクラスタリングされた状態になっているため、Snowflakeのaccount usageビューに直接クエリを投げるよりもはるかに高速に結果が返ります。

クエリタグの代わりにクエリコメントを使う

クエリにタグを付けるもうひとつの定番手法は、各クエリの末尾 1にコメントを追加する方法です。

select
    try_parse_json(query_tag)['model_name']::string as model_name,
    count(*) as num_executions,
    sum(query_cost) as total_cost,
    avg(total_elapsed_time_s) as avg_total_elapsed_time_s
from query_history_enriched
where
    try_parse_json(query_tag)['app_name']::string = 'pipeline'
    and start_time > current_date - 7
group by 1

この方法には、あらゆるデータウェアハウスで普遍的に使え、alter sessionを実行する必要がないため実装が手軽というメリットがあります。さらに、パフォーマンス面でも利点があります。alter sessionを実行するとSnowflakeへのラウンドトリップが発生する 2ためです。多くのユースケースでは問題になりませんが、レスポンスタイムに100〜200msでも上乗せされると困るアプリケーションでは許容できないこともあります。最後に、クエリテキストは最大1MBまで格納できるため、2,000文字に制限されるクエリタグよりも、クエリコメントの方がはるかに多くのメタデータを含められます。

下流での解析・分析が格段にしやすいため、可能な限りクエリタグの利用をおすすめします。クエリメタデータが2,000文字を超える可能性がある場合は、クエリコメントを使いましょう。

補足

  1. Snowflakeはクエリ先頭のコメントを自動的に取り除くため、コメントは必ずクエリの末尾に付加してください。

  2. alter sessionステートメント自体は非常に高速で、平均で約30ms程度です。

Ian Whitestone・Co-founder & CEO of SELECT

Ianは、SaaS型のSnowflakeコスト管理・最適化プラットフォームSELECTのCo-founder兼CEOです。SELECTを起ち上げる以前は、ShopifyとCapital Oneでフルスタックのデータサイエンス・エンジニアリングチームを6年間率いてきました。Shopifyでは、データウェアハウスの最適化とコスト可視化の推進をリードしました。