Web開発
単一のParquetファイルからの高速ドリルダウンダッシュボード
Fast drilldown dashboards from a single Parquet file (hamiltonulmer.com)
要約
この記事では、オブジェクトストレージに保存された単一のParquetファイルから、データベースやクエリエンジンを使わずに高速なドリルダウンダッシュボードを構築する方法を解説しています。ブラウザで動作するHyparquetのようなJavaScript Parquetリーダーと、事前に集計されたデータを「グルーピングセット」としてParquetファイル内に格納する手法を組み合わせることで、効率的なデータアクセスを実現します。このアプローチは、限定的なチャートとフィルターセットを持つ顧客向けダッシュボードに特に有効です。
全文翻訳
毎月、オブジェクトストレージの巧妙な使い方が新たに登場しています。これは、AI以外のソフトウェアインフラストラクチャにおいて、最も活発な分野と言えるでしょう。最近の話題は、Vicent Martí氏によるCursor OriginのS3 + WALアプローチで、Gitリポジトリを大規模に管理するというものでした。それは技術文書の傑作ですが、この記事はそうではありません。
この記事を読む前から、オブジェクトストレージがうまく機能するであろう、顧客向け分析ダッシュボードという全く異なる種類のタスクについて空想していました。友人がR2上のIcebergに顧客利用データを持っており、ユーザーにフィルター付きの基本的なチャートを表示したいと考えています。彼は、これ以上ベンダーを追加したくないと言っていました。これは、私が現在勤務しているクラウドホスト型DuckDBデータベース企業であるMotherDuckを除外する理由となりました。
分析の世界では、オブジェクトストレージしか持たない場合、すべてがレンジリクエストのように見えます。おそらく、このようなデータをParquetバックのデータキューブにロールアップし、Hyparquetという小さなJavaScript Parquetリーダーを使って、ブラウザで実行できる非常にシンプルなレンジクエリでダッシュボードを埋めることができるでしょう。これにより、データベースもクエリエンジンもなしに、実際のドリルダウンダッシュボードを提供できます。
キューブは数十MB(あるいは数百MB)になることもあります。なぜなら、正しくレイアウトされたファイルであれば、一度に数個の小さなスライスしか読み込まないからです。必要なのは、キューブを生成するデータパイプラインだけです。これは、実際の分析データベースを持つ場合に、実際のお金がかかる部分でもあります。
この異端なアイデアは、見過ごすにはあまりにも魅力的でした。最近では、DuckDBが私のあらゆるデータ問題に対する軽量な解決策だと仮定しています。それをテストするために、私のコンピューターにあった有名なNYC 311サービスリクエストデータセット(リクエストレベルで約3400万行、15年分程度)を使用し、都市機関、苦情の種類、提出の種類、区ごとのフィルター、および時系列用の単一の作成時間列で、40MBのParquetキューブにロールアップしました。そしてそれをR2に配置しました。
40MBは、全体をダウンロードするのに苦痛を感じるには十分な大きさです。下のデモダッシュボードは、Hyparquetを使用してそのファイルから直接読み取ります。バイトは、無料のr2.dev URLがレート制限されているため、Cloudflare Workerを通過します。Workerはバイトレンジをプロキシし、エッジでキャッシュします。ファイルは不変なので、これは安全です。
正直なところ、実際のデータベースや強力なクエリエンジンを省略しているにもかかわらず、新しいデータのロードがどれほど速いかに驚きました。UIが実際の読み取りのすべてを行い、この投稿に直接埋め込んでもページの読み込みを損なわないほど軽量です。実際の複雑さは、ほとんどすべてデータキューブのレイアウトにオフロードされています。チャートをスクラブしたり、リーダーボードの行をクリックしたりしてみてください。
このダッシュボードはどのように機能するのでしょうか?
データキューブ
グルーピングセット
このようなダッシュボードは、限定された分析的な質問(1日あたりのリクエスト数、特定の機関の1日あたりのリクエスト数、区ごとの全期間合計など)に答えるように設計されています。各質問はGROUP BYクエリで回答できるため、すべてを事前に計算して、各結果を小さなテーブル(グルーピングセットと呼ばれる)として保存できます。
グルーピングセットを1つのParquetファイルに積み重ね、各セットを1つのセクションにすると、データキューブになります。グルーピングセットは、質問に答えられるようにするか、データの取得レイテンシを削減する場合にのみ有用です。
このファイルには両方の機能があります。全期間合計はリーダーボードに供給され、フィルターのすべての組み合わせに対する日次グルーピングセットは、チャートのブラッシングによって生成されるデータの線グラフを提供します。週次および年次グルーピングセットは、チャートのブラッシングによって生じる行のスキャン数を減らします。同じ合計を日次行から合計することもできますが、週および年ごとに事前に計算すると、フェッチする行数が少なくなります。
row group
footer
ファイルにはダッシュボードをレンダリングするためのグルーピングセットが含まれていますが、ブラウザは必要な行だけを抽出する必要があります。Parquet形式の2つの機能がそれを可能にします。
Parquetファイルは数万行の行グループに分割され、フッターで終わります。フッターには、行グループのバイトレンジと、その中の各列の最小/最大値に関するメタデータが含まれています。
クライアントはフッターを一度読み取ります。各クエリは、最小/最大値を使用して一致する可能性のある行グループを選択し、それらのバイトレンジを取得し、ブラウザで行を集計します。
ダッシュボードリクエストの低レイテンシは、Parquetファイル内の行がソートおよびスキャンされる方法によるものです。
ファイル内の行がランダムに並べられている場合、各行グループの最小/最大値は各列のほぼ全範囲をカバーし、クエリは少数の行を取得するためだけにファイルの大部分を読み取る必要があります。
代わりに、各グルーピングセットの行は、クエリがフィルターをかける列でソートされます。一致する行は通常、ファイルの連続したストレッチを形成し、最小/最大統計により、リーダーは残りの行グループを無視できます。
これが、エージェンシーリーダーボードでNYPDをクリックすると、ファイル全体ではなく40MBのファイルから約260KBが読み取られる理由です。
以下は、バイトとグルーピングセットの観点からのファイルの実際のレイアウトです。
row groups
grouping set
rows
size
totals
"requests in view" total and the four leaderboards feed
831.1k
1.7mb
all time
1 row group
read when no date range is brushed
4.8k
103kb
by week
16 row groups
read when brushed: the leftover weeks at the range's edges
796.6k
1.3mb
by ISO year
2 row groups
read when brushed: the whole years in the range's middle
29.7k
272kb
daily · no dimensions
1 row group
draws the line chart when no filters are active
5.0k
171kb
daily · one dimension
draws the line chart when one filter is active
770.3k
2.4mb
channel
1 row group
22.7k
171kb
borough
2 row groups
30.0k
330kb
complaint
13 row groups
635.6k
1.5mb
agency
3 row groups
82.0k
383kb
daily · two dimensions
draws the line chart when two filters are active
4.5m
10.6mb
borough + channel
3 row groups
127.7k
401kb
complaint + channel
23 row groups
1.1m
2.6mb
complaint + borough
42 row groups
2.1m
4.5mb
agency + channel
5 row groups
198.0k
640kb
agency + borough
8 row groups
358.5k
997kb
agency + complaint
13 row groups
643.0k
1.5mb
daily · three dimensions
draws the line chart when three filters are active
7.3m
15.8mb
complaint + borough + channel
65 row groups
3.3m
6.8mb
agency + borough + channel
17 row groups
804.0k
1.9mb
agency + complaint + channel
22 row groups
1.1m
2.4mb
agency + complaint + borough
43 row groups
2.1m
4.6mb
daily · all four dimensions
64 row groups
draws the line chart when all four filters are active
3.3m
6.6mb
footer · the index
byte ranges and min/max statistics for every section; read first, once
195kb
このセットアップは、2つの条件の下で機能します。
チャートとフィルターの組み合わせが小さく収まること、そしてパイプラインが顧客ごとにファイルを十分に速く再生成でき、更新頻度を満たすことです。
ほとんどの利用状況と請求ページは、両方を満たしています。これらは固定されたチャートセット(時間ごとのイベント、時間または日ごとのカウントまたは合計、いくつかのフィルターまたはリーダーボード)であり、リアルタイムではなく粗いスケジュールで更新されるデータに対して機能します。これは、顧客のためでもあり、あなたのためでもあります。
レイテンシの観点からは、キューブのサイズは問題になりませんが、顧客ごとにスケジュールで再生成するため、ある程度小さくしたいと思うでしょう。
時間粒度は私の例では明らかに支配的であり、日次セクションがファイルのバイトの大半を占めています。カーディナリティはもう一つの乗数です。苦情の種類には485の異なる値があり、図の大きなセクションのほとんどすべてに含まれています。
実際、線グラフに日次粒度を選択すると、ファイルサイズは週次相当(5.6MB)の約7倍になりました。それでも、日次粒度はレンジリクエストのレイテンシに有意な影響を与えませんでした。なぜなら、どのような操作でも常に数個の行グループしか読み込まないからです。そして、このケースでは、大きな1日のデータを見ることができるのは良いことです。