1. StorageEngine 架构概述
1.1 核心架构设计
Doris 的存储引擎采用 LSM-Tree(Log-Structured Merge-Tree) 架构,专为写多读少场景优化。StorageEngine 是 BE 节点存储层的核心管理组件,负责:
- Tablet 管理:管理所有 Tablet 的生命周期
- 数据目录管理:管理多个数据目录(DataDir)
- MemTable 刷盘:协调内存数据持久化
- Compaction 调度:管理 Base 和 Cumulative Compaction
- 事务管理:协调数据加载的事务流程
1.2 存储架构类型
cpp
// be/src/olap/storage_engine.h
class BaseStorageEngine {
public:
enum StorageType {
LOCAL = 0, // 存算一体架构
CLOUD = 1 // 存算分离架构
};
};
LOCAL 模式(存算一体):
- 数据和计算节点紧密耦合
- 本地存储提供高性能读写
- 适合低延迟查询场景
CLOUD 模式(存算分离):
- 数据存储在远程对象存储(S3/HDFS)
- 计算节点无状态,可弹性扩缩容
- 适合云原生部署场景
1.3 LSM-Tree 核心概念
scss
版本链示例:
[0-0] Base Rowset (Base Compaction输出)
↓
[1-1] Delta Rowset (数据导入)
[2-2] Delta Rowset
[3-3] Delta Rowset
↓
[1-3] Cumulative Rowset (Cumulative Compaction输出)
↓ cumulative_layer_point = 4
[4-4] Delta Rowset
[5-5] Delta Rowset
关键字段:
- cumulative_layer_point:区分 Base 和 Cumulative 层的分界点
- _rs_version_map:活跃的 Rowset 版本映射
- _stale_rs_version_map:过期的 Rowset(Compaction 输入)
2. StorageEngine 初始化流程
2.1 初始化入口(_open)
cpp
// be/src/olap/storage_engine.cpp
Status StorageEngine::_open() {
// 1. 初始化 store_map(数据目录映射)
RETURN_NOT_OK_STATUS_WITH_WARN(_init_store_map(), "_init_store_map failed");
// 2. 设置集群 ID
_effective_cluster_id = config::cluster_id;
RETURN_NOT_OK_STATUS_WITH_WARN(_check_all_root_path_cluster_id(),
"fail to check cluster id");
// 3. 更新存储介质类型计数
_update_storage_medium_type_count();
// 4. 检查文件描述符数量
RETURN_NOT_OK_STATUS_WITH_WARN(_check_file_descriptor_number(),
"check fd number failed");
// 5. 加载所有 DataDir 的数据
auto dirs = get_stores();
RETURN_IF_ERROR(load_data_dirs(dirs));
// 6. 初始化 MemTable 刷盘执行器
_disk_num = cast_set<int>(dirs.size());
_memtable_flush_executor = std::make_unique<MemTableFlushExecutor>();
_memtable_flush_executor->init(_disk_num);
// 7. 初始化 DeleteBitmap 计算执行器(MOW表)
_calc_delete_bitmap_executor = std::make_unique<CalcDeleteBitmapExecutor>();
_calc_delete_bitmap_executor->init(config::calc_delete_bitmap_max_thread);
// 8. 解析默认 Rowset 类型
_parse_default_rowset_type();
return Status::OK();
}
2.2 核心组件初始化
MemTableFlushExecutor:
cpp
// be/src/olap/memtable_flush_executor.h
class MemTableFlushExecutor {
public:
void init(int num_disk);
Status create_flush_token(std::shared_ptr<FlushToken>& flush_token,
std::shared_ptr<RowsetWriter> rowset_writer,
bool is_high_priority,
std::shared_ptr<WorkloadGroup> wg_sptr);
private:
std::unique_ptr<ThreadPool> _flush_pool; // 普通优先级线程池
std::unique_ptr<ThreadPool> _high_prio_flush_pool; // 高优先级线程池
std::atomic<int> _flushing_task_count = 0;
};
CalcDeleteBitmapExecutor:
- 用于 Unique Key Merge-on-Write 模型
- 计算 DeleteBitmap 标记重复键为删除状态
- 避免读取阶段的 Merge 开销
3. 数据目录管理(DataDir)
3.1 DataDir 结构
cpp
// be/src/olap/data_dir.h
class DataDir {
public:
DataDir(StorageEngine& engine, const std::string& path,
int64_t capacity_bytes = -1,
TStorageMedium::type storage_medium = TStorageMedium::HDD);
Status init(bool init_meta = true);
const std::string& path() const { return _path; }
size_t available_bytes() const { return _available_bytes; }
size_t disk_capacity_bytes() const { return _disk_capacity_bytes; }
TStorageMedium::type storage_medium() const { return _storage_medium; }
private:
StorageEngine& _engine;
std::string _path; // 数据目录路径
size_t _path_hash; // 路径哈希值
size_t _available_bytes; // 可用空间
size_t _disk_capacity_bytes; // 磁盘总容量
TStorageMedium::type _storage_medium; // 存储介质类型(HDD/SSD)
};
3.2 多目录管理
Doris 支持配置多个数据目录,提供以下能力:
负载均衡:
- 创建 Tablet 时选择负载最低的目录
- 均衡分布数据,避免单盘热点
存储介质分层:
- HDD:存储冷数据
- SSD:存储热数据
- 支持自动冷热数据迁移(Cooldown)
容错能力:
- 单盘故障不影响其他盘的数据
- 支持数据在线迁移
4. TabletManager 详解
4.1 TabletManager 架构
cpp
// be/src/olap/tablet_manager.h
class TabletManager {
public:
TabletManager(StorageEngine& engine, int32_t tablet_map_lock_shard_size);
// Tablet 生命周期管理
Status create_tablet(const TCreateTabletReq& request,
std::vector<DataDir*> stores,
RuntimeProfile* profile);
Status drop_tablet(TTabletId tablet_id, TReplicaId replica_id,
bool is_drop_table_or_partition);
TabletSharedPtr get_tablet(TTabletId tablet_id,
bool include_deleted = false,
std::string* err = nullptr);
private:
using tablet_map_t = std::unordered_map<int64_t, TabletSharedPtr>;
struct tablets_shard {
mutable std::shared_mutex lock; // 分片锁
tablet_map_t tablet_map; // Tablet 映射
std::mutex lock_for_transition; // 状态转换锁
std::map<int64_t, std::tuple<std::string, std::thread::id, int64_t>>
tablets_under_transition; // 正在转换状态的 Tablet
};
StorageEngine& _engine;
const int32_t _tablets_shards_size; // 分片数量
const int32_t _tablets_shards_mask; // 分片掩码
std::vector<tablets_shard> _tablets_shards; // Tablet 分片
std::shared_mutex _partitions_lock;
std::map<int64_t, Partition> _partitions; // Partition 信息
std::shared_mutex _shutdown_tablets_lock;
std::list<TabletSharedPtr> _shutdown_tablets; // 待关闭的 Tablet
};
4.2 分片锁设计
TabletManager 使用 分片锁(Shard Lock) 降低锁竞争:
ini
Tablet ID: 123456789
Shard Index = 123456789 & (shard_size - 1)
例如 shard_size = 1024:
Shard Index = 123456789 & 1023 = 853
访问 _tablets_shards[853] 时,只需锁住该分片
优势:
- 减少全局锁争用
- 提高并发访问性能
- 默认分片数:128 或 256
4.3 Tablet 创建流程
cpp
Status TabletManager::create_tablet(const TCreateTabletReq& request,
std::vector<DataDir*> stores,
RuntimeProfile* profile) {
// 1. 选择数据目录(负载最低的目录)
DataDir* store = _select_data_dir(stores);
// 2. 创建 TabletMeta
TabletMetaSharedPtr tablet_meta = TabletMeta::create(
request, tablet_uid, shard_id, next_unique_id, col_ordinal_to_unique_id);
// 3. 创建 Tablet 对象
TabletSharedPtr tablet = Tablet::create_tablet_from_meta(
_engine, tablet_meta, store);
// 4. 初始化 Tablet
RETURN_IF_ERROR(tablet->init());
// 5. 注册到 TabletManager
RETURN_IF_ERROR(_add_tablet_unlocked(tablet_id, tablet, true, false));
// 6. 创建初始 Rowset(版本 [0-1])
RETURN_IF_ERROR(tablet->create_initial_rowset(1));
return Status::OK();
}
5. Tablet 生命周期管理
5.1 Tablet 核心结构
cpp
// be/src/olap/tablet.h
class Tablet final : public BaseTablet {
public:
Tablet(StorageEngine& engine, TabletMetaSharedPtr tablet_meta,
DataDir* data_dir, const std::string_view& cumulative_compaction_type = "");
// 版本管理
int64_t cumulative_layer_point() const;
void set_cumulative_layer_point(int64_t new_point);
// Rowset 管理
Status add_rowset(RowsetSharedPtr rowset);
Status modify_rowsets(std::vector<RowsetSharedPtr>& to_add,
std::vector<RowsetSharedPtr>& to_delete,
bool check_delete = false);
void delete_expired_stale_rowset();
void max_continuous_version_from_beginning(Version* version,
Version* max_version = nullptr);
private:
StorageEngine& _engine;
DataDir* _data_dir = nullptr;
std::atomic<int64_t> _cumulative_point; // Cumulative 分界点
// Rowset 版本映射(继承自 BaseTablet)
std::unordered_map<Version, RowsetSharedPtr, HashOfVersion> _rs_version_map;
std::unordered_map<Version, RowsetSharedPtr, HashOfVersion> _stale_rs_version_map;
};
5.2 Rowset 添加流程
cpp
Status Tablet::add_rowset(RowsetSharedPtr rowset) {
std::unique_lock wlock(_meta_lock);
// 1. 检查版本是否已存在
RETURN_IF_ERROR(_contains_version(rowset->version()));
// 2. 添加到活跃 Rowset 映射
_rs_version_map[rowset->version()] = rowset;
// 3. 更新 TabletMeta
_tablet_meta->add_rs_meta(rowset->rowset_meta());
// 4. 更新累积点(如果需要)
if (rowset->end_version() >= cumulative_layer_point()) {
// 新 Rowset 超过累积点,可能需要调整
}
return Status::OK();
}
5.3 过期 Rowset 清理
cpp
void Tablet::delete_expired_stale_rowset() {
int64_t now = UnixMillis();
std::vector<RowsetSharedPtr> expired_rowsets;
{
std::shared_lock rlock(_meta_lock);
for (auto& [version, rowset] : _stale_rs_version_map) {
int64_t rowset_stale_time = rowset->rowset_meta()->stale_at();
int64_t interval = now - rowset_stale_time;
// 超过过期时间阈值
if (interval >= config::tablet_rowset_expired_stale_sweep_time_sec * 1000) {
expired_rowsets.push_back(rowset);
}
}
}
// 删除过期 Rowset
for (auto& rowset : expired_rowsets) {
std::unique_lock wlock(_meta_lock);
_stale_rs_version_map.erase(rowset->version());
_tablet_meta->delete_stale_rs_meta_by_version(rowset->version());
}
}
6. LSM-Tree 结构详解
6.1 cumulative_layer_point 机制
cpp
// be/src/olap/tablet_meta.h
class TabletMeta {
private:
int64_t _cumulative_layer_point = 0; // Cumulative 分界点
RowsetMetaMapContainer _rs_metas; // 活跃的 Rowset
RowsetMetaMapContainer _stale_rs_metas; // 过期的 Rowset
};
cumulative_layer_point 的作用:
ini
示例 1:cumulative_layer_point = 10
[0-0] Base Rowset
[1-9] Base Rowset (Base Compaction 输出)
[10-10] Delta Rowset ← Cumulative Layer 开始
[11-11] Delta Rowset
[12-12] Delta Rowset
Base Compaction: 合并 [0-0] 和 [1-9]
Cumulative Compaction: 合并 [10-10], [11-11], [12-12]
更新时机:
- Cumulative Compaction 完成后:point 前移到新 Rowset 的 end_version + 1
- 遇到 Delete 版本:跳过 Delete 版本,point = delete_version + 1
- 长时间无新数据:自动前移 point,让 Base Compaction 继续
6.2 Rowset 版本管理
cpp
// be/src/olap/rowset/rowset_meta.h
class RowsetMeta {
public:
Version version() const {
return {_rowset_meta_pb.start_version(), _rowset_meta_pb.end_version()};
}
int64_t start_version() const;
int64_t end_version() const;
bool is_singleton_delta() const {
return has_version() &&
_rowset_meta_pb.start_version() == _rowset_meta_pb.end_version();
}
bool produced_by_compaction() const {
return has_version() &&
(start_version() < end_version() ||
(start_version() == end_version() &&
segments_overlap() == NONOVERLAPPING));
}
uint32_t get_compaction_score() const {
if (!is_segments_overlapping()) {
return 1;
} else {
return cast_set<uint32_t>(num_segments());
}
}
};
版本类型:
- Singleton Delta:[N-N],单版本 Rowset(数据导入产生)
- Range Rowset:[M-N],版本范围 Rowset(Compaction 产生)
Compaction Score 计算:
- 非重叠 Rowset:Score = 1
- 重叠 Rowset:Score = Segment 数量
6.3 版本跟踪器(TimestampedVersionTracker)
cpp
// be/src/olap/base_tablet.h
class BaseTablet {
protected:
TimestampedVersionTracker _timestamped_version_tracker;
// 活跃 Rowset 映射
std::unordered_map<Version, RowsetSharedPtr, HashOfVersion> _rs_version_map;
// 过期 Rowset 映射
std::unordered_map<Version, RowsetSharedPtr, HashOfVersion> _stale_rs_version_map;
};
TimestampedVersionTracker 功能:
- 跟踪每个版本的创建时间
- 判断 Rowset 是否过期
- 计算连续版本范围
7. 数据写入流程
7.1 MemTable → Rowset 流程
markdown
数据导入流程:
1. DeltaWriter 写入 MemTable
2. MemTable 达到阈值触发 Flush
3. FlushToken 提交 Flush 任务
4. MemTableFlushExecutor 执行刷盘
5. RowsetWriter 生成 Segment 文件
6. Publish 使 Rowset 可见
7.2 FlushToken 机制
cpp
// be/src/olap/memtable_flush_executor.h
class FlushToken : public std::enable_shared_from_this<FlushToken> {
public:
FlushToken(ThreadPool* thread_pool, std::shared_ptr<WorkloadGroup> wg_sptr);
Status submit(std::shared_ptr<MemTable> mem_table);
Status wait();
void cancel();
void set_rowset_writer(std::shared_ptr<RowsetWriter> rowset_writer);
private:
void _flush_memtable(std::shared_ptr<MemTable> memtable_ptr,
int32_t segment_id, int64_t submit_task_time);
Status _do_flush_memtable(MemTable* memtable,
int32_t segment_id, int64_t* flush_size);
std::shared_mutex _flush_status_lock;
Status _flush_status; // Flush 状态
FlushStatistic _stats; // 统计信息
std::shared_ptr<RowsetWriter> _rowset_writer; // Rowset Writer
ThreadPool* _thread_pool; // 线程池
};
7.3 MemTable Flush 执行
cpp
void FlushToken::_flush_memtable(std::shared_ptr<MemTable> memtable_ptr,
int32_t segment_id, int64_t submit_task_time) {
// 1. 更新等待时间统计
uint64_t flush_wait_time_ns = MonotonicNanos() - submit_task_time;
_stats.flush_wait_time_ns += flush_wait_time_ns;
// 2. 检查之前的 Flush 是否失败
{
std::shared_lock rdlk(_flush_status_lock);
if (!_flush_status.ok()) {
return; // 之前已失败,直接返回
}
}
// 3. 执行 Flush
MonotonicStopWatch timer;
timer.start();
size_t memory_usage = memtable_ptr->memory_usage();
int64_t flush_size;
Status s = _do_flush_memtable(memtable_ptr.get(), segment_id, &flush_size);
// 4. 处理 Flush 结果
if (!s.ok()) {
std::lock_guard wrlk(_flush_status_lock);
LOG(WARNING) << "Flush memtable failed with res = " << s;
_flush_status = s;
return;
}
// 5. 更新统计信息
_stats.flush_time_ns += timer.elapsed_time();
_stats.flush_finish_count++;
_stats.flush_size_bytes += memory_usage;
_stats.flush_disk_size_bytes += flush_size;
}
7.4 RowsetWriter 写入 Segment
cpp
// be/src/olap/rowset/segment_v2/column_writer.h
class ScalarColumnWriter : public ColumnWriter {
public:
Status append_data(const uint8_t** ptr, size_t num_rows) override;
Status finish_current_page() override;
Status finish() override;
Status write_data() override;
Status write_ordinal_index() override;
Status write_zone_map() override;
Status write_bitmap_index() override;
Status write_inverted_index() override;
private:
std::unique_ptr<PageBuilder> _page_builder; // Page 构建器
std::unique_ptr<NullBitmapBuilder> _null_bitmap_builder; // NULL Bitmap
std::vector<std::unique_ptr<Page>> _pages; // 缓存的 Page
std::unique_ptr<OrdinalIndexWriter> _ordinal_index_builder; // 序号索引
std::unique_ptr<ZoneMapIndexWriter> _zone_map_index_builder; // Zone Map 索引
std::unique_ptr<BitmapIndexWriter> _bitmap_index_builder; // Bitmap 索引
std::vector<std::unique_ptr<IndexColumnWriter>> _inverted_index_builders; // 倒排索引
std::unique_ptr<BloomFilterIndexWriter> _bloom_filter_index_builder; // BloomFilter
};
8. Compaction 机制
8.1 Base Compaction
触发条件(满足任一):
cpp
// be/src/olap/base_compaction.cpp
Status BaseCompaction::pick_rowsets_to_compact() {
_input_rowsets = tablet()->pick_candidate_rowsets_to_base_compaction();
// 条件 1:Cumulative Rowset 数量达到阈值
if (_input_rowsets.size() > config::base_compaction_min_rowset_num) {
return Status::OK();
}
// 条件 2:Cumulative 数据量与 Base 数据量比值达到阈值
int64_t base_size = _input_rowsets.front()->data_disk_size();
int64_t cumulative_total_size = 0;
for (auto it = _input_rowsets.begin() + 1; it != _input_rowsets.end(); ++it) {
cumulative_total_size += (*it)->data_disk_size();
}
double cumulative_base_ratio =
cast_set<double>(cumulative_total_size) / cast_set<double>(base_size);
if (cumulative_base_ratio > config::base_compaction_min_data_ratio) {
return Status::OK();
}
// 条件 3:距离上次 Base Compaction 时间达到阈值
int64_t interval_since_last = time(nullptr) - _input_rowsets[0]->creation_time();
if (interval_since_last > config::base_compaction_interval_seconds_since_last_operation) {
return Status::OK();
}
return Status::Error<BE_NO_SUITABLE_VERSION>("don't satisfy the policy");
}
Compaction 执行流程:
cpp
Status Compaction::merge_input_rowsets() {
// 1. 创建 RowsetReader
std::vector<RowsetReaderSharedPtr> input_rs_readers;
for (auto& rowset : _input_rowsets) {
RowsetReaderSharedPtr rs_reader;
RETURN_IF_ERROR(rowset->create_reader(&rs_reader));
input_rs_readers.push_back(std::move(rs_reader));
}
// 2. 创建输出 RowsetWriter
RowsetWriterContext ctx;
RETURN_IF_ERROR(construct_output_rowset_writer(ctx));
// 3. 垂直 Compaction(按列组合并)
if (_is_vertical) {
res = Merger::vertical_merge_rowsets(
_tablet, compaction_type(), *_cur_tablet_schema,
input_rs_readers, _output_rs_writer.get(), way_num, _stats);
}
// 4. 水平 Compaction(按行合并)
else {
res = Merger::merge_rowsets(
_tablet, compaction_type(), *_cur_tablet_schema,
input_rs_readers, _output_rs_writer.get(), &_stats);
}
// 5. 倒排索引 Compaction(如果有)
RETURN_IF_ERROR(do_inverted_index_compaction());
return res;
}
8.2 Cumulative Compaction
选择策略:
cpp
// be/src/olap/cumulative_compaction_policy.cpp
size_t SizeBasedCumulativeCompactionPolicy::pick_input_rowsets(
Tablet* tablet,
const std::vector<RowsetSharedPtr>& candidate_rowsets,
const int64_t max_compaction_score,
const int64_t min_compaction_score,
std::vector<RowsetSharedPtr>* input_rowsets,
Version* last_delete_version,
size_t* compaction_score,
bool allow_delete) {
size_t promotion_size = tablet->cumulative_promotion_size();
int64_t total_size = 0;
*compaction_score = 0;
for (auto& rowset : candidate_rowsets) {
// 1. 遇到 Delete 版本
if (!allow_delete && rowset->rowset_meta()->has_delete_predicate()) {
*last_delete_version = rowset->version();
if (!input_rowsets->empty()) {
break; // 有其他版本,先合并
} else {
input_rowsets->clear();
*compaction_score = 0;
continue; // 跳过 Delete 版本
}
}
// 2. 累积 Compaction Score
*compaction_score += rowset->rowset_meta()->get_compaction_score();
total_size += rowset->rowset_meta()->total_disk_size();
input_rowsets->push_back(rowset);
// 3. 达到最大 Score,停止选择
if (*compaction_score >= max_compaction_score) {
break;
}
}
// 4. 数据量达到 Promotion Size,提升到 Base 层
if (total_size >= promotion_size) {
return *compaction_score;
}
// 5. 选择同一 Level 的 Rowset
auto rs_begin = input_rowsets->begin();
while (rs_begin != input_rowsets->end()) {
auto& rs_meta = (*rs_begin)->rowset_meta();
int64_t current_level = _level_size(rs_meta->total_disk_size());
int64_t remain_level = _level_size(total_size - rs_meta->total_disk_size());
if (current_level == remain_level) {
break; // 找到同级 Rowset
}
// 移除不同级的 Rowset
total_size -= rs_meta->total_disk_size();
*compaction_score -= rs_meta->get_compaction_score();
rs_begin++;
}
input_rowsets->erase(input_rowsets->begin(), rs_begin);
return *compaction_score;
}
Level 计算:
cpp
int64_t SizeBasedCumulativeCompactionPolicy::_level_size(int64_t size) {
// Level 0: 0 ~ 64MB
// Level 1: 64MB ~ 128MB
// Level 2: 128MB ~ 256MB
// ...
if (size < 64 * 1024 * 1024) return 0;
int64_t level = 0;
int64_t threshold = 64 * 1024 * 1024;
while (size >= threshold) {
level++;
threshold *= 2;
}
return level;
}
8.3 Compaction 优化
垂直 Compaction:
scss
传统 Compaction(水平):
[Rowset1] Row1 Row2 Row3
[Rowset2] Row4 Row5
[Rowset3] Row6 Row7
↓ 合并所有列
[Output] Row1~Row7 (所有列)
垂直 Compaction:
[Rowset1] Key列 | 列组1 | 列组2
[Rowset2] Key列 | 列组1 | 列组2
↓ 分列组合并
[Output] Key列 → 列组1 → 列组2
优势:
- 降低内存峰值(只加载部分列)
- 提高 I/O 效率(按列组顺序写入)
- 适合宽表场景
9. 版本发布机制(Publish)
9.1 事务两阶段提交
cpp
// be/src/olap/txn_manager.cpp
Status TxnManager::publish_txn(OlapMeta* meta,
TPartitionId partition_id,
TTransactionId transaction_id,
TTabletId tablet_id,
TabletUid tablet_uid,
const Version& version,
TabletPublishStatistics* stats,
std::shared_ptr<TabletTxnInfo>& extend_tablet_txn_info) {
auto tablet = _engine.tablet_manager()->get_tablet(tablet_id);
if (tablet == nullptr) {
return Status::Error<TABLE_NOT_FOUND>("tablet not found");
}
// 1. 获取已提交的 Rowset
TabletTxnInfo txn_info;
{
std::shared_lock txn_rlock(_get_txn_map_lock(transaction_id));
auto it = _get_txn_tablet_map(transaction_id).find(tablet_info);
if (it == _get_txn_tablet_map(transaction_id).end()) {
return Status::Error<TRANSACTION_NOT_EXIST>("txn not found");
}
txn_info = it->second;
}
// 2. 设置版本号
RowsetSharedPtr rowset = txn_info.rowset;
rowset->make_visible(version);
// 3. 添加到 Tablet
Status res = tablet->add_inc_rowset(rowset);
if (!res.ok()) {
return res;
}
// 4. 更新 TabletMeta
{
std::unique_lock tablet_wlock(tablet->get_header_lock());
tablet->save_meta();
}
// 5. 删除事务信息
{
std::unique_lock txn_wlock(_get_txn_map_lock(transaction_id));
_get_txn_tablet_map(transaction_id).erase(tablet_info);
}
return Status::OK();
}
9.2 Rowset 可见性控制
cpp
// be/src/olap/rowset/rowset.h
class Rowset {
public:
void make_visible(Version version) {
_rowset_meta->set_version(version);
_rowset_meta->set_rowset_state(VISIBLE);
}
bool is_pending() const { return _is_pending; }
Version version() const { return _rowset_meta->version(); }
int64_t start_version() const { return _rowset_meta->version().first; }
int64_t end_version() const { return _rowset_meta->version().second; }
};
Rowset 状态:
- COMMITTED:事务已提交,等待 Publish
- VISIBLE:已 Publish,查询可见
10 冷热数据分层(Cooldown)
10.1 Cooldown 架构
cpp
// be/src/olap/tablet.h
struct CooldownConf {
int64_t term = -1;
int64_t cooldown_replica_id = -1;
};
class Tablet {
public:
Status cooldown(RowsetSharedPtr rowset = nullptr);
RowsetSharedPtr pick_cooldown_rowset();
RowsetSharedPtr need_cooldown(int64_t* cooldown_timestamp, size_t* file_size);
private:
CooldownConf _cooldown_conf;
mutable std::shared_mutex _cooldown_conf_lock;
std::mutex _cold_compaction_lock;
};
10.2 Cooldown 执行流程
cpp
Status Tablet::_cooldown_data(RowsetSharedPtr rowset) {
// 1. 检查是否为 Cooldown 副本
DCHECK(_cooldown_conf.cooldown_replica_id == replica_id());
// 2. 获取远程存储资源
auto storage_resource = DORIS_TRY(get_resource_by_storage_policy_id(storage_policy_id()));
// 3. 选择待 Cooldown 的 Rowset
if (!rowset) {
rowset = pick_cooldown_rowset();
}
if (!rowset) {
return Status::OK();
}
// 4. 上传 Rowset 到远程存储
RowsetId new_rowset_id = _engine.next_rowset_id();
auto start = std::chrono::steady_clock::now();
RETURN_IF_ERROR(rowset->upload_to(storage_resource, new_rowset_id));
auto duration = std::chrono::duration<double>(std::chrono::steady_clock::now() - start);
LOG(INFO) << "Upload rowset " << rowset->version()
<< " to " << storage_resource.fs->root_path().native()
<< ", tablet_id=" << tablet_id()
<< ", duration=" << duration.count()
<< ", capacity=" << rowset->total_disk_size();
// 5. 生成新的 RowsetMeta(指向远程)
auto new_rowset_meta = std::make_shared<RowsetMeta>();
new_rowset_meta->init(rowset->rowset_meta().get());
new_rowset_meta->set_rowset_id(new_rowset_id);
new_rowset_meta->set_remote_storage_resource(std::move(storage_resource));
// 6. 创建新 Rowset
RowsetSharedPtr new_rowset;
RETURN_IF_ERROR(RowsetFactory::create_rowset(
_tablet_meta->tablet_schema(), "", new_rowset_meta, &new_rowset));
// 7. 替换本地 Rowset
{
std::unique_lock wlock(_meta_lock);
RETURN_IF_ERROR(delete_rowsets({std::move(rowset)}, false));
add_rowsets({std::move(new_rowset)});
_tablet_meta->set_cooldown_meta_id(UniqueId::gen_uid());
}
// 8. 保存元数据
{
std::shared_lock rlock(_meta_lock);
save_meta();
}
// 9. 上传 Cooldown Meta 到远程
async_write_cooldown_meta(shared_from_this());
return Status::OK();
}
10.3 Cooldown Rowset 选择
cpp
RowsetSharedPtr Tablet::pick_cooldown_rowset() {
if (!_has_data_to_cooldown()) {
return nullptr;
}
int64_t cooldowned_version = -1;
int64_t min_local_version = std::numeric_limits<int64_t>::max();
RowsetSharedPtr rowset;
{
std::shared_lock rlock(_meta_lock);
for (auto& [v, rs] : _rs_version_map) {
if (!rs->is_local()) {
cooldowned_version = std::max(cooldowned_version, v.second);
} else if (v.first < min_local_version) {
min_local_version = v.first;
rowset = rs;
}
}
}
// 确保版本连续性
if (min_local_version != cooldowned_version + 1) {
if (UNLIKELY(cooldowned_version != -1)) {
LOG(WARNING) << "version not continuous. tablet_id=" << tablet_id()
<< " cooldowned_version=" << cooldowned_version
<< " min_local_version=" << min_local_version;
}
return nullptr;
}
return rowset;
}
Cooldown 策略:
- 按版本顺序 Cooldown(保证连续性)
- 优先 Cooldown 最老的 Rowset
- 支持按时间或大小触发
11. 性能优化建议
-
调整 cumulative_layer_point
- 根据写入频率调整累积点前移策略
- 避免过多小 Rowset 积累
-
Compaction 参数调优
- base_compaction_min_rowset_num:Base 触发阈值
- cumulative_compaction_max_deltas:Cumulative 最大 Score
- vertical_compaction_num_columns_per_group:垂直 Compaction 列组大小
-
MemTable Flush 优化
- 调整 MemTable 大小(write_buffer_size)
- 增加 Flush 线程池大小(num_threads_per_disk)
-
Cooldown 策略
- 设置合理的 Cooldown 时间阈值
- 监控远程存储访问延迟
- 使用 SSD 作为本地缓存