🏔️

Snowflakeパフォーマンスのカギはやっぱりデータモデリング

2023/12/19に公開

この記事はSnowflakeアドベントカレンダー2023の19日目です。

はじめに

昨年、Snowflakeのパフォーマンスにおいて非常に重要な概念であるクラスタリングとプルーニングに関して、以下の記事を書きました。

https://zenn.dev/ryotas_data/articles/a709dadb4717d4

なぜクラスタリングやプルーニングが大事なのかを説明させてもらったのですが……理屈はわかった!ではどうすれば?という、具体的なユースケースについて全く書けておらず、また別のブログにします、と宣言しておいて、1年間完全に放置してしまっていました。すみません。。

この記事では、上記の記事の続きとして、クラスタリング・プルーニングの概念を知ったうえで、具体的にパフォーマンスを向上させるにはどうすれば良いかのヒントとなるようなものを書きたいと思います。

具体的には、そうデータモデリングです!

データモデリング万能説

データモデリングがめちゃくちゃ大事だよ!ってことは、SnowVillage YouTubeチャンネルの「みんなのデータモデリング講座」でも話をさせてもらっていますし、自社のブログにも記事を書いていますし、JDMCエンジニアの会で講演させてもらったりもしました。

https://www.nttdata.com/jp/ja/data-insight/2022/0407/

https://speakerdeck.com/ryotas_data/dwhniokerudetamoderu-ding-fan-karazui-xin-torendomade

なのでまーた渋谷がデータモデルの話してるよ……という感じかもしれませんが、本当にそれだけデータモデルって重要なんです!データマネジメント的な観点や、データアーキテクチャ的な観点等、データモデリングが重要であることを説明することはいくらでもできますが、今回はどちらかというと物理寄りの視点で、データモデリングがいかにデータウェアハウス製品のパフォーマンスを引き出すのに貢献するか、という視点の記事となっています。

ちなみに、本記事はSnowflakeで説明しますが、どのDWH製品でも似たような特性があります。どんな製品であっても、データモデリングが性能を大きく左右することには違いないです。

大福帳をやめてディメンショナルモデリングで性能大幅アップ!

結論から言うと、大福帳をやめてディメンショナルモデリングしましょう!というお話です。

大福帳(ワンビッグテーブル、ワイドテーブル、フラットテーブルなどとも言います)、つまり一つのテーブルにたくさんのカラムを持たせて、結合しなくても分析できる構造のデータモデルは、確かにメリットもあります。

ただ、性能面でいうと、Snowflakeのアーキテクチャを考えてみても、実際に私の経験上でも、大福帳よりディメンショナルモデリングのほうがずっと性能が良いです。このことについて、3つの誤解に反論する形で解説していこうと思います。

誤解1:現代のクラウドDWHは列指向なので、ディメンションをまとめることの効果は低い

ディメンショナルモデリングは、ディメンションが別テーブルとしてファクトから切り出されています。例えば10億行あるファクト表に対して、ディメンション表は1万件とか、少なければ100件とかいうことも普通にありえます。ファクト表に対して繰り返し登場するディメンション列を別の表として実装することは、行指向の従来型RDBMSではストレージを節約する意味で重要な利点であり、繰り返しを省くのはRDBとして当然のことでした。

一方、現代のDWHのほとんどは列指向(カラムナ型)です。どんなに行数のあるテーブルでも列ごとに値は並びかえられ、データ種の少ないカラムは大きく圧縮されます。確かに、もはやディメンション列を別テーブルとして持つ必要はないようにも思えます。

しかし、このことを考える場合には「パーティション」の存在を忘れてはいけません。カラムナ型のRDBでは、確かに列ごとにデータが並び替えられ圧縮されますが、それはあくまでもパーティションの中だけです。パーティションの存在しないDWHは(私の知る限り)ありません。そしてSnowflakeにも、「マイクロパーティション」というパーティションが存在します。前回の記事でお伝えした通り、マイクロパーティションごとにデータを列方向に保持するハイブリッドカラムナというアプローチです。

例:リスト項目の取得

例えば、いくつかのプルダウンメニューを備えたダッシュボードを想定します。そのダッシュボードは10億件のデータをもとに、プルダウンでディメンションを選択し、選択結果に応じて画面に様々な情報を表示するようなものだとします。

上図のようなダッシュボードを初期表示させるとき、左ペインにあるプルダウンのリスト項目を取得するために、データソースとなるデータベースにはそのリスト項目のカラムをSELECT DISTINCTして取得することになります。このとき、もしもそのテーブルが大福帳だった場合、Snowflakeのプルーニングの仕組み上、最大値/最小値によるプルーニングが効かないため、必ず全てのマイクロパーティションを走査することになります。しかもこのフルスキャンは、プルダウンのボックスの数だけ発行されたSQLによって並列に実行されます。

もちろん、マイクロパーティションの特性上、使おうとしているもの以外の列をスキャンすることはありませんが、対象の列だけだとしてもS3上に配置された多数のマイクロパーティションからウェアハウス上にネットワーク越しでダウンロードして重複排除を行うというのは、決して軽い操作ではないことがお分かりいただけるのではないでしょうか。

これがディメンションとして別テーブルとして定義されていれば、その小さいテーブルが格納された少数のパーティション(多くの場合は1~数パーティション程度)をスキャンすれば済みます。

リスト項目の取得というのは一つの例であって、リストは低頻度で取得してローカルに置けば良いのではないかとか、キャッシュを利用しようとか、色々と回避策はあります。しかし残念ながらBI製品によってはダッシュボードの一部分のみを別データソースにしたり、別の更新頻度にするなどと言った制御が難しいこともあります。なにより、アーキテクチャとしてこのような大きな差が出てしまうということをぜひ知っておいてください。

現代のクラウドDWHにおいても、行指向的な特性は少なからず存在し、ディメンションを別テーブルとして保存することには大きな意味があるということです。

誤解2:現代のクラウドDWHは列指向なので、結合のパフォーマンスが悪い

インデックスを利用することのできる行指向のデータベースに比べて、カラムナ型データベースは結合の性能が良いとは言えません。あらかじめ結合しておいた大福帳のほうがパフォーマンスが良いことは確かに考えられるでしょう。

しかしこれは行指向でもそうですが、クエリの実行計画次第という側面も大きいです。結合はパフォーマンスチューニングの腕の見せ所であり、それは行指向であっても列指向であっても実は変わりません。むしろ結合することにより、単一テーブルよりも性能が良くなることも十分にあり得ます。いよいよ、プルーニングとクラスタリングの出番です!

例:ディメンション表を使ってファクト表をフィルタリング

先ほどのダッシュボードで、左ペインのプルダウンをユーザが操作することによりグラフや表が変化する場面を考えてみます。

この左ペインにディメンション表を用い、メインのグラフや表にファクト表を用いる場合、結合が発生します。プルダウン操作後画面描写時に以下のようなSQLが発行されるとします。(このクエリはあくまでもイメージです)

select f.date, sum(f.price)  
from fact_table as f
inner join dimension_table as d
on f.store_key = d.store_key
where d.store_region = 'XXXXXXXX'
group by f.date;

このクエリはWHERE句によってディメンション表が先に絞り込まれ、その結果表を使って(駆動表として)ファクト表が絞り込まれることになります。あらかじめファクト表における外部キー(ディメンションとの結合キー)項目、つまりこの例で言えばstore_keyをクラスタリングキーに設定しておいたうえで、クエリプロファイルで実行計画を確認すると、その結合の際にもプルーニングが効いていることがわかります。

前回の記事で説明したようなシンプルなWHERE句の絞り込みだけでなく、JOIN ON句による結合でもプルーニングが効くということです。当然、ディメンション表の絞り込み結果によっては結合キーの取る値は複数にもなり得ますし、それが連続値とも限りません。それでも、プルーニングは効くのです。この仕組みはダイナミックプルーニングと言い、Snowflakeの歴史的論文「The Snowflake Elastic Data Warehouse (SIGMOD 2016) 」にもプルーニングの重要な利点として載っている、分散データベースとして非常に優れた結合手法です。

ファクト表のクラスタリングキーをディメンション表との外部キーに設定することは、巨大なデータに対するクエリパフォーマンスを向上させるうえで非常に重要なテクニックです。このように設定することで、ディメンション表上のどの列を対象に絞り込んだとしても、ファクト表に対しては常に外部キーでフィルタリングをかけることになるため、プルーニングが効きます。一方、もしこのテーブルが大福帳だった場合は、多数の列に対してクラスタリングキーをうまく設定できず、絞り込む列によってプルーニングを効かせられない状況が出てくるでしょう。ディメンショナルモデリングが性能に効くという一つの大きな例です。

ちなみに、これをTableauで実装するためには「リレーションシップ」機能の活用が欠かせません。リレーションシップについてはもりたひろあきさんによる以下の記事シリーズが非常に詳しいのでお勧めです。

https://note.com/hiroakimo/n/n6d7466394f5f

Tableauユーザの方は、リレーションシップを使いこなせるとSnowflakeのライブ接続でのパフォーマンスがぐっと向上し、Hyperにできないようなサイズ感のデータを扱ったり、ライブ接続ならではのデータ鮮度を持ったダッシュボードを作ったりできるようになります。

いずれの製品を使うにせよ、小さなテーブルを駆動表として大きなテーブルに結合し、しっかりフィルタリングして扱うということは、本当に巨大なデータを扱う現代のDWHにおいてこそ、良いパフォーマンスを引き出すために必要な技術と言えるでしょう。

誤解3:データ民主化時代において、ディメンショナルモデリングはエンドユーザの学習コストが高すぎる

これはパフォーマンスの話ではなく、そもそもなぜ大福帳を作るのか、という根本原因とも言える理由です。しかも、誤解とは言えないかもしれません。というか正しいです。確かに、ディメンショナルモデリングをエンドユーザにまで浸透させるのは非常に大変なことです。データエンジニアが大福帳のデータマートを作って、「これ使ってください」と渡して済むのであれば、そのほうがずっと現実的に思えます。

しかし、このやり方は多くのサイロと、その結果として大きな混乱を生む可能性があります。つまり依頼に応じて都度似たような大福帳を作っていると、一つの指標を表す様々な尺度、粒度を持った大福帳を量産してしまうという悲劇を巻き起こしかねないということです。(残念ながらこれは本当にどの企業でもよく見られる事象です)

毎回固定的な大福帳データマートを作るやり方は、そもそもがデータエンジニアの稼働が非常に高止まりする可能性を持っており、スケーラビリティに乏しく、大福帳の量産とメンテ、その品質の担保にデータエンジニアは追われることになってしまいます。そのうえ、これまで見てきたようにあまり性能も良くないのですから、データエンジニアとしては大福帳は全然作りたいものではありません。

と言ってもビジネスユーザが理解できないデータを作ってもしょうがないので、この問題にはなかなか良い解決策がない……と思われていました。

しかしここ1,2年でにわかに注目を集まている技術であるセマンティックレイヤーは、この大福帳量産問題の一つの解決策になりうるのではないかと私は考えています。まだまだ発展途上の技術で、エンドユーザ向けに十分な情報を提供できるようになるかは今度の発展にかかってきますが、ディメンショナルモデリングによるメンテナンス性の良さや性能の良さを活かしながら、エンドユーザ向けに使いやすいセマンティックなデータモデルを提供できるようになったら最強です。dbtをはじめとするこの領域の進歩には引き続き大注目しています。

次世代のモダンデータスタックにおいては、大福帳以外のエンドユーザ向けデータモデルが誕生しつつあり、ディメンショナルモデリングの性能の良さを生かせるようになってきている、という状況だと言えます。

まとめ

ということで、パフォーマンスを中心とした観点でSnowflakeにおけるディメンショナルモデリングを深堀してみました。

実際にディメンショナルモデルにクエリを流してプロファイルを見ていると、ダイナミックプルーニングを始めとして本当にSnowflakeのクラスタリング・プルーニングの技術がすごいものだとわかるし、そもそもディメンショナルモデリングがいかに現代にも通用する優れた技法なのかがよくわかります。

ディメンショナルモデリングはユーザから見てすぐに効果が出るものではありませんが、ソースデータを無理やりこねくりまわして頑張って大福帳を作っていっても、いつかは無理が来ます。まずは一呼吸おいて、ディメンショナルモデルを作ってみてください。DWHの基本を実践することで、輝ける未来が見えてくるはずです。

Discussion