LayerX、Langfuse 改善で ClickHouse 負荷低減
LayerX は Langfuse と ClickHouse を用いた LLM オブザーバビリティ基盤のボトルネックを特定し、トレース取り込み処理の最適化によりデータ遅延とストレージ負荷を大幅に解消した。
キーポイント
LLM 運用におけるトレース記録遅延の発生
バクラク事業部で LLM 機能が増加した結果、Langfuse へのトレース記録に最大 1 日近くの遅延が生じ、プロンプト改善作業に影響を及ぼしていた。
ボトルネックの特定:Redis と ClickHouse の非効率
Redis メモリ使用率の上昇に伴う滞留と、ClickHouse への書き込み処理における EFS の読み取り IO 増加という矛盾した現象が確認され、アーキテクチャ上の課題を浮き彫りにした。
トレース取り込み処理の改善と負荷低減
Langfuse worker と ClickHouse の処理フローを見直し、Redis キューの効率化およびストレージアクセス最適化を行うことで、CPU 使用率とデータ反映時間を劇的に短縮した。
セルフホスト環境でのスケーラビリティ実証
外部サービス依存を避けつつ、大規模なトレースデータを扱う際のインフラ設計における具体的な課題解決策と成功事例を示した。
ClickHouse の負荷要因
イベント取り込み時に特定レコードの検索を行う SELECT クエリが頻繁に発行され、ClickHouse が苦手とする処理により遅延が発生していた。
SELECT クエリのスキップ機能
環境変数で指定した日付以降に作成されたプロジェクトに対しては、取り込み時の ClickHouse 読み取りクエリをスキップする機能が実装されている。
環境変数設定による ClickHouse クエリスキップ
運用開始時から v3 を利用し S3 に全データが保存されているため、環境変数を調整して ClickHouse への SELECT クエリを不要な処理としてスキップした。
重要な引用
ピーク時はトレースの反映まで 1 日近くかかることもあり、プロンプト変更後のモニタリングや改善作業にも影響が出ている状況でした
EFS DataReadIOBytes が大幅に増えていることも判明しました。処理の内容はトレースの書き込みですが、EFS で増えているのは読み取りという少し不思議な状況です
ClickHouse はカラム単位の大量データ集計やスキャンには強い一方で、特定1レコードの検索や読み取りを大量に繰り返す処理は苦手としています。
この環境変数には YYYY-MM-DD 形式で日付を指定します。指定日以降に作成されたプロジェクトの場合、ClickHouse からの SELECT クエリがスキップされる仕様です。
環境変数には「Langfuse 運用開始日より前の日付」をセットしました
ClickHouse の苦手とするワークロードを取り除くことで、取り込み処理を大幅に高速化できました
影響分析・編集コメントを表示
影響分析
この記事は、LLM アプリケーションの運用規模が大きくなる際に直面する観測データ(トレース)の遅延問題を、具体的なインフラ構成とメトリクス分析を通じて解決した実例を示しています。特に、書き込み処理なのに読み取り IO が急増するという逆説的な現象を特定し解消した点は、ClickHouse や Redis を用いた大規模システム設計において非常に示唆に富む知見です。
編集コメント
LLM の運用規模が拡大する中で、観測データの遅延が改善サイクルを阻害するケースは多く見られます。本記事の Redis と ClickHouse を介した IO パターンの分析手法は、同様の課題に直面するエンジニアにとって極めて参考になる実践知です。
バクラク事業部 Platform Engineering 部 SRE グループの uehara です。
LayerX のバクラクでは、LLMOps プラットフォームとして Langfuse を採用しています。また、Langfuse のストレージには ClickHouse を使用しており、どちらもセルフホストで運用中です。詳細については、以下の記事も合わせてご覧ください。
バクラク内で LLM を活用した機能が増えるにつれ、Langfuse へのトレース記録に遅延が目立つようになりました。
お客様の利用に直接影響する事象ではありませんが、ピーク時にはトレースの反映まで約 1 日かかることもあり、プロンプト変更後のモニタリングや改善作業にも支障をきたす状況でした。
参考までに、トレースが取り込まれるまでの流れは以下の通りです。

- クライアントから Langfuse へトレースが送信される
- Langfuse はジョブを Redis キューへ積む
- Langfuse worker がジョブを取り出し、トレースを永続化する
当時の Redis メモリ使用率は次の通りで、アイテム数の急増に伴ってメモリ使用率が上昇しており、処理遅延による滞留が確認できます。
imageRedis メモリ使用率
キューを処理する Langfuse worker は CPU を使い切れておらず、流量が増える日中は 30% 台で頭打ちになりました。書き込み先ストレージである ClickHouse も CPU が 60% 前後で限界に達しています。
imageLangfuse worker CPU 使用率
imageClickHouse CPU 使用率
一方で、EFS の DataReadIOBytes が大幅に増加していることも判明しました。処理の内容はトレースの書き込みですが、EFS で読み取りが増えているのは少し不思議な状況です。
imageEFS DataReadIOBytes
Langfuse トレース取り込み処理の概要
まずは Langfuse の worker がトレースを取り込む処理を調査しました。記事の理解を容易にするため、ここでは用語を以下のように定義します。
- プロジェクト:トレースが保存される場所。バクラクでは製品や機能ごとに分割しています。
- トレース:LLM アプリケーションにおける 1 リクエストまたは 1 操作に対応する記録単位です。
- イベント:SDK から送信される、トレースの作成や更新などのデータです。「Observation」の種類である「Event」とは区別されます。
- レコード:ClickHouse に書き込まれる 1 行のデータ。同一トレースに対する複数のイベントをマージした結果が格納されます。
トレース取り込みの詳細な流れを確認すると、以下の処理が行われています。
- クライアント SDK からイベントが送信される
- イベント情報を S3 に保存し、worker が実行するジョブを Redis にキューイングする
- worker がジョブを受け取り、S3 から同一トレースの全イベント情報を取得してマージする
- ClickHouse を検索し、該当する既存レコードがあれば新イベント情報とマージする
- マージ結果を 1 レコードとして ClickHouse に書き込み、永続化する
トレース保存の際のポイントとなるのは、S3 のイベント情報や ClickHouse の既存レコードとのマージ処理です。特に 4 つ目のステップでは、ClickHouse に対して以下のクエリが実行されます。
SELECT *
FROM ${table}
WHERE project_id = {projectId: String}
AND id = {entityId: String}
${additionalFilters.whereCondition}
ORDER BY event_ts DESC
LIMIT 1 BY id, project_id SETTINGS use_query_cache = false;
これは特定の 1 レコードを検索する処理で、クエリキャッシュを無効化するオプションも付与されています。イベントが 1 件送られるたびにこのクエリが発行されるため、膨大な数の SELECT 実行が発生してしまいます。
ClickHouse はカラム単位での大量データ集計やスキャンには強い反面、特定の 1 レコードを検索・読み取る処理を大量に繰り返すことには苦手としています。今回の処理遅延の主な原因が、この SELECT クエリの増加によるものでした。
SELECT クエリのスキップによる負荷軽減
Langfuse worker では、この SELECT クエリを実行しないための環境変数 LANGFUSE_SKIP_INGESTION_CLICKHOUSE_READ_MIN_PROJECT_CREATE_DATE が用意されています。
// S3 が稼働し始めた日以降に作成されたプロジェクトについては、取り込みパイプライン内で ClickHouse への読み取りを行わないように設定します。
LANGFUSE_SKIP_INGESTION_CLICKHOUSE_READ_MIN_PROJECT_CREATE_DATE: z
.string()
.date()
.optional(),
https://github.com/langfuse/langfuse/blob/v3.119.0/worker/src/env.ts#L130-L135
この環境変数には YYYY-MM-DD 形式の日付を指定します。指定日以降に作成されたプロジェクトでは、ClickHouse からの SELECT クエリがスキップされる仕様です。
この設定が用意された背景には、Langfuse の v2 と v3 でトレースの取り込み処理が大きく異なる点があります。v2 ではイベント情報を PostgreSQL に直接書き込んでいましたが、v3 からは S3 を経由した非同期処理へと変更されました。
v2 から v3 へ移行した環境では、PostgreSQL 上のデータは ClickHouse へ移管されますが、S3 には v2 時代のイベント情報が残っていません。つまり、v2 時代に作成されたプロジェクトでトレースを更新する際、S3 のデータに加えて ClickHouse のレコードも合わせてマージする必要があります。
しかし、弊社では運用開始当初から v3 を利用しており、すべてのイベント情報が S3 に保存 *1 されています。そのため全プロジェクトで ClickHouse への SELECT クエリをスキップでき、環境変数には「Langfuse の運用開始日より前の日付」を設定しました。
設定変更後の効果
ClickHouse への SELECT クエリを停止したところ、すぐに効果が現れました。
Redis のメモリ使用率を確認すると、滞留していたアイテムが解消され、アイテム数が減少してメモリ使用率も急激に低下しています。
imageRedis メモリ使用率 (設定変更後)
後続処理の詰まりにより 30% 台で頭打ちになっていた Langfuse worker も、滞留したジョブを一気に消化した後は従来の半分以下の負荷に落ち着いています。
imageLangfuse worker CPU 使用率 (設定変更後)
ClickHouse の CPU 使用率も一桁台まで急減し、リソースに余裕が生まれました。この状態で安定的に運用できたため、その後はタスクのスケールダウンによりコストを最適化できています。
imageClickHouse の CPU 使用率(設定変更後)
EFS の DataReadIOBytes も、休日や深夜の閑散時と同程度の負荷に落ち着きました。
imageEFS の DataReadIOBytes(設定変更後)
まとめ
Langfuse でのトレース取り込み処理の遅延について調査した結果、ClickHouse への SELECT 処理をスキップすることで解消できました。
実施した変更は環境変数を 1 つ追加しただけですが、ClickHouse が苦手とするワークロードを取り除いたことで、取り込み処理を大幅に高速化できました。運用チューニングの事例として、少しでも参考になれば幸いです。
今回はセルフホストならではの深掘り事例でしたが、弊社では要件に応じてマネージドサービスも積極的に活用しています。
バクラク事業部の SRE チームでは、技術選定からパフォーマンス改善、開発プラットフォームの整備まで一緒に取り組める仲間を探しています。少しでもご興味のある方は、ぜひお話させてください!
AI カンファレンス「Bet AI Day 2026」を開催します!
AI は「使う」段階から「共に働く」段階へ。
2 回目となる「Bet AI Day 2026」は、"AI エージェントと働く未来に、Bet しよう"をテーマに、LayerX における AI エージェント活用の実践知などを余すことなく共有します。
昨年同様オンライン開催となりますので、全国どこからでも PC やスマートフォンで気軽にご視聴いただけます。AI エージェントと働く未来に興味のある方は、ぜひ「Bet AI Day 2026」にご参加ください!
「Bet AI Day 2026」開催概要
- 開催日:2026 年 9 月 3 日(木)12:00 開演
- 開催方法:オンライン配信
- 参加費:無料(事前申込制)
- お申し込み(connpass):https://layerx.connpass.com/event/399169/
*1: 保存先バケットでオブジェクトを削除するライフサイクルルールは設定していません
原文を表示
バクラク事業部 Platform Engineering 部 SRE グループの uehara です。
LayerX のバクラクでは、LLMOps プラットフォームとして Langfuse を採用しています。また、Langfuse のストレージとして ClickHouse を使用しており、いずれもセルフホストで運用中です。詳細については、以下の記事も合わせてご覧ください。
バクラク内で LLM を用いた機能が増えてきた頃、Langfuse へのトレース記録遅延が目立つようになりました。
お客様の利用に直接影響する事象ではありませんが、ピーク時はトレースの反映まで1日近くかかることもあり、プロンプト変更後のモニタリングや改善作業にも影響が出ている状況でした。
参考までに、トレースが取り込まれるまでの流れは以下の通りです。

- クライアントから Langfuse へトレースが送信される
- Langfuse はジョブを Redis キューへ積む
- Langfuse worker がジョブを取り出しトレースを永続化
当時の Redis メモリ使用率は次の通りで、アイテム数の急増に伴ってメモリ使用率が上昇しており、処理遅延に伴う滞留が確認できます。

キューを処理する Langfuse worker は CPU を使い切れておらず、流量が増える日中は 30% 台で頭打ちになりました。書き込み先ストレージである ClickHouse も CPU 60% 前後で頭打ちの状況です。


一方で、EFS DataReadIOBytes が大幅に増えていることも判明しました。処理の内容はトレースの書き込みですが、EFS で増えているのは読み取りという少し不思議な状況です。

Langfuse トレース取り込み処理の概要
まずは Langfuse worker のトレース取り込み処理を調査しました。説明簡略化のために、本記事では以下のように用語を使い分けます。
- プロジェクト: トレースの保管先。バクラクではプロダクトや機能単位で切っています
- トレース: LLM アプリケーションの1リクエスト/1操作に相当する記録単位
- イベント: トレースの作成/更新など SDK から送信されるデータ。Observation の種別である「Event」とは別です
- レコード: ClickHouse に書き込まれる1行。同一トレースに対する複数イベントをマージした結果
トレース取り込みの流れをもう少し詳しく見てみると、次の処理が行われています。
- クライアント SDK からイベントが送信される
- イベント情報を S3 へ保存し、worker 用のジョブを Redis に積む
- worker がジョブを受け取り、S3 から同一トレースのイベント情報を全取得してマージ
- ClickHouse を検索し、同一トレースの既存レコードがあれば新イベント情報とマージ
- マージ結果を1レコードとして ClickHouse へ書き込んで永続化
トレースを保管する際、S3 のイベント情報や ClickHouse の既存レコードとマージする点が重要です。特に4番の処理では、ClickHouse に対して以下のクエリが発行されます。
`
SELECT *
FROM ${table}
WHERE project_id = {projectId: String}
AND id = {entityId: String}
${additionalFilters.whereCondition}
ORDER BY event_ts DESC
LIMIT 1 BY id, project_id SETTINGS use_query_cache = false;
> https://github.com/langfuse/langfuse/blob/v3.119.0/worker/src/services/IngestionService/index.ts#L1310-L1318
特定の1レコードを検索する処理で、クエリキャッシュの無効化オプションも付いています。これがイベント1件ごとに毎回発行されるため、膨大な件数の SELECT が走ることとなります。
ClickHouse はカラム単位の大量データ集計やスキャンには強い一方で、特定1レコードの検索や読み取りを大量に繰り返す処理は苦手としています。今回の処理遅延の要因はこの SELECT クエリの増加にありました。
## SELECT クエリのスキップによる負荷軽減
Langfuse worker にはこの SELECT クエリをスキップするための環境変数 `LANGFUSE_SKIP_INGESTION_CLICKHOUSE_READ_MIN_PROJECT_CREATE_DATE` が用意されています。
> ```
// Set a date after which S3 was active. Projects created after this date do
// perform a ClickHouse read as part of the ingestion pipeline.
LANGFUSE_SKIP_INGESTION_CLICKHOUSE_READ_MIN_PROJECT_CREATE_DATE: z
.string()
.date()
.optional(),
https://github.com/langfuse/langfuse/blob/v3.119.0/worker/src/env.ts#L130-L135
この環境変数には YYYY-MM-DD 形式で日付を指定します。指定日以降に作成されたプロジェクトの場合、ClickHouse からの SELECT クエリがスキップされる仕様です。
本設定が用意された背景として、Langfuse v2 と v3 でトレースの取り込み処理が大幅に異なる点があります。v2 ではイベント情報を PostgreSQL へ直接書き込んでいましたが、v3 からは S3 を経由した非同期処理となりました。
v2 から v3 へ更新した環境では、PostgreSQL 上のデータが ClickHouse へ移行されるものの、S3 には v2 時代のイベント情報が残っていません。つまり、v2 時代に作成されたプロジェクトでトレースを更新する場合、S3 に加えて ClickHouse のレコードも含めてマージする必要があります。
しかし、弊社では運用開始時点から v3 を利用しており、S3 に全イベント情報が保存 *1 されています。全プロジェクトで ClickHouse の SELECT クエリをスキップできるため、環境変数には「Langfuse 運用開始日より前の日付」をセットしました。
設定変更後の効果
ClickHouse への SELECT クエリを止めたところ、すぐに効果が現れました。
Redis のメモリ使用率を見ると、滞留解消に伴ってアイテム数が減り、メモリ使用率も急減しました。

後続処理が詰まって 30% 台で頭打ちになっていた Langfuse worker も、滞留したジョブを一気に捌いたあとは従来の半分以下の負荷に落ち着いています。

ClickHouse は CPU 使用率が一桁台まで急減し、リソースに余裕が生まれました。この状態で安定的に運用できたこともあり、後日にはタスクのスケールダウンによりコストを最適化できています。

EFS の DataReadIOBytes も、休日や深夜の閑散時間帯と同程度の負荷に落ち着きました。

まとめ
Langfuse トレース取り込みの処理遅延について調査し、ClickHouse への SELECT 処理をスキップすることで解消しました。
行った変更自体は環境変数を1つ追加したのみですが、ClickHouse の苦手とするワークロードを取り除くことで、取り込み処理を大幅に高速化できました。運用チューニングの一事例として参考になりましたら幸いです。
今回はセルフホストならではの深堀り事例でしたが、弊社では要件に応じてマネージドサービスも積極的に活用しています。
バクラク事業部の SRE チームでは、こうした技術選定からパフォーマンス改善、開発プラットフォームの整備まで一緒に取り組める仲間を探しています。少しでもご興味のある方は、ぜひお話させてください!
AIカンファレンス「Bet AI Day 2026」を開催します!
AIは「使う」段階から「共に働く」段階へ。
2回目となる「Bet AI Day 2026」は"AIエージェントと働く未来に、Betしよう"のメッセージを軸に、LayerXにおけるAIエージェント活用の実践知などを余すことなく共有します。
昨年同様にオンラインでも開催しますので、全国どこからでも、PCやスマートフォンからお気軽にご視聴いただけます。AIエージェントと働く未来について興味のある方、ぜひ「Bet AI Day 2026」にご参加ください!
「Bet AI Day 2026」開催概要
開催日:2026年9月3日(木)12:00 開演
開催方法:オンライン配信
参加費:無料(事前申込制)
お申し込み(connpass):https://layerx.connpass.com/event/399169/
*1:保存先バケットでオブジェクトを削除するライフサイクルルールは設定していません
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み