S3のログをコスト効率よく検索したい!Quickwit調査編
はじめに
tsuzuki(@melktzk)です。
ログ管理について、コストを抑えつつログの全体を眺めながら検索・閲覧したい。でもAthenaとかでクエリを書くのはちょっと面倒。結局、とりあえずS3に生ログ保管するだけで活用できていない...そんなシーンがたまにあります。
そのような要件に対応するログ管理方式は様々あるかと思いますが、今回は関連するツールとして、2025年1月にDatadogによる買収が発表されたQuickwitというOSSを調査した内容について紹介していきます。
Quickwit とは
Quickwitは、Amazon S3やGoogle Cloud Storage、Azure Blob Storageなどのクラウドストレージ上で動作するクラウドネイティブな全文検索エンジンです。公式サイトでは「Datadog、Elasticsearch、Loki、Tempoのオープンソース代替」と位置づけられています。
基本情報
OSSとしての基本情報は以下の通りです。(2025年12月時点)
| 項目 | 内容 |
|---|---|
| 開発言語 | Rust |
| ライセンス | Apache License 2.0 |
| GitHub Stars | 約10,600 |
| 最新バージョン | 0.8.2(2024年6月リリース) |
| 公式サイト | https://quickwit.io/ |
| GitHub | https://github.com/quickwit-oss/quickwit |
CHANGELOGには0.9.0のエントリが存在しますが、2025年12月時点でGitHubリリースページには未掲載です。
2021年7月に0.1.0がリリースされて以降、定期的に機能追加・アップデートがされており、プルリクやディスカッションも盛んにされている様子が見受けられます。
また、公式ドキュメントページやブログ記事もしっかり整備されており、とても親切なつくりになっている印象です。
Datadogによる買収
2025年1月、QuickwitはDatadogに買収されたことを発表しました。買収後もApache License 2.0でのOSS公開は継続される方針とのことです。
買収の背景として、以下の実績が挙げられています。
Datadogでは、Quickwitの技術を活用したDatadog CloudPremについて発表しています。2025年12月時点でプレビュー版として提供されており、今後の展開が気になるところです。
アーキテクチャ
Quickwitは以下のようなアーキテクチャとなっています。
Quickwitは上記4つのメインサービスと1つのメンテナンスサービスで構成されています。
-
Indexer
- データソース(Kafka/Kinesis/Pulsarなど)からドキュメントを取り込み、インデックス化してクラウドストレージにアップロード
-
Searcher
- REST APIからの検索クエリを受け付け、クラウドストレージ上を検索
-
Metastore
- インデックス設定やメタデータをPostgreSQL(またはS3上のJSONファイル)で管理
-
Control Plane
- インデックス化タスクのスケジューリング
-
Janitor
- ガベージコレクション (GC)、削除タスク、リテンションポリシーなどの定期メンテナンス
上記のような構成により、コンピュートとストレージを完全に分離している点が特徴です。
公式ブログによると、DatadogのHuskyと類似のアプローチで設計されているとのこと。インデックス化(書き込みのパス)と検索(読み取りのパス)を分離することで、コスト効率とクラスタ管理の簡素化を実現しているようです。
このような設計思想により、以下の利点が挙げられています。
- IndexerとSearcherがステートレスなため、独立してスケールアウト可能
- クラスターノード追加時にデータ移動やシャード再配置などが不要
- 安価なクラウドストレージにデータ保存可能であり、データ可用性の確保が容易
このあたりはLokiも近い設計思想になっていますね。また、Elasticsearchも2022年10月にStateless Architectureを発表し、Elasticsearch ServerlessとしてElastic Cloud上でスケール可能なサービスを提供していたりするので、割とメジャーな方式になっているのかもしれません。
特徴
ここからはドキュメントとコードを参考に、いくつか特徴的な点を書き出していきます。以下、クラウドストレージはS3としています。
インデックス化
Quickwitのインデックス化(indexing)は、Actorモデルを採用したパイプラインで処理されます。データの流れを簡潔にまとめると、以下の3段階で表せます。
- 取り込み
- データソースからドキュメントを取得し、JSON解析・スキーママッピングを実行
- バッファリングとインデックス化
- Tantivyエンジンで、ドキュメントをメモリ上のインデックスに追加
- 以下のいずれかの条件を満たすと"Split"ファイル(後述)を生成
-
commit_timeout_secs(デフォルト60秒)経過 -
split_num_docs_target(バッファドキュメントがデフォルト1,000万件)到達
-
- 公開
- SplitをS3にアップロード、Metastoreに登録して検索可能に
S3へのアップロード完了後に、MetastoreでSplit公開とチェックポイント更新を一括で行うことで、exactly-once semantics(重複や欠損なし)が可能になっているとのこと。
この仕組みにより、Indexer障害時も最後のチェックポイントから再開でき、データロスやデータ重複を防止します。ただしトレードオフとして、最大でcommit_timeout_secs(デフォルト60秒)時間分程度の遅延が発生することになります。
Splitとデータ構造
上記のとおり、Quickwitではインデックスデータを"Split"という単位で管理します。各SplitはUUIDで識別される独立した小さなインデックスで、主に以下の要素で構成されます。
- 転置インデックス: 全文検索用
- 列指向ストレージ: ソートや分析用
- 行指向ストレージ / ドキュメントストア(Zstd圧縮): IDでのドキュメント取得用
- Hotcache: Splitを開くために必要なファイル構造情報
具体的には、SplitはTantivy形式のインデックスファイルをバンドルしたものであり、以下のような構造となっています。
split_xxx.split
├── meta.json # Splitメタデータ
├── .managed.json # Tantivy管理ファイル
├── segment_0/ # セグメント(Tantivyインデックス)
│ ├── .store # ドキュメントストア(Zstd圧縮)
│ ├── .term # 転置インデックス(Term Dictionary)
│ ├── .pos # ポジション情報
│ ├── .fast # Fast Fields(列指向ストレージ)
│ ├── .fieldnorm # フィールド正規化情報
│ └── ...
└── hotcache
上記構造を持つ一塊のファイルとして連結された状態でS3に保存され、Hotcacheの構造情報を参照して、各ファイルにアクセスする仕組みとなっています。
この仕組みにより、S3上のSplitが例えば15GBなどのサイズでも、Range指定でHotcache部分(約10MB)だけの取得でSplit全体の把握ができるため、60ms以下で検索を開始可能であるとしています。以下、Splitファイル内の割合イメージ図です。
画像出典元: Quickwit 101 - Architecture of a distributed search engine on object storage
また、Splitメタデータは以下のようなJSON構造となっています。
{
"splits": [
{
"split_id": "...",
"num_docs": 10060714,
"split_state": "Published", // Staged → Published → MarkedForDeletion
"time_range": { // Split Pruningに使用
"start": 1691719200, // Unixタイムスタンプ(2023-08-11)
"end": 1691936694 // Unixタイムスタンプ(2023-08-13)
},
"split_footer": { // Hotcacheのバイト範囲
"start": 1612940400, // Splitファイル内のバイトオフセット指定(約1.5GB地点から)
"end": 1616529599 // Splitファイル内のバイトオフセット指定(約1.5GB地点まで)
}
}
],
"checkpoints": { // 処理済みオフセット(Exactly-Once保証)
"partition_a": "00000000000000000128",
"partition_b": "00000000000000060187"
}
}
検索時は、上記の時間範囲time_rangeやタグで不要なSplitのスキャンをスキップすることで、S3へのアクセスを最小限に抑えることができます。
# 動作イメージ
クエリ: timestamp:[2024-01-01 TO 2024-01-31]
Split A: time_range=[2023-12-01, 2023-12-31] → スキップ
Split B: time_range=[2024-01-15, 2024-02-15] → 検索対象
Split C: time_range=[2024-02-01, 2024-02-28] → スキップ
検索とワークロードの分散
検索時の処理フローは以下の通りです。
- MetastoreからSplitメタデータを取得
- 時間範囲・タグで関連するSplitを特定(Split Pruning)
- Rendezvous Hashingで各Splitを担当するLeaf Nodeを決定
- Leaf Nodeに検索を分散、結果をマージして返却
このアーキテクチャの利点として、以下の2つが挙げられています。
- ステートレス性
- どのSearcherノードでもクエリを処理できるため、ノードの追加・削除が秒単位で可能で、データ移動も不要。後述するLokiでは、Ingesterにステートがあるため、スケーリング時にステート管理が必要になる。
- キャッシュ効率
- Rendezvous Hashingにより、同じSplitへのクエリは常に同じLeaf Nodeにルーティングされ、前述のHotcacheが効きやすくなる。
Lokiとの比較
同様なOSSのログ管理ソリューションとしてよく挙げられるGrafana Lokiとの違いについて少し触れていきます。
アーキテクチャの違い
インデックス化
QuickwitとLokiは両方ともオブジェクトストレージをバックエンドとしますが、インデックス化方式に以下のような違いがあります。
- Quickwit
- 転置インデックス + 列指向ストレージ。ログ本文も含めてインデックス化する。
- Loki
- ラベルのみをインデックス化(いわゆるindex-freeなアプローチ)。ログ本文はインデックスせず、検索時はラベルでフィルタした後にgrep的な検索を行う。
ファイル数
Quickwitは前述のとおりSplit単位で大きなファイルにまとめる設計のため、ファイル数が少なく抑えられます。
一方、Lokiはラベルごとに細かくChunkを分割するため、大量のファイルが生成されます。この差はGETリクエストコストに直結します。
Lokiは3.0でBloom Filterを導入し、「needle in haystack」(大量のログから特定のログを探す)シナリオを改善しています。ただし、頻出ワードには効果が限定的との指摘があります。
ベンチマーク
Quickwit公式ブログでは、212GB/2.4億件のデータセットを使った比較が公開されています。テスト環境はGCP n2-standard-16 (16 vCPU, 64GB RAM)、Google Cloud Storageです。
インジェスト性能
以下表は、ログ取り込み時の性能比較結果の抜粋です。
| 項目 | Quickwit | Loki |
|---|---|---|
| インジェスト時間 | 123分 | 55分 |
| ファイル数 | 25個 | 145,756個 |
| ストレージサイズ | 53 GiB | 55 GiB |
Lokiはログ本文をインデックスしないため、ログ取り込みは高速です。したがってインデックス化時のコスト(主にコンピュート費用)はLokiの方が優位といえます。
検索クエリ性能
以下表は、最新ログ100件クエリ時のレイテンシの比較結果の抜粋です。
| クエリ種別 | Quickwit | Loki |
|---|---|---|
| キーワード検索 | 0.6秒 | 9.3秒 |
| ラベル検索 | 0.6秒 | 1.0秒 |
| ラベル + キーワード | 0.6秒 | 0.98秒 |
As expected, searching a keyword in the whole dataset is very costly with Loki, both in CPU and in the number of GET requests.
とあるように、キーワード検索は特にQuickwitのほうが優位となっているようです。
集計クエリ性能
以下の表は、ログボリュームを集計するクエリ実行時のレイテンシの比較結果の抜粋です。
| クエリ種別 | Quickwit | Loki |
|---|---|---|
| 全データセット集計 | 2.1秒 | 90秒 |
| キーワード検索 + 集計 | 0.4秒 | 565秒 |
| ラベル検索 + 集計 | 0.6秒 | 4.8秒 |
集計クエリはQuickwitが圧倒的に高速なようです。
GETリクエスト数
以下の表は、GETリクエスト数の比較でそれぞれ差が大きかったものを記載しています。
| クエリ種別 | Quickwit | Loki |
|---|---|---|
| キーワード検索 | 206回 | 14,821回 |
| 全データセット集計 | 88回 | 203,665回 |
GETリクエスト数はQuickwitの方が少なく、検索時のクラウドストレージコストはQuickwitが優位と言えます。
選択の指針
以上のベンチマーク結果から以下のようなことが言えそうです。
-
Quickwitが向いているケース
- 全文検索や集計/分析クエリが多い
- ログ取り込みは多少遅くてもよい
- クエリ頻度が高く、GETリクエストコストを抑えたい
-
Lokiが向いているケース
- 分析などはせずにラベルベースの検索がメイン
- 高速なログ取り込みが必要(大量ログを取り込むが検索は低頻度など)
- Prometheusなどを既に利用している
おわりに
本記事では、Quickwitについて机上調査した内容を紹介しました。
Quickwitは、スケールしやすいクラウドストレージをバックエンドとしながらGETリクエスト数を抑えることでコスト効率が良く、インデックス化の仕組みにより高いクエリ性能を持つことなどが分かりました。ログ管理の仕組みはコストが高くなりがちなイメージがあったのですが、Quickwitをうまく利用すれば解決できるかもしれませんね。また、今回記事では触れていませんがElasticsearch互換APIやGrafana連携、OpenTelemetry対応といった拡張性も魅力的です。
一方、月$7以下の低コストなサーバレス構成としてLambda利用した構成が公式ブログで紹介されており期待していたのですが、メンテ負荷が高いわりに利用者がいないとのことで削除されてしまいました。0.8.2では利用可能ですが、次のリリース以降は使用できなくなるようです。Quickwitを利用するようなユースケース(常にログを取り込み続ける大規模データ環境?)だとサーバレス化の需要は少ないのかもしれませんね。
また、Datadog買収後のロードマップについても、OSSとしての継続は発表されていますが、今後の方向性は注視が必要です。
次回は実際にQuickwitを環境構築して、S3上のログデータを取り込んで可視化、検索できるか試したいなと思っています。

