注:本記事は(2021年8月3日)に公開された(How Cisco Optimized Performance on Snowflake to Reduce Costs 15%: Part 2)を翻訳して公開したものです。

CiscoがすべてのワークロードにわたってSnowflake上のパフォーマンスを最適化した方法から学ぶ、推奨事項とベストプラクティスの概要。

このシリーズの前編では、Snowflakeの主要なアーキテクチャ構成要素と、パフォーマンスを監視、最適化するためのサービスと機能について見ていきました。後編では、Snowflakeでのパフォーマンスを改善し、コストを低減する上で役立つベストプラクティスについて説明します。

ストレージコストの最適化

Snowflake内のすべてのPermanentテーブルのデータ保持期間を確認する

タイムトラベルは、すぐに使える優れた機能ですが、付随してコストも発生します。履歴を保持する日数が多ければそれだけ、ストレージコストも多くかかります。よって、データ保持期間の最適値を決定する前に、データセットのサイズ、データセットに施す変更の量、およびタイムトラベルに関するビジネス要件を考慮してください。データベースのタイプごとに異なるタイムトラベル期間を設定することが理想です。

テーブルをドロップするときはタイムトラベルをオフにする

テーブルをドロップすると、データはタイムトラベルで設定された期間(DATA_RETENTION_TIME_IN_DAYSパラメーター)に基づいて保持されます。ドロップするテーブルのタイムトラベルの保持が不要な場合、ALTER TABLEコマンドを使用して、DATA_RETENTION_TIME_IN_DAYS=0と設定してから、テーブルのドロップに進むとよいでしょう。これにより、タイムトラベルが直ちにパージされ、付随するストレージコストを節約できます。

Permanentテーブルではなく、TransientテーブルかTemporaryテーブルを使用することを検討する

テーブルに復元性を必要としない場合、Transientテーブルを作成すると、付随するコストを削減できます。Snowflakeはセッション中、Transientテーブルを維持するため、その間のストレージ料は発生しますが、Permanentではないので、Fail-safeコストは発生しません。あるいは、セッションの最後に自動的にドロップされるTemporaryテーブルを作成することもできます(Temporaryテーブルの場合、アクティブセッションで使用されている間のみストレージ料が発生します)。

Snowflakeゼロコピークローン機能を利用する

すぐに使えるSnowflakeゼロコピークローン機能は、任意の時点で何回でも複製できますが、ストレージ料金はゼロか、もしくは関連する基本テーブルに施された変更に応じた最小限の料金しか発生しません。

コンピュートコストの最適化

ウェアハウスのサイズを確認する

時おり、自分がワークロードに対して適切なサイズのウェアハウスを使用しているかどうかを検証します。かつて、当社ではソースシステムからSnowflakeへのデータ取り込みに1つのサイズのウェアハウスを使用していました。取り込みにはさまざまなタイプのクエリ(PUT、COPY、INSERT、MERGE、SELECT、DELETE)と、さまざまなサイズのデータセット(Lookupテーブルの場合は1,000行未満、Transactionテーブルの場合は何10億もの行など)が関与するため、フリーサイズのアプローチでは、すべてのデータに対して最適とは言えませんでした。私たちの解決策は、基盤となるデータサイズに応じて各テーブルのウェアハウスのサイズを動的に変更するというものでした。その結果、年間37万5,000ドルもの節約を実現しました。正しいサイズを事前に決定することは必ずしも簡単なことではないため、何回か反復して試行錯誤しながら適正なサイズを見つけます。最適なサイズを見極めるための反復候補選びの詳細については、前半のクエリ履歴を参照してください。

効率的なクエリテクニックを使用する

このセクションでは、クエリをより効率的にし、パフォーマンスを上げ、コストを低減するためのいくつかの要素について説明します。

テーブル結合の効果:

  • 自己結合(self-join)は、特にテーブルが巨大な場合、コスト高になります。self-joinのあるクエリを確認して、そのような状況を避ける方法を探してください。
  • 多対多(many-to-many)結合を避けましょう。キーカラム上で2つのテーブルを結合させる場合、1対多(one-to-many)または多対1(many-to-one)のいずれかを用いるようにします。多対多はデカルト結合になり、結果内の大量の行を次のステップに渡さなければならなくなります。

集計:

  • 集計演算(GROUP BY、ORDER BY、DISTINCTなど)は、特に結果データセットが巨大であるとき、高コストとなります。これらの演算を完全に避けることはできませんが、これらを効率的に使用することを検討してください。たとえば、基となるテーブルに重複がない場合は、DISTINCTまたはGROUP BYを使用する必要はありません。このような演算を使用すると、クエリパフォーマンスが損なわれ、コストが上昇します。

フィルタ効率:

  • フィルタの使用方法にばらつきがあると、前テーブルのスキャンをしなければならず、クラスタリングの目的が損なわれます。そのような演算の例としては、<>、NOT IN、NOT LIKEが挙げられます。そのような使用例に対して考え得る解決策は、クラスタリングキーを定義する一方で関数自体を使用することです。たとえば、カラムemployee_nameにおいて、フィルタ中にUPPER関数を常にオンにする場合、単なるemployee_nameではなく、UPPER(employee_name)を使用してテーブルをクラスタリングします。

JSON解析

  • JSONデータをPARSE_JSONを使用してVARIANTに抽出することは、さらなる経費がかかると共に、コンピュート費用も高額になります。JSONデータを直接的にVARIANTに抽出することがアウトオブボックスでサポートされています。
  • LATERAL FLATTEN関数を使用すると、クエリ内のJSONエレメントを横断するパフォーマンスが最適化されます。

共通テーブル式(CTE)

  • CTEを使用すると、TemporaryテーブルやTransientテーブルを作成するよりも、中間結果の格納が最もコスト効率的になります。
  • CTEは完全にメモリベースの演算であるため、データをフィジカライズする場合と比較して、I/Oを大きく削減できます。

メモリ不足エラー

  • 非効率的なテーブル結合の結果、行数が何10億にもなった場合、Snowflakeのクエリ実行がメモリ不足エラーにより失敗する場合があります。そのようなインシデントが発生した場合、基のテーブル、結合条件、使用されたフィルタ、クラスタリングキーの効率性、使用されたウェアハウスのサイズを確認してください。

UNION演算

  • UNION演算は常に、OR演算よりもパフォーマンスが優れています。たとえば、filterorg_id=1 or bu_id=1は、間にUNION演算子を使用することで、2つのクエリとして書き直すことができます。
  • UNION ALLは常に、UNIONよりもパフォーマンスが優れています。UNIONは、結果セット内で重複をフィルタしたい場合のみ使用します。そうでない場合、デフォルトでは「union」ではなく「union all」を使用します。

INSERT演算

  • 行ごとの挿入はコストがかかります。非効率的なだけではなく、ウェアハウスが最小の負荷で常に稼働している状態になるため、クレジットが不必要に消費されます。代わりに、COPY INTO演算子を使用して一括挿入をするか、COPY INTOが使用できない場合は複数行ごとの挿入を用います。

カスタムモニタリングダッシュボードの作成

Snowflakeはクレジットの利用状況を確認するビューを複数提供していますが、カスタムダッシュボードを構築すると利用しやすいのでお勧めです。仮想ウェアハウスクレジットの場合、WAREHOUSE_METERING_HISTORYテーブル関数でクエリすると、時間ごとの利用状況が分かります。しかしながら、用途がさらに広い場合や詳細な分析をしたい場合は、カスタムダッシュボードの作成を検討してください。

カスタムダッシュボードは、ウェアハウスと日別トレンドを示すデータの上に構築できます。これにより、エンドユーザーは自身の使用パターンを特定し、使用量を適宜微調整できます。個別のダッシュボードを構築して、ストレージコスト、クラスタリングコスト、マテリアライズドビューの更新コスト、全体的なSnowflakeコストなどのパターンを確認することもできます。ワークロードを論理的にグループ分けして、付随するコストを分析するには、クエリタグを使用します。

Snowflakeの使用を最適化する方法について、前後編をお読みいただきありがとうございました。

参考資料

Manickaraja Kumarappanは、このブログの執筆時点、Ciscoデータアンドアナリティクス部門のアーキテクトでした。