首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Langfuse ClickHouse CPU 打满事件复盘

Langfuse ClickHouse CPU 打满事件复盘

作者头像
用户12023652
发布2026-09-06 11:23:04
发布2026-09-06 11:23:04
40
举报

日期:2026-09-04 | 机器:81.71.xx.xx(4C4G,Langfuse 专用机) 栈:Langfuse v4(web + worker 分离)+ ClickHouse 25.12 + Postgres/Redis/MinIO 本地配置源:F:\<user>\ai-daily\lighthouse\langfuse\ 关联文档:readme.md(🔧 ClickHouse CPU 打满一节)、技能 lighthouse-docker-ops T26/T27

摘要

2026-09-04,81.71.xx.xx 上的 Langfuse 栈 ClickHouse CPU 长期打满(实测 350%)。排查发现活跃 merge 全落在系统日志表而非业务表,err.log 记录 57,937 次 merge 失败。根因是 max_server_memory_usage 只给 700MB、低于真实工作集 1GB+,导致每次合并因内存不足失败并无限重试,失败又写 part_log 生成新 part,形成正反馈死循环。解法:内存上调至 1000MB、后台池 32→8、缓存收敛、关闭 5 张自监控日志表并清理历史垃圾 5.35GB。修复后 CPU 稳定 2~3%,磁盘回收至 81MB。核心教训:内存上限不可拍脑袋压低,ClickHouse 异常先看挂载出来的 err.log,docker logs 不全。


一、现象

用户报告两个问题:

  1. langfuse-web 容器持续显示 unhealthy,但页面实际能正常打开
  2. ClickHouse CPU 占满(用户侧观测约 99%)

重启栈之后实测,比报告的更严重:

代码语言:javascript
复制
docker stats(修复前)
langfuse-v2-clickhouse-1   CPU 350.28%   MEM 830MB / 1.75G
主机 load average: 7.40          ← 4 核机器,load 7.4 已严重过载
clickhouse 累计 CPU 时间 103 分钟(启动后不久)

注:用户看到的"99%"与实测 350% 不矛盾——前者多半是单核口径(htop 单核 100%), 后者是容器占满 3.5 个核。两者指向同一个事实:CPU 被吃满了。

同时 web 的 unhealthy 与 ClickHouse 问题相互独立(见第六节),本文主线是 CPU。


二、排查过程(按实际顺序)

第 1 步:活跃 merge 在忙什么

代码语言:javascript
复制
SELECT database, table, num_parts, progress, elapsed FROM system.merges;

结果出人意料:活跃 merge 全部落在 system.metric_log / system.part_log, Langfuse 业务表(langfuse.events_core / events_full)反而没有活跃 merge。 → 问题不是"业务数据量大",方向转向系统自身。

第 2 步:内存水位对不上

  • 查询 system.parts(纯元数据表)直接报 MEMORY_LIMIT_EXCEEDED
  • 实测 RSS 在 820MB ~ 1.04GB 之间震荡,而硬上限 max_server_memory_usage 只有 700MB
  • 连元数据查询都被 OvercommitTracker 杀掉 → 服务端处于持续内存超限抖动状态

第 3 步:err.log 给出决定性证据

⚠️ 排查弯路:docker compose logs看不到这些错误。 全量错误在挂载出来的 clickhouse-server.err.log(volume langfuse-v2_clickhouse_logs)里。

代码语言:javascript
复制
Code: 241 (MEMORY_LIMIT_EXCEEDED) 累计 10 万+ 条
其中 merge 失败 57,937 次,堆栈全部指向 MergeTask → 内存分配失败

第 4 步:默认配置三宗罪

读容器内 /etc/clickhouse-server/config.xml

实际值

问题

max_server_memory_usage

700MB(历史遗留)

远低于实际需要(1GB+)

uncompressed_cache_size

默认 8GB

机器总共 4G 内存,形同虚设

自监控日志表 ×4

全开,7.5 秒 flush 一次

metric_log/part_log/trace_log/text_log 高频写入

background_pool_size

32(配 ratio 拉起 48 个 merge 线程)

4 核机器 48 线程互相抢内存

第 5 步:磁盘佐证

langfuse-v2_clickhouse_data 卷占 5.35GB——绝大部分是系统日志表 churn 出来的垃圾。


三、根因:一条"合并失败 → 重试"的正反馈死循环

代码语言:javascript
复制
max_server_memory_usage = 700MB(太小)
        ↓
每次后台 merge 在内存分配阶段失败(Code 241)
        ↓
失败自动重试,48 个线程全部空转 → CPU 350%
        ↓
每次失败还往 system.part_log 写一条事件、产生新 part
        ↓
新 part 触发新的 merge 需求 → 更多失败 → 更多日志 → 循环放大

三个关键认知:

  1. CPU 打满 ≠ 数据量大。业务表只有个位数 part,烧 CPU 的是系统日志表的死循环。
  2. 内存上限不是越保守越安全。给 ClickHouse 的内存低于其真实工作集,merge 永远做不完, 结果比内存紧张本身更糟——空转烧 CPU。
  3. 700MB 这个值是 09-03 OOM 事故的遗留(当时为防 OOM kill 压到 700MB)。 防住了 OOM,防不住"慢性自杀"。

四、解决办法

4.1 配置修正(clickhouse-config/memory.xml + compose)

原值

新值

目的

max_server_memory_usage

700MB

1000MB

让 merge 能成功(治本)

compose mem_limit

1280m

1792m

容器配额 > 服务端上限,留出余量

background_pool_size

32(48 线程)

8(16 线程)

4 核机器砍掉无效并发

uncompressed_cache_size

8G

64MB

缓存收敛,不再画饼

part_log / metric_log / trace_log / text_log / async_metric_log

全开 7.5s

全部关闭

斩断正反馈环

ClickHouse 参数只能走 clickhouse-config/memory.xml(挂载到 config.d),不能用 command:。 config.d 的额外好处:配错了启动时报错,而不是静默不生效。

4.2 清理历史垃圾

关闭日志表配置后,把已禁用的旧系统日志表清掉:

代码语言:javascript
复制
磁盘:clickhouse_data 5.35GB → 81MB(根分区空闲 8.9G → 14G)
清理后业务表各只剩 1 个活跃 part,无排队 merge

4.3 过程中踩到的两个参数联动坑(差点以为改坏了)

报错

原因

处理

Code 115 启动失败

background_merges_mutations_concurrency_ratio 在 25.12 已被移除,写了直接不认识

删掉;25.x 合并并发只由 background_pool_size 控制

Code 36 启动失败

number_of_free_entries_in_pool_to_execute_mutation(默认 20) 必须 ≤ background_pool_size × ratio,池从 32 砍到 8 后校验不过

同步下调到 8。这族 number_of_free_entries_in_pool_to_* 参数是联动的,调池必查

4.4 验证不止于健康端点

  • OTLP 端点 /api/public/otel/v1/traces:无凭据返回 401、假 key 也返回 401 → 写入链路与鉴权均通
  • system.metrics 确认新内存上限生效
  • 连续观察:35 分钟零新增 Error(期间仅有的 26 条是我自己的诊断脚本没带凭据造成的认证失败,非服务问题)

五、效果

指标

修复前

修复后

ClickHouse CPU

350%

2 ~ 3%(多次采样 2.16 / 2.26 / 2.59 / 3.39 / 8.08%)

ClickHouse 内存

830MB(超限震荡)

~550MB(稳定在上限 55%)

主机 load average

7.40

0.05 ~ 0.28

err.log 新增错误

57,937 次 merge 失败

35 分钟零新增

磁盘占用

5.35GB

81MB

对外服务

health=200,OTLP 401(鉴权正常)

worker 偶现 ~75% CPU 是每分钟一次的定时作业瞬时峰值(Event Propagation / Backfill),非持续占用,不用管。


六、同场加映:web unhealthy 的真相(另一条独立线)

用户原判断是"健康检查命令不合适"——对了一半:服务确实好,但换命令治不好。

证据链:

代码语言:javascript
复制
netstat:Next.js 只监听 172.18.xx.xx:3000(容器自身 IP),没监听 127.0.0.1
printenv HOSTNAME = de25e9ef3e1a(Docker 注入的容器 ID)

Next.js standalone 启动时直接把 HOSTNAME 当绑定地址,而 Docker 把 HOSTNAME 设成了容器 ID。 于是:端口映射走 docker-proxy 转发到容器 IP,对外完全正常;容器内访问 127.0.0.1 必然 Connection refused → 健康检查恒失败。

修复(两处缺一不可):

  1. web 环境变量显式设 HOSTNAME: "0.0.0.0"
  2. 健康检查用 127.0.0.1 而非 localhost——镜像内 Node 24 对 localhost 会先试 ::1(IPv6),服务只听 IPv4,照样失败

七、经验与防再发清单

根因层

  • 给 ClickHouse 定内存上限前,先看真实工作集(RSS),上限 ≥ 工作集 + 余量;宁可给够,不可拍脑袋压低
  • 小内存机器(≤4G)部署 ClickHouse:主动关闭自监控日志表(part_log/metric_log/trace_log/text_log/async_metric_log),默认配置是给大机器写的
  • 缓存类参数(*_cache_size)按机器内存比例设,默认值多为大机器口径(uncompressed_cache 默认 8G)
  • background_pool_size 必查 number_of_free_entries_in_pool_to_* 一族联动,且确认所用版本的参数仍存在(25.12 已移除 concurrency_ratio)

排查层

  • ClickHouse 异常先看挂载出来的 err.logdocker logs 只有 stdout,不全
  • CPU 打满先查 system.merges + system.processes,分清"业务负载"还是"系统内耗"
  • 出现大量 Code 241 + merge 失败 → 直接怀疑内存上限过低,不是数据问题
  • 健康端点绿 ≠ 可用:验收必须做端到端探针(本例 OTLP 401 即链路通)

监控层

  • 部署后必查 dmesg(OOM kill 是静默的);本例无 OOM,是"慢性病"而非"猝死",更依赖错误日志巡检

附:本次产物与备份

位置

生效配置

服务器 /home/ubuntu/<user>/langfuse-v2/clickhouse-config/memory.xml

修改前备份

同目录 memory.xml.bak.20260904-*、docker-compose.yaml.bak.20260904-*

本地配置源

F:\<user>\ai-daily\lighthouse\langfuse\(compose + memory.xml + readme)

经验沉淀

技能 lighthouse-docker-ops(T25 / T26 / T27 + security-baseline)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-09-05,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 摘要
  • 一、现象
  • 二、排查过程(按实际顺序)
    • 第 1 步:活跃 merge 在忙什么
    • 第 2 步:内存水位对不上
    • 第 3 步:err.log 给出决定性证据
    • 第 4 步:默认配置三宗罪
    • 第 5 步:磁盘佐证
  • 三、根因:一条"合并失败 → 重试"的正反馈死循环
  • 四、解决办法
    • 4.1 配置修正(clickhouse-config/memory.xml + compose)
    • 4.2 清理历史垃圾
    • 4.3 过程中踩到的两个参数联动坑(差点以为改坏了)
    • 4.4 验证不止于健康端点
  • 五、效果
  • 六、同场加映:web unhealthy 的真相(另一条独立线)
  • 七、经验与防再发清单
  • 附:本次产物与备份
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档