大規模サブ秒アナリティクスの実践ガイド:Apache Druid入門

Database tutorial - IT technology blog
Database tutorial - IT technology blog

大規模リアルタイムアナリティクスの課題

以前、高トラフィックなECサイトの監視ダッシュボードを構築しようとしていたときのことを今でも覚えています。MySQL、PostgreSQL、MongoDBをそれぞれの用途で使っており、それぞれの領域では十分なパフォーマンスを発揮していました。しかし、毎秒5万件のイベントを取り込みながら数十億行に対して複雑な集計クエリを実行しようとすると、途端に限界を迎えました。ダッシュボードのウィジェットは30秒以上スピナーが回り続け、最悪の場合はデータベース全体がロックしてしまうこともありました。

従来のRDBMSやNoSQLストアは、OLAP(オンライン分析処理)特有のアクセスパターンを想定して設計されていません。大規模データセットへの高並列読み書き操作に弱く、行レベルのロックやクエリに不要なデータのフルスキャンに多くの時間を浪費してしまいます。Apache Druidはこの問題を解決します。数十億件のレコードに対して高速なスライス・ダイス分析ができるよう設計された、リアルタイム分析データベースです。

クイックスタート:5分でDruidを起動する

Druidを理解する最善の方法は、実際にデータを処理させてみることです。ローカル開発環境には「Micro-Quickstart」構成を使用します。この構成では、Broker、Historical、MiddleManagerなどのDruidサービスをすべて1台のマシンに集約して動かせます。

1. 前提条件

Java 8または11が必要です。DruidはJavaのバージョンに対して非常に厳格です。取り込み中の予期せぬクラッシュを避けるため、OpenJDK 11の使用を強くお勧めします。

# 環境を確認する
java -version

2. ダウンロードと展開

wget https://dlcdn.apache.org/druid/29.0.0/apache-druid-29.0.0-bin.tar.gz
tar -xzf apache-druid-29.0.0-bin.tar.gz
cd apache-druid-29.0.0

3. クラスターの起動

micro-quickstartスクリプトを実行して、スタック全体を起動します。

./bin/start-micro-quickstart

サービスが起動したら、http://localhost:8888にアクセスしてください。これがDruid Consoleです。データの取り込み管理、タスクの監視、SQLクエリの実行など、あらゆる操作のコントロールセンターとして機能します。

深掘り:アーキテクチャとデータ取り込み

Druidの速さは魔法ではなく、アーキテクチャによるものです。データを「セグメント」と呼ぶ時間軸でチャンク分割されたファイルに格納します。このファイルはカラム指向で、強力な圧縮が施され、自動的にインデックスが作成されます。つまり、直近1時間の特定のユーザーIDを検索する場合、Druidはそのウィンドウに必要なファイルとカラムにのみアクセスします。

Apache Kafkaによるストリーミング取り込み

本番環境のほとんどでは、DruidsをKafkaトピックに直接接続します。DruidのIndexing ServiceはネイティブなKafkaコンシューマーとして機能し、イベントをリアルタイムで読み込んでインデックスを作成します。これにより、イベント発生から200ミリ秒以内にデータがクエリ可能な状態になります。

コンソールでKafkaスーパーバイザーを設定する手順:

  1. Load Dataを選択し、次にStreamingを選択します。
  2. KafkaのブートストラップサーバーとターゲットトピックのURLを入力します。
  3. Timestamp specを設定します。Druidはデータを効果的にパーティションするために主タイムスタンプが必要です。
  4. Dimensionsuser_idcountryなどのカテゴリデータ)とMetricspricelatency_msなどの数値)を定義します。

ロールアップの有効化を常にお勧めします。アプリが毎秒1万件のクリックを生成している場合、すべての個別クリックを保存する必要はないでしょう。Druidは取り込み時にこれらを集計できます。たとえば、1分単位でデータをロールアップするだけで、ストレージ使用量を10倍以上削減しながら、クエリを大幅に高速化できます。

// ロールアップの取り込みスペック例
"granularitySpec": {
  "type": "uniform",
  "segmentGranularity": "HOUR",
  "queryGranularity": "MINUTE",
  "rollup": true
}

高度な使い方:クエリと最適化

Druidのクエリは標準SQLを使うため、慣れ親しんだ書き方ができます。Druid BrokerはSQLを受け取り、ネイティブなJSONベースのクエリに変換した後、並列実行のためクラスター全体にワークロードを分散させます。

高パフォーマンスSQLクエリ

Druidは複雑なフィルタリングで真価を発揮します。ライブパフォーマンスダッシュボード向けのクエリ例を示します:

SELECT 
  TIME_FLOOR("__time", 'PT1M') AS "minute",
  country,
  SUM(clicks) AS total_clicks
FROM "website_traffic"
WHERE "__time" >= CURRENT_TIMESTAMP - INTERVAL '1' HOUR
GROUP BY 1, 2
ORDER BY "minute" DESC;

データスケッチの効率性

50億行に対して「ユニークビジター数」(Count Distinct)を計算することは、ほとんどのデータベースにとってパフォーマンス上の大きな障壁です。Druidはこれをデータスケッチ(HyperLogLogなど)を使って処理します。すべてのユニークIDをメモリに保持する代わりに、数学的な近似値を格納します。これにより、CPUとメモリ使用量を大幅に削減しながら、98%の精度を実現できます。

本番クラスターの実践的なヒント

本番環境でDruidを運用することは、ローカルのテストとは全く異なります。私が苦労して学んだ3つの教訓を紹介します:

1. セグメントサイズを適切に設定する

セグメントサイズはパフォーマンスを左右する最大の要因です。小さなファイルを大量に作成してしまうと(例:低ボリュームのストリームを分単位でパーティション分割するなど)、Brokerがメタデータの処理に追われてしまいます。セグメントサイズは300MBから700MBを目安にしてください。また、自動コンパクションを使って小さなフラグメントをバックグラウンドで結合させましょう。

2. データティアリングを実装する

すべてのデータが等しく価値を持つわけではありません。ユーザーが最も気にするのは、たいていの場合、直近24時間または7日間のデータです。私は通常、最新データ用に高速NVMe SSDを使った「ホット」ティアと、古いデータを低コストのHDDストレージに移行する「コールド」ティアを設定します。Druidはリテンションルールに基づいてこの移行を自動的に管理します。

3. ダイレクトメモリを監視する

Druidは処理バッファに大量の「オフヒープ」ダイレクトメモリを使用します。OutOfMemoryErrorが発生した場合、単純にJVMヒープを増やすだけでは解決しません。druid.processing.buffer.sizeBytesの設定を確認してください。また、これらの操作のためにJavaヒープ外で十分な空きRAMがOSに確保されているかどうかも必ずチェックしましょう。

リアルタイム分析システムの構築は一朝一夕にはいきません。トランザクション処理にはPostgresやMySQLが優れていますが、サブ秒レイテンシが譲れない要件となる分析レイヤーにおいては、Apache Druidが私の最初の選択肢です。まずはmicro-quickstartから始め、自分のストリームを接続して、最も難しいクエリにどれだけ対応できるか確かめてみてください。

Share: