版本: doris-4.0.5-rc01 (commit: 59de8c4c524)
影响: BE 反复段错误崩溃 (122次), audit_log 分区 p20260518 数据文件损坏, 查询间歇性返回 RPC UNAVAILABLE
一、问题现象
用户执行 SQL 查询时收到错误:
SQL 错误 [1105] [HY000]: RpcException, msg: send fragments failed.
io.grpc.StatusRuntimeException: UNAVAILABLE: io exception, host: 192.168.1.118
二、环境信息
集群配置
| 项目 | 值 |
|---|---|
| FE 版本 | doris-4.0.5-rc01 (commit: 59de8c4c524) |
三、完整时间线
阶段一: 文件损坏产生 (5/18)
5/18 09:34 — Compaction 产出损坏文件
文件路径: /data/doris/be/storage/data/194/1815518124650/173204708/
020000000000dc83844902009917fdfb91110df9bcffd0b4_0.dat
文件大小: 35,845,523 字节 (35MB)
创建时间: 2026-05-18 09:34
所属表: __internal_schema.audit_log
所属分区: p20260518 (Tablet ID: 1815518124650)
该文件是 compaction 合并输出 (远大于同目录下每分钟写入的 15KB-3MB 小文件)。
阶段二: ColumnReaderCache 崩溃 (5/22)
5/22 11:34 — 首次缓存相关崩溃
*** Aborted at 1779420889 (unix time) → 2026-05-22 11:34
*** SIGSEGV unknown detail explain (@0x0) received by PID 1842829
0# doris::signal::FailureSignalHandler
...
4# doris::segment_v2::ColumnReader::~ColumnReader()
at column_reader.cpp:294
5# doris::segment_v2::ColumnReader::~ColumnReader()
at column_reader.cpp:294
6# std::_Sp_counted_base::_M_release_last_use_cold()
at shared_ptr_base.h:198
7# std::_List_base<CacheNode>::_M_destroy_node()
at stl_list.h:845
8# doris::segment_v2::ColumnReaderCache::_insert_locked_nocheck()
at column_reader_cache.cpp:74
9# doris::segment_v2::ColumnReaderCache::get_column_reader()
at column_reader_cache.cpp:109
10# doris::segment_v2::Segment::get_column_reader()
11# doris::segment_v2::Segment::new_column_iterator()
at segment.cpp:679
12# doris::vectorized::VStatisticsIterator::init()
at vgeneric_iterators.cpp:54
...
17# doris::BetaRowsetReader::next_batch()
at beta_rowset_reader.h:116
18# doris::vectorized::VCollectIterator::Level0Iterator::next()
at vcollect_iterator.cpp:578
22# doris::vectorized::BlockReader::_direct_next_block()
at block_reader.cpp:268
关键: 崩溃发生在 BetaRowsetReader::next_batch() → ColumnReaderCache::get_column_reader() 路径上, 这是 compaction 读取阶段 的调用链。ColumnReaderCache 的 use-after-free bug 在缓存淘汰旧条目时触发空指针。
阶段三: PageCache 崩溃循环 (5/23 起)
5/23 10:01 — 首次 PageCache 崩溃
*** Aborted at 1779501667 (unix time) → 2026-05-23 10:01
*** SIGSEGV unknown detail explain (@0x0) received by PID 3151440
4# google::protobuf::internal::RepeatedPtrFieldBase::DestroyProtos()
5# doris::segment_v2::ColumnMetaPB::SharedDtor()
at segment_v2.pb.cc:4758
8# doris::segment_v2::SegmentFooterPB::SharedDtor()
at segment_v2.pb.cc:6363
9# doris::segment_v2::SegmentFooterPB::~SegmentFooterPB()
at segment_v2.pb.cc:6358
10# doris::MemoryTrackedPageBase::~MemoryTrackedPageBase()
at page_cache.h:48
11# doris::MemoryTrackedPageWithPagePtr::~MemoryTrackedPageWithPagePtr()
at page_cache.cpp:69
12# doris::LRUCache::prune_if()
at lru_cache.cpp:627
14# doris::LRUCachePolicy::prune_stale()
15# doris::CacheManager::for_each_cache_prune_stale()
at cache_manager.cpp:46
16# doris::Daemon::cache_prune_stale_thread()
at daemon.cpp:566
此后持续崩溃, 全部相同堆栈:
| 时间 | PID | tablet_id |
|---|---|---|
| 5/23 10:01 | 3151440 | 0 |
| 5/23 11:42 | 3576641 | 0 |
| 5/28 11:48 | 3613021 | 1815518124650 |
| 5/28 20:27 | 1636049 | 0 |
| 5/30 20:00 | 1809319 | 1815518124650 |
| 5/31 12:24 | 4062478 | 1815518124650 |
累计 122 次 SIGSEGV (be.out 统计)。
阶段四: 数据损坏暴露 (5/29 起)
5/29 02:55 — Compaction 首次读到损坏文件
W20260529 02:55:04.701244 1809681 status.h:439] meet error status:
[CORRUPTION]Bad page: checksum mismatch
(actual=103909948 vs expect=704282814),
file=/data/doris/be/storage/data/194/1815518124650/173204708/
020000000000dc83844902009917fdfb91110df9bcffd0b4_0.dat
W20260529 02:55:04.701385 1809681 merger.cpp:309]
failed to read next block when merging rowsets of tablet 1815518124650,
error: [CORRUPTION]Bad page: checksum mismatch...
W20260529 02:55:04.706915 1809681 tablet.cpp:1892]
failed to do cumulative compaction, tablet=1815518124650 :
[CORRUPTION]Bad page: checksum mismatch...
此后每次 compaction 尝试处理 tablet 1815518124650 都会失败, 每 8 秒重试一次, WARNING 日志膨胀到 10MB+。
四、根因分析
4.1 两个独立的缓存 Bug
Bug 1: ColumnReaderCache use-after-free
- 位置:
column_reader_cache.cpp:74→_insert_locked_nocheck() - 触发路径: Segment 读取 → ColumnReaderCache::get_column_reader() → 淘汰旧缓存条目 → ColumnReader 析构 → SIGSEGV
- 影响: compaction 读取阶段返回脏数据, 直接导致数据损坏
Bug 2: PageCache (SegmentFooterPB) use-after-free
- 位置:
lru_cache.cpp:627→LRUCache::prune_if() - 触发路径: 后台清理线程 → prune_stale() → SegmentFooterPB 析构 → RepeatedPtrFieldBase::DestroyProtos() → SIGSEGV
- 影响: 后台线程崩溃, 导致 BE 进程状态异常
4.2 数据损坏的因果链
ColumnReaderCache use-after-free bug
↓
Compaction 读取阶段通过 ColumnReaderCache 获取 ColumnReader
↓
Cache 返回悬空指针指向的 ColumnReader
↓
通过悬空指针读取的数据 = 垃圾数据
↓
垃圾数据被写入新的 compaction 输出 segment 文件
↓
Footer checksum 基于正确的元数据计算, 但数据页已被污染
↓
checksum mismatch → [CORRUPTION]
4.3 恶性循环
损坏文件 → compaction 反复尝试读取 → 失败 → 损坏的 page 进入 PageCache
→ PageCache prune 线程触发 use-after-free → SIGSEGV
→ BE 崩溃/异常 → 更多 compaction 中断 → 循环
(以上内容为AI读取日志生成的报告)