Langfuse 打爆两台机器:一条分位数 SQL,在 400G 表上扫了 8 分钟

📝 摘要 :两台机器隔 13 天各报一次 CPU 告警,查下来是同一条 SQL 的两端。Langfuse dashboard 算 duration 分位数,在 400GB / 6600 万行的 observations 上全表扫,稳定 125MB/s 读盘------正好是 AWS gp3 默认吞吐上限。加 24h 过滤也没用:traces_view 是跨 observations 聚合重建的视图,duration 根本不是列,索引救不了。止血靠一条 kill 语句,根治靠升 v3 把观测数据搬去 ClickHouse。
7 月 1 日,A 机器 CPU 告警,92%。查出来是 Langfuse 容器吃满了。

7 月 14 日,B 机器 CPU 告警,97%。查出来是 PostgreSQL 在全表扫。

两台不同的机器、两个不同的进程、隔了整整 13 天------直到把两份现场摆在一起,才发现它们是同一条 SQL 的头和尾。

这篇讲怎么把两个看起来无关的告警拼成一个根因,以及一个更值得记住的教训:行存数据库上跑分位数聚合,加索引是救不回来的。


一、问题现象:两次告警,两台机器

1.1 第一次:应用机 CPU 92%

text 复制代码
【云监控】主机告警
· 实例:ai-node-A
· 报警规则:CPU.total
· 监控指标:(Agent)cpu.total
· 报警条件:连续满足 3 次,平均值 >= 90%
· 当前值:92.07%
· 持续时间:2 分钟

这台机器上跑着一堆 AI 相关的容器:推理服务、对话服务、Java 后端、Nacos,还有一个 Langfuse(LLM 可观测平台,用来记录 prompt / 回复 / token / 耗时)。

说来惭愧,这已经是 Langfuse 第二次进我的复盘了。上一次是它的 Python SDK 把我坑了------CallbackHandler 每请求 new 一个、内部起线程还不回收,日均涨 445 个线程,最后 load 飙到 150(见 《load 飙到 150 但 CPU 不到 20%:一次 LangFuse 线程泄漏的深度复盘》)。那次是客户端的锅,这次是服务端的。

1.2 第二次:数据库机 CPU 97%

13 天后,另一台机器告警,而且级别更高:

text 复制代码
【云监控】主机告警
· 实例:ai-node-B
· 报警规则:CPU.total
· 报警条件:连续满足 5 次,平均值 >= 95%
· 当前值:97.62%
· 持续时间:4 分钟

这台是数据库机,跑 PostgreSQL。2 核。

当时的直觉是「又一起独立的 CPU 告警」。后面会看到,这个直觉错得很彻底。


二、第一现场:从 top 一路收敛到容器

2.1 top:一个 Node 进程吃了 91%

text 复制代码
top - 02:48:46 up 54 days,  4 users,  load average: 1.35, 2.00, 1.92
%Cpu(s): 57.4 us, 10.3 sy,  0.0 ni, 30.8 id,  0.0 wa,  0.0 hi,  1.5 si

  PID USER   PR  NI    VIRT    RES   S  %CPU  %MEM     TIME+  COMMAND
 1327 1001   20   0   24.0g   3.3g   R  91.4  21.6   6d+8h    next-server
 1097 root   20   0 1238716  16152   S  22.3   0.1   23,05    containerd-shim
30861 root   20   0 1597432 170100   S  11.0   1.1   19,37    uvicorn
35592 root   20   0 7811992   1.8g   S   9.6  11.5   3:44.90  java

这里先纠正一个我自己的初判:CPU 榜首 next-server 不是 Python,是 Node.js 。Langfuse 是个 Next.js 应用,跑起来的进程名就叫 next-server。

一个可复用的小习惯:top 里看到不认识的进程名,别急着按语言归类。next-server / uvicorn / java 分属三种运行时,猜错一次就会往错误的方向准备排查工具(我差点直接上 py-spy)。

⚠️ 这里有个容易看岔的口径问题 :进程那列的 %CPU 是相对单核 的(91.4% ≈ 吃满 0.9 个核),而顶部 %Cpu(s) 那行才是整机 口径。所以「91.4%」和「30.8% id」并不矛盾------这台机器不止一个核。另外这份 top 是事后补抓的(时间戳 02:48),和触发告警的那一刻不是同一时间,别拿两个数字直接对账。

真正说明问题的是「谁排第一」,不是那个百分比。

2.2 docker stats:实锤,顺带一条被低估的线索

top 只告诉你哪个进程,docker stats 才告诉你哪个容器:

text 复制代码
CONTAINER     NAME                CPU %     MEM USAGE / LIMIT     NET I/O
3b0f250ab10e  ai-chat-service     14.81%    222.4MiB / 15.27GiB   0B / 0B
b337fb63d325  ai-backend          14.26%    1.901GiB / 15.27GiB   0B / 0B
225212d011e1  langfuse            93.29%    3.367GiB / 15.27GiB   940GB / 660GB
5c3f073911ac  py-infer-textcls     0.14%    2.474GiB / 15.27GiB   0B / 0B
b9a0e6f49e36  nacos                0.35%    986.1MiB / 2GiB       5.34GB / 3.72GB

langfuse CPU 93.29% ,和 top 里 next-server 的 91.4% 对得上。凶手确认。

但真正值钱的是最后一列 :Langfuse 的 NET I/O 是 940GB / 660GB------全场唯一网络吞吐上百 GB 的容器,其它要么是 0,要么个位数 GB。

当时我的判断是:Langfuse 靠 HTTP 接收 trace 上报,这么大的流量说明上游在疯狂灌数据,怀疑是重试风暴或者采集率开太大。

这个判断是错的 ,而且错得很有代表性:我看对了「流量主要是进来的」,却想当然地认定「进来的东西 = 上游上报的 trace」------流量的方向和流量的来源,是两个问题。真相要等 13 天后才揭晓。


三、第二现场:数据库侧抓到全文 SQL

13 天后 B 机器告警,这次直接查数据库当前在跑什么。PostgreSQL 排查慢查询的第一命令永远是 pg_stat_activity------零配置、实时、不用提前开任何东西:

bash 复制代码
docker exec -it postgres psql -U postgres -c "
SELECT pid, now() - query_start AS dur, wait_event, left(query, 40) AS query
FROM pg_stat_activity
WHERE datname='langfuse' AND state='active'
  AND now() - query_start > interval '30 seconds'
ORDER BY dur DESC;"

抓到的东西让人精神一振:

text 复制代码
  pid   |       dur       |  wait_event  |                  query
--------+-----------------+--------------+------------------------------------------
 772778 | 00:08:59.281651 | DataFileRead | SELECT percentile_disc(0.5) within group
 772801 | 00:08:59.281651 | DataFileRead | SELECT percentile_disc(0.5) within group
 772800 | 00:08:59.281651 | DataFileRead | SELECT percentile_disc(0.5) within group
(3 rows)

SQL 全文(Langfuse dashboard 算「各 trace 的耗时分位数」用的):

sql 复制代码
SELECT percentile_disc(0.5)  WITHIN GROUP (ORDER BY "duration") AS "percentile50Duration",
       percentile_disc(0.9)  WITHIN GROUP (ORDER BY "duration") AS "percentile90Duration",
       percentile_disc(0.95) WITHIN GROUP (ORDER BY "duration") AS "percentile95Duration",
       percentile_disc(0.99) WITHIN GROUP (ORDER BY "duration") AS "percentile99Duration",
       t."name" AS "traceName"
FROM traces_view t
WHERE t."timestamp" > $1 AND t."timestamp" < $2 AND t."project_id" = $3
GROUP BY t."name"
ORDER BY percentile_disc(0.95) WITHIN GROUP (ORDER BY "duration") DESC NULLS LAST;

三行、三个 pid、同一条 SQL、dur 完全相同------这是一条查询 + 两个并行 worker 。跑了 8 分 59 秒 还没结束,全卡在 DataFileRead(等磁盘)。

再看库有多大:

bash 复制代码
docker exec -it postgres psql -U postgres -d langfuse -c "
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS size, n_live_tup
FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 5;"
text 复制代码
    relname       |  size  | n_live_tup
------------------+--------+------------
 observations     | 400 GB |   66000000
 traces           |  21 GB |    2800000
 scores           | 144 kB |          0
 api_keys         | 160 kB |          0

observations 400GB、6600 万行。 一个「顺手装的可观测组件」,悄悄攒下了这台机器上最大的一张表。

PostgreSQL 更多排查命令(\d、pg_stat_*、慢日志开关等)见 《PostgreSQL 常用命令速查:MySQL 用户平滑上手》。


四、拼图合上:两个告警是同一条 SQL 的两端

摊开两次现场,一件事变得很明显:Langfuse 应用在 A 机器,它的 PostgreSQL 在 B 机器。

图:Langfuse dashboard 分位数查询的两端------应用机 CPU 93% 与数据库机全表扫,是同一条 SQL 的头和尾

告警 机器 现象 它在整条链路里的角色
第一次(7/1) ai-node-A Langfuse 容器 CPU 93%、NET I/O 940GB 应用端:发起查询 + 接收结果行
第二次(7/14) ai-node-B PG leader + 2 worker 全表扫、125MB/s 数据库端:真正执行全表扫

回头看第二章那条「被低估的线索」:940GB 的网络流量不是上游灌进来的 trace,是全表扫的结果行从 B 机器传回 A 机器。

先把 NET I/O 的口径说清:它不是合计,是「收 / 发」两个方向------

NET I/O: The amount of data the container has received and sent over its network interface. ------ Docker 官方文档:docker container stats

940GB / 660GB 就是「收 940GB、发 660GB,入站是大头」------这一点我当时没看错。

扎心的是:方向在这里根本救不了我。 「上游疯狂灌 trace」和「查询结果行回传」------这两种猜测都预测入站是大头,940GB 这个数字同时符合两者。我看到入站大,顺手就套上了「Langfuse 靠 HTTP 收 trace」这个先入为主的模型,而它恰好自洽,于是就不查了。

能一刀切开这两种猜测的,只有一个问题:对端是谁。 如果这 940GB 主要来自 PostgreSQL 那个 5432 连接,那它就绝不可能是 trace 上报------ss -tnp 看一眼就完事,13 天的弯路当场省掉。


五、根因分析:三个「本该没事」叠成了事故

事故很少是单一原因。这次是三层,每一层单独看都不致命。

5.1 为什么加了 24h 过滤,还是全表扫?

这是最反直觉、也最值钱的一问。

Dashboard 默认只看近 24 小时,SQL 里明明白白写着 WHERE t."timestamp" > $1 AND t."timestamp" < $2。按常理,只要 timestamp 上有索引,就该走索引范围扫、只读最近一小撮数据。

但它扫了全表。 一开始我列了三种可能:没索引 / 统计信息过时 / 数据量真大。查了 Langfuse 官方文档才发现,真正的答案是第四种,而且加索引根本救不了:

The legacy "traces view" is expensive to query at scale because it must reconstruct trace-level fields by aggregating across all observations belonging to a trace. ------ Langfuse 官方文档:Dashboard Changes in v4

关键在于 FROM traces_view 的这个 traces_view 不是表,是视图 。而排序用的 duration,根本不是 traces 表的列------它得把这条 trace 名下所有 observations 捞出来聚合,才能算出「这条 trace 耗时多久」。

于是链条变成:

text 复制代码
想算 duration 的分位数
  → 必须先有每条 trace 的 duration
    → 必须把该 trace 名下所有 observations 聚合一遍
      → observations 有 400GB / 6600 万行
        → 时间过滤挡住的是「哪些 trace 参与统计」
          → 挡不住「算 duration 要读多少 observations」

timestamp 索引能帮你少选几行 trace,但省不掉重建 duration 的那次聚合。 这就是为什么「加个索引」在这里是无效解------你要索引的那一列,压根不存在于磁盘上。

再叠上 percentile_disc 本身的性质:它是 ordered-set aggregate(有序集聚合),语义就是「把整组值排好序,取第 N 个位置」。分位数天然需要看到全量数据 ,不像 count / sum 可以边扫边累加、提前结束。四个分位数(P50/P90/P95/P99)还要在同一组上各算一遍。

一句话:这是一个不折不扣的 OLAP 查询,跑在一个 OLTP 数据库上。

5.2 为什么偏偏是 125MB/s?

三个进程合计读盘速率稳定在 125MB/s。这个数字太整了,整到不像巧合。

查了下:AWS gp3 云盘的默认基线吞吐就是 125 MiB/s(默认还有 3000 IOPS),要更高得单独花钱买。

gp3 volumes deliver a consistent baseline throughput performance of 125 MiB/s ... a baseline performance of 3,000 IOPS and 125 MB/s at any volume size. ------ AWS 官方文档:Amazon EBS General Purpose SSD volumes

所以这条查询不是被 CPU 卡住的,是被磁盘吞吐焊死的 。wait_event = DataFileRead 已经写在脸上了。

这个认知直接改变了后续决策:

  • ❌ 换台 CPU 更强的机器没用------瓶颈不在 CPU。
  • ❌ 加内存也基本没用------400GB 的表,加 8G、16G 缓存都是杯水车薪。
  • ✅ 要么提高 gp3 的吞吐配额,要么根本别扫这么多数据。

💡 数字是有指纹的。看到 125MB/s、1Gbps、64KB 这种「过于整齐」的数值卡点,八成不是巧合,而是撞上了某个默认配额上限。把它当成一条线索去查,往往一查一个准。

5.3 为什么并行反而更糟?

PostgreSQL 从 10 开始默认开并行查询,max_parallel_workers_per_gather 默认是 2------也就是一条大查询默认会拉起 1 个 leader + 2 个 worker。

在一台 32 核的机器上,这是好事。在这台 2 核机器上,这是灾难:

单进程扫 leader + 2 worker
占用 CPU 1 核(还剩 1 核干正事) 3 个进程抢 2 个核
磁盘读 一路顺序读 三路读,在 125MB/s 天花板下互相抢带宽
查询变快了吗 --- 没有,瓶颈是磁盘不是 CPU

并行扫描的前提是「CPU 有富余、磁盘还没到顶」。这两条这台机器一条都不满足,于是并行只带来了坏处:查询没快,整机却被拖死了------包括同机的其它业务容器。


六、解决方案

6.1 止血:一条命令杀掉所有卡住的慢查询

再次告警时抓到的不止一条,是十几个进程、六类查询在并发全表扫(有人开着 dashboard,多个图表同时加载)。一条条杀来不及:

bash 复制代码
docker exec -it postgres psql -U postgres -c "
SELECT pid, pg_terminate_backend(pid), left(query,60) AS killed_query
FROM pg_stat_activity
WHERE datname='langfuse' AND state='active'
  AND pid <> pg_backend_pid()
  AND now() - query_start > interval '30 seconds'
  AND query ILIKE 'SELECT%';"

三个安全阀,缺一不可:

条件 作用
datname='langfuse' 只动这个库,不误伤同实例的其它库
pid <> pg_backend_pid() 不把执行这条命令的自己杀掉
query ILIKE 'SELECT%' 只杀读查询,不碰正在摄取数据的 INSERT

⚠️ 杀完记得让人把 dashboard 页面关掉。前端会自动重发请求,页面开着就等于有个人在旁边不停帮你把查询重新拉起来------杀完没几分钟又冒出来一批,多半就是这个原因。

杀 leader 时两个 worker 会跟着退,不用单独处理。

6.2 持久化兜底:设一次,以后自动挡

止血是手动的,得让它下次自己挡住:

bash 复制代码
# ① 关并行全表扫(2 核 + 磁盘已到顶,parallel seq scan 是纯亏)
docker exec -it postgres psql -U postgres -c \
  "ALTER SYSTEM SET max_parallel_workers_per_gather = 0;"

# ② 让 PG 重读配置,不用重启数据库
docker exec -it postgres psql -U postgres -c "SELECT pg_reload_conf();"

# ③ 确认生效
docker exec -it postgres psql -U postgres -c "SHOW max_parallel_workers_per_gather;"

# ④ 只给 langfuse 库挂语句超时,慢查询 90 秒自尽
docker exec -it postgres psql -U postgres -c \
  "ALTER DATABASE langfuse SET statement_timeout = '90s';"

⚠️ statement_timeout 千万别设成全局 。同实例其它库可能有正常的长事务(批量导入、维护作业),一刀切会误杀。ALTER DATABASE 精确挂到问题库上,代价最小。

⚠️ 但 ALTER DATABASE 只对「新建立的连接」生效 ------它在建立连接时才被读进 session。Langfuse 用连接池,池里那些老连接会一直用着旧配置,你 SHOW 出来是 90s,实际卡住的查询照样跑满。设完必须重启一次应用容器(或等池子里的连接自然轮换),否则你会以为兜底生效了,其实一次都没触发过。

6.3 根治:把观测数据搬去列存

止血和兜底都只是「让它慢得没那么伤人」,没解决「用行存数据库跑 OLAP 聚合」这个根本错配。

正好 Langfuse 官方也是这么想的------v3 把 traces / observations / scores 整体搬到了 ClickHouse,PostgreSQL 只留事务和配置数据:

(大意) Langfuse 把 traces、observations、scores 从 Postgres 迁到 ClickHouse,因为无论官方还是自部署用户,在数据量到百万行级别时都在 Postgres 上遇到了瓶颈------摄取和查询两头都卡。 ------ 译自 Langfuse 博客:From Zero to Scale, Langfuse's Infrastructure Evolution

为什么列存能根治,就看这条查询要什么:

PostgreSQL(行存) ClickHouse(列存)
算 duration 分位数要读什么 整行所在的数据页------不到约 2KB 的 prompt / 回复内联在行里,跟着一起读 只读 duration 一列
压缩 只压超过约 2KB 的大字段(TOAST),普通列不压 列内同构,400GB 大概率压到几十 GB
聚合方式 逐行处理 向量化批量处理
量级 本次:125MB/s 啃 8 分钟还没完 同类查询典型:亚秒到几秒

差距不是「快一点」,是数量级。 行存要把整头牛拖出来才能称一称牛角有多重;列存直接把牛角单独放在一个筐里。

最终方案定为:不迁旧数据,在独立机器上全新搭一套 v3。理由三条:

  1. 避开 400GB 迁移------那是最费时、最容易出岔子的一环,而且迁过去还是那么大。
  2. 旧观测数据本就低价值------它的价值随时间快速衰减,成本却永远累加。重搭等于做了一次隐式 retention。
  3. 顺便完成物理隔离------Langfuse 全家桶(web / worker / PG / Redis / ClickHouse / MinIO)搬到专用机,再也炸不到 AI 业务。

v3 具体怎么搭、应用怎么切、上线后 ClickHouse 又怎么涨起来的,单独写成了一篇:【待发布后补充引用关系:《Langfuse v3 独立部署:CK 三周涨到 430G,PG 只剩 1G》】。

这里的 ClickHouse 是给 Langfuse 专用的独立实例 ,不是骑到现成的数仓 CK 上。共用会把「Langfuse 拖垮邻居」的老问题原样复制一遍------只是邻居从 AI 服务换成了数仓。真要共用,至少得建独立库 + 用 SETTINGS PROFILE 限死内存和线程,做法可参考 《1800 行 INSERT 跑了 16 分钟:ClickHouse 并发洪流踩坑 + 多账号资源隔离方案》。ClickHouse 本身怎么搭见 《ClickHouse 25.4 从裸奔到全副武装》。


七、还没根治之前,retention 是必答题

哪怕不升 v3,周期清理也是必须做的------这是很多人会犹豫的一步,所以把账算清楚:

删了会丢什么? 只会失去「翻旧账」的能力:查不到保留窗口之外的单条 trace、历史趋势只能看窗口内。

删了不会丢什么? 业务数据一条都不少。observations / traces 存的是 prompt、模型回复、token、耗时、成本,是排查和看监控用的副本 ;用户、会话、订单、支付这些真业务数据在别的库里,跟 Langfuse 一点关系没有。用户和产品对这次删除完全无感。

不删会怎样? 表只增不减 → dashboard 越来越慢 → 反复触发 CPU / iowait 告警 → 磁盘迟早写满。成本永远累加,问题只会更严重,不会自愈。

几个实操要点:

  • 是「周期删」不是「删一次」 。删一次,三个月后原样长回来,所以要上 cron。⚠️ 别指望升 v3 后用内置的 Data Retention 省事:它在自托管场景下只属于企业版(官方文档),开源版还得自己清。
  • 删除有外键顺序 :scores → observations → traces,反了会报外键约束。
  • 删完用 VACUUM,别上 VACUUM FULL 。VACUUM FULL 要锁表 + 需要约 2 倍空间重写,在 400GB 的表上等于二次事故。普通 VACUUM 不会把空间还给操作系统,但会标记为可复用------对这种持续写入的表足够了,新数据会填回去(PG 为什么删了数据表却不变小,见【待发布后补充引用关系:《MySQL DBA 转 PostgreSQL 的十个认知差:不是语法不同,是模型不同》】差 8)。
  • 有合规 / 审计留存要求的,别硬删,导成 Parquet 归档到对象存储,PG 只留热数据。

保留窗口建议 30 ~ 90 天,看你实际往回查多远。


八、举一反三

经验 1:两个告警,可能是一个根因

这次最大的教训不是技术上的,是排查方法上的:我把 7/1 和 7/14 当成两起独立事件,各查各的,中间隔了 13 天。

如果第一次就问一句「这个容器的 940GB 流量,对端是谁」,链路当场就能连起来。

分布式环境下,一个根因产生 N 个告警是常态 。看到告警别急着单点归因,先问三件事:这个组件的上下游是谁 ?告警时间线 对得上吗?流量的对端是谁?

方向是最便宜的线索,也常常是最没用的------对立的几种假设往往共享同一个方向。能证伪的是对端(ss -tnp、iftop)。

经验 2:这是「OLAP 混进 OLTP」的镜像面

我之前写过一篇 《OLAP 数据库混进 OLTP 链路:一次 ClickHouse 拖死 API 101 分钟的复盘》------那次是把 OLAP 数据库(ClickHouse)塞进了 OLTP 主链路,一个分析库拖垮了在线接口。

这次正好反过来:把 OLAP 查询(分位数聚合)压在了 OLTP 数据库(PostgreSQL)上。

上一篇 本篇
错配方向 OLAP 数据库 进了 OLTP 链路 OLAP 查询 压在 OLTP 数据库上
谁被拖垮 在线 API(连接池耗尽) 整台机器(磁盘吞吐打满)
表面症状 接口超时 CPU 告警

症状完全不同,病根是同一个:分析型负载和事务型负载没有隔离。 一枚硬币的两面。

所以那条通用原则可以更完整地表述为:要隔离的不是「数据库」,是「负载类型」。 只盯着「别把 CK 接进主链路」是不够的------你在 PG 上写一条 percentile_disc,本质上做的是同一件事。

经验 3:cpu.total 是个会骗人的指标

两次告警都是 cpu.total 触发的,但它们的性质完全不同:A 机器是真的在烧 CPU(Node 进程算数据),B 机器其实是在等磁盘 (DataFileRead)。

cpu.total 把 CPU busy(us + sy) 和 iowait 混成了一个数。看到「CPU 95%」,你的第一反应会是「谁在算」,于是一头扎进 CPU 火焰图------而真相是「谁在等盘」。

建议把 CPU 告警拆成两个指标:us + sy 和 iowait,分别设阈值。 一个指标只回答一个问题,排查方向才不会一开始就被带偏。iowait 高到什么程度会把机器拖进「假死」,见 《一条 bzcat,把线上机器查到假死的复盘》。

经验 4:可观测组件是「隐形的资源大户」

Langfuse 这类组件的装机成本极低------一个 docker run 就完事,通常也不会进架构评审。但它的数据只进不出(第七章),查询又天生是 OLAP(dashboard 全是聚合和分位数),于是几个月后,你的「监控工具」变成了整台机器上最大的表、最重的查询、以及第一个把 CPU 打满的进程。

给一切自带存储的可观测组件配 retention,把它当上线检查项。 装的时候多花 5 分钟,比事后删 400GB 便宜得多。

经验 5:非核心组件必须物理隔离

Langfuse 是 fail-open 的------它挂了,AI 服务照常跑,只是不上报 trace。这么一个「挂了也无所谓」的组件,却有能力把整台机器上的核心服务一起拖死。

这不是第一次了。我还写过一起 SkyWalking 的积压事故------OAP 和 Elasticsearch 挤在同一台机器,把 page cache 抢光了,ES 段合并被迫去读物理盘,磁盘直接打满。详见 《SkyWalking 第三次积压超 3 亿:分片又小又匀,却只有一台机在扛》。症状完全不同(那次抢的是内存),病根一模一样:非核心组件和核心服务混部,没有资源隔离。

抢 CPU、抢 page cache、抢磁盘吞吐------形式各异,结论一致:

可观测 / 中间件类组件独立部署,或至少用 cgroup 限死资源(docker update --cpus=2)。 判断标准很简单:如果这个组件挂了业务不受影响,那它就更没有资格把业务拖挂。


九、延伸阅读

同一个 Langfuse

同一个病根,不同的现场

工具与底座

官方文档

相关推荐
笃行3502 小时前
列存储这条线,我顺着 KingbaseES 的文档摸了一遍
数据库
Navicat中国2 小时前
Navicat 18 支持 Azure OpenAI 接入,合规 AI 赋能 SQL 开发
数据库·人工智能·microsoft·ai·azure·navicat·数据
1024奇点2 小时前
【仓颉语言入门 · 第二篇 第 10 课】
开发语言·数据库·ide·mysql·开源
数据狐(Datafox)2 小时前
一文读懂京东商品详情API:返回字段解读与 JSON 样例
java·开发语言·数据库·windows·json
SelectDB2 小时前
从实时入库到实时可用:Apache Doris 5.0 增量计算场景与实践
大数据·数据库·开源
IvorySQL3 小时前
PostgreSQL 18 性能与执行计划新特性实测
数据库·postgresql
赵渝强老师3 小时前
【赵渝强老师】高斯数据库(openGauss)的服务器日志文件
数据库·opengauss·国产数据库·高斯数据库
SelectDB3 小时前
以实时驱动智能:Apache Doris Summit 2026 盛大落幕,开启 Agentic AI 时代数据新范式
数据库·程序员·agent
Source.Liu3 小时前
【A11】服务器数据库结构笔记
服务器·数据库·笔记