20260603.记一次 Doris FE “幽灵卡死”引发的惨案:从表象到真凶的追凶实录

20260603.记一次 Doris FE "幽灵卡死"引发的惨案:从表象到真凶的追凶实录

作为大数据团队的打工人,最怕的不是系统报错,而是系统"不报错,直接死给你看"。最近,我们负责的 Apache Doris 集群就患上了一种让人抓狂的"月经病"------每隔一两个月,Doris FE(Frontend)就会在深夜毫无征兆地彻底卡死,指令下发不了,集群形同瘫痪。

本以为这是个隐藏极深的底层 Bug,没想到一番抽丝剥茧后,却牵扯出了我们对 CBO(基于成本的优化器)和后台任务调度的深刻教训。


第一幕:案发现场的迷雾

案发当晚 23:55,报警群炸锅。Doris 集群再次陷入"假死"状态。

  • 现象描述: 所有的 DDL(如建表、修改表)和查询指令被堵在门外,BE(Backend)仿佛失联,系统完全停止响应。
  • 初步怀疑:GC(垃圾回收)灾难。
    这通常是第一反应。毕竟 Java 写的系统,遇到这种大面积的 STW(Stop-The-World),大概率是堆内存被打爆了。于是,我立刻捞出了当时的 fe.gc.log

然而,日志给了我结结实实的一巴掌:

text 复制代码
    
    
    
  [2026-06-03T23:00:34.052+0800] GC(23091) Pause Young (Mixed) (G1 Evacuation Pause) 2123M->1663M(12288M) 119.895ms

堆内存健康得不能再健康了!在 12GB 的总内存中,回收后常态只占用 1.6GB,完全没有 OOM 的迹象,停顿时间也不过是一百多毫秒。GC 灾难的嫌疑被彻底洗清。

  • 再次怀疑:NFS 磁盘 I/O 阻塞。
    因为我们的 doris-meta 挂载在阿里云的 NFS 上。根据经验,NFS 偶尔的网络抖动会导致 fsync 刷盘极慢,一旦主线程刷盘被卡,所有的心跳和元数据操作都会排队,这也能造成"假死"。
    我满怀信心地去 grep fe.warn.log 寻找 fsync 相关的超时报错,结果------干干净净,没有一条相关日志。

这也不对,那也不对,究竟是谁捂住了 Doris 的嘴?


第二幕:剥开伪装,直指真凶

既然底层资源没问题,那就一定是在业务层面出了幺蛾子。我决定硬啃 fe.warn.log 中杂乱的报错信息,终于,几条被淹没的关键日志浮出水面。

线索一:RPC 队列被打爆

text 复制代码
    
    
    
  2026-06-03 23:26:43,742 WARN (thrift-server-pool-7|10404) [ReportHandler.putToQueue():228] the report queue size exceeds the limit: 100. current: 101

铁证如山!FE 用来接收 BE 心跳和报告的 thrift-server-pool 队列(默认大小 100)被完全打满了。

一旦这个队列溢出,BE 发送的心跳 FE 收不到,FE 自然认为 BE 失联,进而引发大面积瘫痪。

线索二:漫长的"毒瘤"查询被强杀

text 复制代码
    
    
    
  [ConnectContext.checkTimeout():995] kill query timeout, remote: ... query timeout: 900000...

大量的查询跑了整整 15 分钟(900000 毫秒)才被系统强杀。这说明在卡死前,BE 正在负重前行,处理着极其沉重的烂 SQL。

但问题来了,我去 BE 看了一圈,CPU 和内存波澜不惊,更离谱的是,fe.audit.log 里根本查不到这几条 15 分钟超时的 SQL!

线索三:疯狂的后台 Analyze 任务

text 复制代码
    
    
    
  [BaseAnalysisTask.runQuery():313] Failed to execute sql SELECT CONCAT... SUM(t1.count) * COUNT(t1.column_key)... FROM amzn_cleaned_report_reserved_inventory_bak...

就在系统濒临崩溃的 23:55,Doris 居然在后台疯狂生成几十组重度的 SELECT ... GROUP BY 聚合查询,对我们的千万级核心表执行全表扫描!

破案了!这是一场由并发风暴和元数据锁争用引发的惨案。

原来,那些 15 分钟超时的查询,根本连物理执行计划都没下发到 BE,它们死在了 FE 的"娘胎"里 。由于瞬间爆发的几十个 Auto Analyze 任务疯狂抢占 FE 的元数据锁,导致正常的业务查询连"读锁"都拿不到,只能在 FE 内部傻等。与此同时,锁竞争导致处理 BE 心跳的线程也被阻塞,最终 report queue 被塞满,系统彻底失联。


第三幕:自救与根除

找到原因后,我们要解决的就是"如何消除并发风暴"。

起初,为了缓解队列打爆的问题,我打算修改 fe.conf

properties 复制代码
    
    
    
  report_queue_size = 300thrift_server_max_worker_threads = 8192

但这只是"治标",扩大等位区确实能让系统在遭遇并发尖刺时喘口气,不至于立刻死掉,但治不了产生尖刺的根。

真正的根源在于 Auto Analyze(自动统计信息收集)

为了避免 Analyze 影响业务,我第一反应是限定它的执行时间,比如只在凌晨 3 点到下午 2 点跑:

sql 复制代码
    
    
    
  SET GLOBAL auto_analyze_start_time = "03:00:00"; SET GLOBAL auto_analyze_end_time   = "14:00:00"; 

险些酿成大祸!

仔细复盘了我们的调度系统后,我惊出了一身冷汗:凌晨 1 点到 5 点,正是我们第三方海外仓链路(Scan -> ETL -> 清洗)和 ERP 补写数据最疯狂的时刻。

Doris 的 Auto Analyze 是由"表数据变更量超过阈值"触发的。如果允许在这段期间自动收集,写入大军刚越过阈值,系统就会立刻拉起成百上千个 Analyze 任务,直接和写入任务"撞车",那系统绝对会从"一个月死一次"变成"天天死"!

最终的治本方案,是加上并发限制的"紧箍咒":

sql 复制代码
    
    
    
  -- 强制限制后台统计分析的并发数SET GLOBAL auto_analyze_task_num = 2;

这一招"釜底抽薪",直接斩断了并发风暴的可能。无论后台积压了多少表需要统计,同一时刻只允许 2 个任务在跑,FE 门前变成了有序的排队叫号。


第四幕:反思与总结,为无知买单

复盘这次惊魂事件,最大的教训不是参数配错了,而是我们对现代数据库 CBO(Cost-Based Optimizer)优化器及其配套机制的理解过于粗浅

在传统的认知里,数据库只要建好索引、配好内存就能跑。但现代的 OLAP 引擎(如 Doris),由于要在几百亿数据中快速选择是用 Hash Join 还是 Broadcast Join,极度依赖于底层的统计信息(NDV、Min/Max、Null Count)。

这也就是 Auto Analyze 存在的意义------它是不知疲倦的"清道夫",默默地为优化器收集情报。

我们犯了三个致命错误:

所幸,惊吓过后,集群重归宁静。通过这次追凶实录,不仅彻底治好了 Doris FE 的"幽灵假死",也让我明白:驾驭复杂系统,不仅要会看 GC 和 I/O,更要理解隐藏在背后的调度机制和优化哲学。敬畏每一行默认配置,才能少背一点锅。

相关推荐
学以智用1 小时前
.NET Core 序列化 **超清晰完整版教程**
后端·.net
Java患者·2 小时前
Spring Boot 3 整合 Elasticsearch 8
spring boot·后端·elasticsearch
雪隐2 小时前
个人电脑玩AI01-让5060 Ti给你打工——Whisper语音识别篇(上)
人工智能·后端
我是一颗柠檬2 小时前
【Redis】主从复制Day9
java·数据库·redis·后端
侯盛鑫2 小时前
理解 RocksDB IngestExternalFile
数据库·后端
1368木林森2 小时前
【Spring源码17·完结篇】SpringBoot核心注解+高频坑点+失效场景万字全集!收官Spring全家桶源码系列
java·spring boot·后端
武子康2 小时前
Java-15 深入浅出MyBatis 分页与通用 Mapper 实战:PageHelper + tk.mybatis 从配置到分页查询
java·后端
CodeSheep2 小时前
胡彦斌都开始苦修Vibe Coding,还上架App Store,都卷到编程来了吗?
前端·后端·程序员
DongWook2 小时前
关于Harness Engineering的一次实践
前端·后端