揭秘Apache Doris存储引擎:从设计到实践的高性能之路
你是否在处理海量数据时遇到查询缓慢、存储成本高企的问题?作为一款分布式SQL查询引擎,Apache Doris(GitHub_Trending/doris/doris)凭借其独特的存储引擎设计,在OLAP领域脱颖而出。本文将深入剖析Doris存储引擎的核心架构,带你掌握从数据写入到查询优化的全流程实现原理,读完你将能够:
- 理解Doris如何通过分层存储实现万亿级数据高效管理
- 掌握Compaction机制如何解决写入放大问题
- 学会通过存储配置优化提升30%+查询性能
存储引擎整体架构:分层设计的智慧
Doris存储引擎采用多级存储架构,通过协调内存、本地磁盘和云存储,实现了高性能与低成本的平衡。核心模块包括:
核心组件解析
-
StorageEngine:存储引擎的中枢神经系统,定义在be/src/olap/storage_engine.h中,负责管理所有Tablet和Rowset的生命周期。它通过
_store_map维护数据目录映射,协调Compaction任务调度,是整个存储系统的大脑。 -
Tablet:数据分片的基本单位,对应be/src/olap/tablet.cpp实现。每个Tablet包含多个Rowset,通过版本控制实现MVCC,支持原子性的读写操作。关键代码片段展示了Tablet如何维护版本映射:
// Tablet::add_rowset - 原子性添加新的版本数据
Status Tablet::add_rowset(RowsetSharedPtr rowset) {
std::lock_guard<std::shared_mutex> wrlock(_meta_lock);
if (_contains_rowset(rowset->rowset_id())) {
return Status::OK(); // 已存在则直接返回
}
RETURN_IF_ERROR(_contains_version(rowset->version())); // 检查版本冲突
_rs_version_map[rowset->version()] = rowset; // 版本映射表
_timestamped_version_tracker.add_version(rowset->version()); // 版本追踪
}
- Rowset:版本化的数据集合,定义在be/src/olap/rowset/rowset.h。每个Rowset包含多个Segment文件,采用状态机管理生命周期:
enum RowsetState {
ROWSET_UNLOADED, // 初始状态
ROWSET_LOADED, // 已加载
ROWSET_UNLOADING // 卸载中
};
Rowset通过load()和close()方法管理资源,支持引用计数确保并发安全访问。
写入流程:LSM树的创新实践
Doris借鉴LSM树思想,但做了针对性优化,实现了高吞吐写入与低延迟查询的平衡。写入流程遵循以下步骤:
- MemTable写入:数据首先写入内存表,支持批量提交
- Flush触发:当达到阈值(默认128MB),通过MemTableFlushExecutor异步刷盘
- Rowset创建:生成不可变的Rowset,包含多个Segment文件
关键优化点在于Delta-Writer机制,通过delta_writer.cpp实现批量写入和按列存储组织,大幅提升写入吞吐量。
Compaction机制:消除存储碎片化
Compaction是Doris存储引擎的性能调优核心,解决了LSM树架构中的写入放大问题。Doris实现了三种Compaction策略:
分层Compaction策略
- Cumulative Compaction:合并多个小版本Delta Rowset,对应be/src/olap/cumulative_compaction.cpp
- Base Compaction:合并Delta与Base Rowset,生成更大的Rowset
- Single Replica Compaction:单副本Compaction优化,降低网络开销
Compaction任务调度通过CompactionPermitLimiter实现资源控制,防止系统过载:
// 控制Compaction并发度的令牌桶实现
void CompactionPermitLimiter::request(int64_t permits) {
std::unique_lock<std::mutex> lock(_permits_mutex);
_permits_cv.wait(lock, [this, permits] {
return _used_permits.load() + permits <= config::max_compaction_permits;
});
_used_permits += permits;
}
数据结构创新:Segment V2的威力
Doris采用列式存储,每个Segment文件包含多个列族,通过Page组织数据。Segment V2实现(be/src/olap/rowset/segment_v2/segment.h)引入了多项创新:
分层索引设计
- Short Key Index:稀疏索引,加速范围查询
- Primary Key Index:唯一键索引,支持快速点查
- Bloom Filter:针对高基数列的过滤优化
智能压缩策略
根据数据类型自动选择压缩算法:
- 数值类型:采用LZ4压缩(压缩比2-3x)
- 字符串类型:使用ZSTD(压缩比5-8x)
- 重复值多的列:RLE编码(压缩比可达10x+)
最佳实践:性能调优指南
基于存储引擎特性,推荐以下优化策略:
Compaction调优
# be.conf中的关键配置
max_compaction_permits = 10000 # 控制Compaction资源占用
cumulative_compaction_num_threads = 4 # 根据CPU核心数调整
base_compaction_num_threads = 2
存储介质选择
// 存储介质选择逻辑 - tablet.cpp
std::vector<DataDir*> StorageEngine::get_stores_for_create_tablet(
int64_t partition_id, TStorageMedium::type storage_medium) {
// 根据数据热度选择存储介质
if (storage_medium == TStorageMedium::HDD) {
return _select_hdd_stores(partition_id);
} else {
return _select_ssd_stores(partition_id);
}
}
查询优化建议
- 分区策略:时间序列数据按天分区,配合分区裁剪
- 索引设计:为过滤频繁的列创建Bloom Filter
- 数据倾斜处理:通过分桶键设计避免热点Rowset
未来展望:云原生存储之路
Doris正朝着存储计算分离架构演进,通过CloudStorageEngine实现云对象存储支持。主要方向包括:
- 冷热数据自动分层存储
- 跨区域数据复制
- 按需加载与缓存优化
这些改进将进一步降低TCO,使Doris在云环境中更具竞争力。
总结:存储引擎设计的启示
Apache Doris存储引擎通过分层架构、智能Compaction和高效索引三大支柱,构建了高性能的OLAP存储系统。其设计理念对同类系统具有重要参考价值:
- 平衡设计:在内存、磁盘和网络之间找到最佳平衡点
- 增量优化:通过Compaction实现写入性能与查询性能的动态平衡
- 适应性架构:从单机到云原生的平滑演进路径
掌握这些设计思想,不仅能更好地使用Doris,更能在构建自己的存储系统时获得宝贵灵感。查看官方文档获取更多技术细节,或通过社区论坛参与讨论。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



