订阅更新

导入数据后查询突然变慢了——Compaction机制和小文件合并

Doris手册 第 9 题 约 6 分钟读完

本文目录
  1. 先说结论
  2. 为什么需要Compaction?
  3. 两种Compaction的区别
  4. Compaction相关的核心参数
  5. 实战踩坑:高频写入导致版本堆积
  6. 面试高频追问
  7. 总结

先说结论

Compaction是Doris后台的数据合并过程。每次Stream Load写入都会生成一个新的数据版本(Rowset),Compaction负责把多个小版本合并成大版本。如果Compaction跟不上写入速度,小版本堆积,查询就要扫描大量小文件,性能急剧下降。

面试核心概念:Base Compaction(大合并)和Cumulative Compaction(增量合并)的区别、触发条件、调优参数。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

在某元宇宙社交公司用Doris做实时数仓时,有一次Flink高频写入后发现查询变慢了——平时800ms的查询涨到了5秒。排查发现是BE节点的Compaction跟不上导入速度,产生了大量小版本文件。Compaction是Doris后台非常重要的机制,理解它才能诊断很多性能问题。

为什么需要Compaction?

Doris采用LSM-Tree思想管理数据:写入时追加新版本(不修改旧数据),后台异步合并。这和Kafka的Segment合并、HBase的Compaction原理类似。

# Doris的数据版本管理
#
# 第一次Stream Load → 生成 Rowset-1(Version 2)
# 第二次Stream Load → 生成 Rowset-2(Version 3)
# 第三次Stream Load → 生成 Rowset-3(Version 4)
# ...
# 查询时要扫描所有版本 → 版本越多越慢!
#
# Cumulative Compaction(增量合并):
#   Rowset-1 + Rowset-2 + Rowset-3 → 合并成一个Rowset
#   触发条件:小版本数量达到阈值(默认5个)
#
# Base Compaction(大合并):
#   把累积合并后的Rowset和Base Rowset合并
#   触发条件:累积版本大小达到Base的比例阈值
#
# 合并后版本数减少 → 查询只需扫描少量大文件 → 快!

两种Compaction的区别

类型合并对象触发条件频率IO开销
Cumulative最近的小版本互相合并小版本数>=5频繁低
BaseCumulative结果和Base合并累积/Base比例达标较少高(全量重写)

可以理解为:Cumulative是'日常整理',把每天的零碎小文件归档到一起;Base是'大扫除',把归档后的文件和底层大文件做一次全量合并。

Compaction相关的核心参数

# BE配置文件(be.conf)中的Compaction参数
 
# Cumulative Compaction
cumulative_compaction_num_singleton_deltas=5  # 小版本数>=5触发
max_cumulative_compaction_num_singleton_deltas=1000  # 上限
 
# Base Compaction
base_compaction_num_cumulative_deltas=5  # 累积版本数>=5触发Base
 
# Compaction线程数(核心调优参数)
max_compaction_threads=10  # 默认10,CPU多的机器可以调大
compaction_task_num_per_disk=4  # 每块磁盘并行Compaction数
 
# Compaction内存限制
compaction_mem_limit=2147483648  # 2GB,单个Compaction的内存上限
 
# 如果写入量大、Compaction跟不上,可以调大线程数
# 但要注意不要占用太多CPU影响查询性能

实战踩坑:高频写入导致版本堆积

在某元宇宙社交公司,Flink每5秒向Doris做一次Stream Load,每次写入约1000条。这意味着每分钟产生12个新版本,每小时720个版本。Compaction的默认配置(10个线程)在正常情况下能处理,但有一次晚高峰数据量翻倍,Compaction开始跟不上了。

症状:查询从800ms涨到5秒,BE节点的磁盘IO飙升。用SHOW TABLET命令查看,某些Tablet的版本数到了200+。

-- 查看Tablet的版本数(诊断Compaction是否跟不上)
SHOW TABLET FROM ods_user_event;
-- 关注 VersionCount 列
-- 正常值:< 50
-- 告警值:> 100
-- 危险值:> 500(查询会非常慢)
 
-- 手动触发Compaction(应急手段)
-- 通过BE的HTTP API触发
-- curl http://be-host:8040/api/compaction/run?tablet_id=xxx
 
-- 更好的方案:调整Compaction线程数
-- 在be.conf中设置
max_compaction_threads=20  # 从10调到20
compaction_task_num_per_disk=8  # 从4调到8

解决方案:① 临时增加Compaction线程到20;② 调大Stream Load的攒批量,从1000条/5秒改成5000条/10秒,减少版本生成频率;③ 最终版本数从200+降到30以内,查询恢复到800ms。

根本教训:Stream Load的攒批策略很重要。不要太频繁地做小批量写入——每次写入都生成一个新版本。攒大一点的批次,减少版本数,对Compaction的压力小得多。我后来把所有Flink写Doris的任务都调成了5000条或10秒一批。

面试高频追问

追问一:Compaction会影响查询吗?

会。Compaction会占用CPU和磁盘IO。如果Compaction线程太多,会和查询争抢资源。建议:低峰期可以多线程Compaction赶进度,高峰期控制线程数避免影响查询。Doris也支持设置Compaction的优先级和限速。

追问二:Doris的Compaction和HBase的有什么区别?

原理类似,都是LSM-Tree的合并。区别在于Doris的Compaction合并的是列存的Rowset,HBase合并的是行存的HFile。另外Doris的Compaction在合并时会做Aggregate Key和Unique Key的去重/聚合计算,HBase只做简单的文件合并。

追问三:怎么监控Compaction的健康状况?

三个指标:① Tablet的VersionCount(SHOW TABLET查看,>100要关注);② Compaction Score(BE的metrics里,越高说明积压越多);③ BE的磁盘IO利用率。我在元宇宙社交公司把VersionCount>100设为告警阈值,接飞书通知。

总结

Compaction面试讲清楚三点:① 为什么需要(LSM-Tree架构,写入生成新版本,合并减少版本数);② Cumulative vs Base的区别(增量小合并 vs 全量大合并);③ 怎么调优(攒批减少写入频率、调大Compaction线程数、监控VersionCount)。踩坑经验是:不要高频小批量Stream Load,攒大批次减少版本生成,这是避免Compaction积压的根本方法。

下一篇聊Doris在实时数仓中的分层实践:ODS/DWD/DWS/ADS在Doris里怎么建?和传统Hive数仓有什么区别?

你们遇到过Doris的Compaction问题吗?怎么解决的?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器