Snowflake で ODBC 接続を使ったことがある方なら、クエリが期待どおりの速度で動かないという経験があるのではないでしょうか。多くの場合、ボトルネックは Snowflake 本体ではなく、接続レイヤーにあります。ドライバーの細かい設定や構成の漏れがパフォーマンスを落としているケースが多く、原因さえ特定できれば修正は意外とシンプルです。それでも、レイテンシの増加、コストの上昇、安定しないパフォーマンスに悩まされているデータ利用者は少なくありません。実際の処理速度は、ウェアハウスのサイズだけでなく、データの接続方法、クエリの実行方法、フェッチの仕組みにも大きく左右されるためです。
こうした問題の多くは、Snowflake アカウントやアプリケーションのコードに手を加えなくても、ドライバーレベルで解決できます。CData ODBC Driver for Snowflake には、接続管理、クエリ実行、データフェッチを細かく制御するための設定が用意されています。本記事では、クエリパフォーマンスの改善とコスト削減につながる 5 つのテクニックを、具体的な設定手順とあわせて紹介していきます。
1. 接続プーリングを最適化してセッションを永続化する
アプリケーションが Snowflake に接続するたびに、認証、セッションのセットアップ、ハンドシェイクといった処理が発生します。複数のユーザーがこれを繰り返すと、その都度発生する遅延がパフォーマンスとコストの両方に直接響いてきます。接続プーリングは、開いたままのデータベースセッションをキャッシュとして保持し、複数のクライアントスレッドで再利用することで、この問題を解消します。毎回新しい接続を開く代わりに、アプリケーションはプールから 1 つ取り出して使い、終わったら返却するだけです。これにより、セッション関連のレイテンシを大きく削減できます。
ドライバーレベルでのプーリング
最初のステップは、プーリングの有効化です。CData ODBC Driver では、接続プーリングを DSN 上で直接設定できます。つまり、アプリケーション側に変更を加えなくても、すべての接続に自動的に適用されます。
機能 | CData ODBC Driver | Snowflake 純正 ODBC ドライバー |
組み込みプーリング | DSN レベルで設定可能 | OS レベルの ODBC ドライバーマネージャーに依存 |
プール有効化の設定 | UsePool=1 | ドライバー単体では不可 |
アイドル接続のタイムアウト | Timeout=60 | ドライバーレベルでは設定不可 |
キープアライブの制御 | DSN で CLIENT_SESSION_KEEP_ALIVE=true を指定 | アプリケーション側での実装が必要 |
プラットフォームサポート | Windows、macOS、Linux | 主に Windows |
これらを設定すれば、あとはドライバーがすべてのリクエストにわたってセッション再利用を自動的に処理してくれます。
キープアライブの設定
プーリングを有効にするだけでは不十分で、アイドル状態のセッションが切断されてしまっては意味がありません。DSN 文字列に CLIENT_SESSION_KEEP_ALIVE=true を追加すると、Snowflake は非アクティブ時にもセッションを維持するようになります。CData ODBC Driver は、この設定を SessionIdleTimeout プロパティとあわせて DSN で直接サポートしています。Snowflake 純正 ODBC ドライバーの場合、このパラメーターはアプリケーション側で処理するか、Snowflake 側で ALTER SESSION を使って設定する必要があります。
プールサイズの決め方
プールを有効にするだけでなく、適切な数の接続を確保することも同じくらい重要です。目安としては、平均的な同時実行クエリ数の約 2 倍にしておくとよいでしょう。たとえば、通常 10 個のクエリが同時に走るなら、プールサイズは 20 程度が適切です。プールがウェアハウスの処理能力を超えて大きくなると、クエリがキューイングされ始めます。これは後述するウェアハウスサイジングとも関わってくるポイントです。
2. 処理を Snowflake 側に寄せる:クエリフォールディング、プッシュダウン、パススルーを活用する
クエリフォールディングとは、BI ツールが自身のデータ変換処理をネイティブな Snowflake SQL に変換する仕組みのことです。うまく機能していれば、Snowflake 側で処理が完結し、結果だけが返ってきます。逆に機能していないと、BI ツールが生データを丸ごと取得してローカルで処理することになり、パフォーマンスは落ち、メモリ消費も増えてしまいます。
フォールディングが効いているか確認する
最適化に取り掛かる前に、まずは現在の環境でフォールディングが機能しているかを確認しましょう。確認方法は次のとおりです。
Power BI: Power Query 内の任意のステップを右クリックし、「ネイティブクエリの表示」が選択できるかを確認します。選択できればそのステップはフォールディングされており、グレーアウトしていればフォールディングが途切れています。
Tableau: パフォーマンスログで SFD_QF_SUCCESS を探します。これが見当たらない場合、変換処理は Snowflake ではなくローカルで実行されています。
フォールディングが途切れる箇所が分かったら、次はドライバーレベルでプッシュダウンが有効になっているかを確認します。
クエリプッシュダウンとパススルーの設定
Snowflake 純正 ODBC ドライバーの場合、プッシュダウンの動作は完全に BI ツール側に依存します。Snowflake で実行する処理とローカルで処理するものを切り分けるための設定はドライバー側にありません。一方、CData ODBC Driver には独自のクエリエンジンが組み込まれており、フィルター、集計、射影といったサポート対象の処理を能動的に Snowflake へプッシュします。プッシュダウンできない処理があれば、ドライバー側でクライアントサイド処理に切り替えてくれます。BI ツール任せにする必要がない、というのが大きな違いです。
完全にコントロールしたい場面では、QueryPassthrough=true を設定してみてください。SQL がドライバー側で書き換えられず、そのまま Snowflake に送信されるため、Time Travel や半構造化データの射影など、Snowflake 固有の構文を使うときに便利です。
プッシュダウンが機能しなくなる典型的な原因の一つが、データ型の不一致です。クエリで不要な型変換をかけてしまうと、ドライバーは Snowflake に処理を任せず、ローカルでデータを取得してしまいます。
-- Good: Snowflake のネイティブ型と一致している
SELECT order_id, order_date FROM orders WHERE region = 'US-East'
-- Bad: 不要な CAST がクライアントサイド処理を強制してしまう
SELECT order_id, CAST(order_date AS VARCHAR) FROM orders WHERE region = 'US-East'
3. プリペアドステートメントとパラメーターバインディングでクエリプランを再利用する
クエリのフォールディングが正しく動き、ドライバーレベルのプッシュダウンも効くようになったら、次は Snowflake が同じクエリを何度コンパイルしているかに目を向けてみましょう。同じクエリを値だけ変えて繰り返し実行するアプリケーションでは、毎回パース、検証、実行プラン作成が発生してしまいます。プリペアドステートメントは、クエリの構造をあらかじめ定義しておくことでこの問題を解決します。Snowflake は一度コンパイルしたものを別のパラメーターで再利用するため、毎回のパース処理をスキップできます。
バインドパラメーター
毎回コンパイルするのではなく、値をプリペアドステートメントにバインドしましょう。Snowflake はクエリ構造を認識して再コンパイルをスキップし、そのまま実行に進みます。
ODBC API での実装
言語によってアプローチは少し異なりますが、考え方は共通です。クエリを一度定義し、値をバインドして実行する、これだけです。
C/C++: SQLPrepare でクエリを定義し、SQLBindParameter で値をアタッチして、SQLExecute で実行します。バルクインサートでは SQL_ATTR_PARAMSET_SIZE を設定すると、複数行を 1 回の呼び出しにまとめられます。
Python (pyodbc): execute 呼び出しに直接パラメーターを渡します。
cursor.execute("SELECT * FROM orders WHERE region = ?", "US-East")
.NET: OdbcCommand でクエリを定義し、パラメーター値の位置に ? を使います。値は Parameters.AddWithValue でバインドします。
動的 SQL は避ける
文字列連結で SQL を組み立てると、Snowflake は変化形ごとに新しいクエリとして扱ってしまいます。結果としてプランの再利用が効かず、コンパイルコストが上がるうえ、SQL インジェクションのリスクにもさらされます。パラメーター化されたクエリを使うほうが、速度面でもセキュリティ面でも有利です。
4. ウェアハウスを適切にサイジングし、ドライバー設定とあわせてコストを抑える
ワークロードに合ったウェアハウスでクエリを実行すれば、コストは下がります。ウェアハウスサイズの設定自体は Snowflake 側で行いますが、そのウェアハウスにどれだけの処理量を背負わせるかは、使っているドライバー次第です。
ドライバー選定がウェアハウスサイズに影響する理由
CData ODBC Driver がフィルター、集計、射影を Snowflake に直接プッシュすれば、ウェアハウスがクエリごとに処理するデータ量は減ります。これに QueryPassthrough=true を組み合わせれば、クエリはさらに軽量になり、同じワークロードを小さめのウェアハウスでこなせるケースも増えます。一方、Snowflake 純正 ODBC ドライバーではプッシュダウンが BI ツール任せになるため、必要以上にデータを引き出してしまい、結果的に大きく高コストなウェアハウスを選ぶ必要が出てきがちです。
まずは X-Small か Small で始めて、典型的な ODBC クエリをテストしてみましょう。キューイングが発生したり実行が遅くなったりしたタイミングで初めてスケールアップするのがおすすめです。Snowflake のクレジットはサイズが 1 段階上がるごとに倍になります。X-Small は 1 時間あたり 1 クレジット、Small は 2、Medium は 4、Large は 8 です。
自動サスペンドとドライバーレベルのセッション制御
Snowflake はデフォルトで、最後のクエリから 10 分間ウェアハウスを稼働状態のままにします。AUTO_SUSPEND=60 と AUTO_RESUME=TRUE を設定すれば、60 秒の非アクティブで停止し、必要に応じて自動的に再開するようになります。再開時にわずかな遅延が発生するため、ドライバーが毎回新しい接続を開く構成だとアプリケーションがストールしてしまうことがあります。CData ドライバーのプーリングと、第 1 章で触れた CLIENT_SESSION_KEEP_ALIVE=true を組み合わせれば、セッションが常にスタンバイ状態に保たれるため、こうしたストールを避けられます。CData ドライバーの Timeout プロパティ(デフォルト 60 秒)も、ウェアハウス再開のための余裕を確保するのに役立ちます。
クレジット使用量のモニタリング
クレジット使用量を追跡したい場合は、SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY を参照しましょう。
SELECT warehouse_name,
SUM(credits_used) AS total_credits FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE start_time >= DATEADD('day', -30, CURRENT_TIMESTAMP()) GROUP BY warehouse_name ORDER BY total_credits DESC;
常時可視化したいときは、CData Connect AI でダッシュボードを構築し、すべてのウェアハウスにわたって使用量をモニタリングするとよいでしょう。
5. バッチ処理と診断でネットワークレイテンシを最小化する
ウェアハウスサイズを最適化し、クエリをチューニングしても、ネットワーク距離が処理を遅らせていることがあります。アプリケーションサーバーと Snowflake アカウントが別々のクラウドリージョンに配置されていると、すべてのクエリがその距離を往復することになります。アプリケーションを同じリージョンに移すだけで、パフォーマンスが 30〜50% 改善するケースもあります。コンプライアンス要件のある本番ワークロードでは、Snowflake PrivateLink を使うとパブリックインターネットを経由せず、クラウドプロバイダーのプライベートネットワーク経由でトラフィックをルーティングできます。
ドライバーレベルでは、PREFETCH_ROWS を設定して、1 回のラウンドトリップで複数行をまとめて取得するようにしましょう。分析系ワークロードでは 10,000 程度、トランザクション系のクエリでは 1,000 程度が目安です。これでメモリを圧迫することなく、ラウンドトリップ数を削減できます。これらをすべて試してもまだ遅い場合は、ネットワーク層を疑ってみましょう。SnowCD で Snowflake エンドポイントへの接続性をテストできます。さらに踏み込んで分析したいときは、Wireshark を使うと便利です。
Snowflake ホストでフィルタリング: ip.addr ==
TCP ハンドシェイクでフィルタリング: tcp.flags.syn == 1 && tcp.flags.ack == 0
結果の件数をカウント。各エントリが新しい接続オープンに対応します。
カウントが多い場合は、接続が再利用されていないということです。これはステップ 1 のプーリング設定に立ち戻るサインです。ここまでの 5 つのテクニックを組み合わせれば、Snowflake ODBC 接続は高速化され、コストは下がり、運用もぐっと楽になります。
よくある質問
再認証なしで Snowflake ODBC セッションを維持するにはどうすればよいですか?
DSN で CLIENT_SESSION_KEEP_ALIVE=true を有効にしてください。CData ODBC Driver なら、この設定を SessionIdleTimeout プロパティとあわせて DSN で直接サポートしています。Snowflake 純正ドライバーの場合は、ALTER SESSION を使うか、アプリケーション側で処理する必要があります。
32 ビット版と 64 ビット版でパフォーマンスに違いはありますか?
はい、Power BI のような最新の BI ツールには 64 ビット版を使うのがおすすめです。アーキテクチャの不一致による認証エラーや、メモリページングの遅延を避けられます。
QueryPassthrough とドライバー組み込みのプッシュダウンの違いは何ですか?
プッシュダウンは、Snowflake で処理するかクライアント側で処理するかを CData ドライバーが判断する仕組みです。QueryPassthrough=true にすると、SQL が書き換えられずそのまま Snowflake に送信されます。BI ツールのクエリにはプッシュダウンを、Time Travel など Snowflake 固有の構文にはパススルーを使うとよいでしょう。
大きな結果セットを取得するときにラウンドトリップを減らすには?
PREFETCH_ROWS を設定して、1 回のラウンドトリップで複数行を取得するようにしてください。分析系なら 10,000 程度、トランザクション系なら 1,000 程度が目安です。あわせて接続プーリングを使い、毎回新しい接続を開かずにセッションを再利用しましょう。
CData で Snowflake ODBC のパフォーマンス最適化を始めましょう
CData ODBC Driver for Snowflake は、接続プーリング、クエリプッシュダウン、プリペアドステートメントなどを標準でサポートし、シンプルかつ高速な接続性を提供します。統一されたドライバーから 350 を超えるデータソース に接続できます。今すぐ無償トライアルをお試しください!
※本記事は CData US ブログ Optimize Snowflake ODBC Performance Faster & Smarter の翻訳です。
CData Drivers & Connectors を試してみる
データソースと普段お使いのツールをシームレスにつなぎ、ライブデータへの高速アクセスを実現します。標準規格ベースのコネクタなら、データ連携はとてもシンプル。選んで、クリックするだけで、すぐに使い始められます。
無償トライアルをはじめる