StarRocks源码阅读:2.5版本compaction机制
前言
最近由于业务需要搭建了StarRocks2.5.3版本的集群,但是在使用过程中发现曾经在2.3版本设置的一些配置项在2.5版本不太好用,看起来需要重新盘一盘2.5版本的compaction压缩机制了。
本文是通过StarRocks2.5.3版本源码进行阅读梳理的,其他版本内容仅供参考。
源码阅读
_schedule
void CompactionManager::_schedule() {
LOG(INFO) << "start compaction scheduler";
while (!_stop.load(std::memory_order_consume)) {
++_round;
_wait_to_run();
std::shared_ptr<CompactionTask> compaction_task = _try_get_next_compaction_task();
if (!compaction_task) {
std::unique_lock<std::mutex> lk(_mutex);
_cv.wait_for(lk, 1000ms);
} else {
if (compaction_task->compaction_type() == CompactionType::BASE_COMPACTION) {
StarRocksMetrics::instance()->tablet_base_max_compaction_score.set_value(
compaction_task->compaction_score());
} else {
StarRocksMetrics::instance()->tablet_cumulative_max_compaction_score.set_value(
compaction_task->compaction_score());
}
compaction_task->set_task_id(next_compaction_task_id());
LOG(INFO) << "submit task to compaction pool"
<< ", task_id:" << compaction_task->task_id()
<< ", tablet_id:" << compaction_task->tablet()->tablet_id()
<< ", compaction_type:" << starrocks::to_string(compaction_task->compaction_type())
<< ", compaction_score:" << compaction_task->compaction_score() << " for round:" << _round
<< ", task_queue_size:" << candidates_size();
auto st = _compaction_pool->submit_func([compaction_task] {
compaction_task->start(); });
if (!st.ok()) {
LOG(WARNING) << "submit compaction task " << compaction_task->task_id()
<< " to compaction pool failed. status:" << st.to_string();
compaction_task->tablet()->reset_compaction();
CompactionCandidate candidate;
candidate.tablet = compaction_task->tablet();
update_candidates({
candidate});
}
}
}
}
首先注意到的地方是compaction_manager.cpp的_schedule函数,根据字面意思,这个函数是所有压缩的调度器,用来向compaction pool提交待压缩任务的。
它是通过_try_get_next_compaction_task()
函数获取的待压缩任务队列。
_try_get_next_compaction_task
std::shared_ptr<CompactionTask> CompactionManager::_try_get_next_compaction_task() {
VLOG(2) << "try to get next qualified tablet for round:" << _round
<< ", current candidates size:" << candidates_size();
CompactionCandidate compaction_candidate;
std::shared_ptr<CompactionTask> compaction_task = nullptr;
if (pick_candidate(&compaction_candidate)) {
compaction_task = compaction_candidate.tablet->create_compaction_task();
}
return compaction_task;
}
这个函数比较短,可以看出由pick_candidate
函数来选择的压缩候选者。
pick_candidate
bool CompactionManager::pick_candidate(CompactionCandidate* candidate) {
std::lock_guard lg(_candidates_mutex);
if (_compaction_candidates.empty()) {
return false;
}
auto iter = _compaction_candidates.begin();
while (iter != _compaction_candidates.end()) {
if (_check_precondition(*iter)) {
*candidate = *iter;
_compaction_candidates.erase(iter);
return true;
}
iter++;
}
return false;
}
接下来看pick_candidate
函数,首先有一个_compaction_candidates
队列,循环这个队列,对每一行调用一次_check_precondition
函数进行判断,如果满足压缩前置条件,那么就出队并返回true。
这里有2个函数需要注意
_compaction_candidates
队列的设计,待压缩的tablet是怎样入队的。_check_precondition
函数的判断逻辑。
update_candidates
void CompactionManager::update_candidates(std::vector<CompactionCandidate> candidates) {
size_t erase_num = 0;
{
std::lock_guard lg(_candidates_mutex);
// TODO(meegoo): This is very inefficient to implement, just to fix bug, it will refactor later
for (auto iter = _compaction_candidates.begin(); iter != _compaction_candidates.end();) {
bool has_erase = false;
for (auto& candidate : candidates) {
if (candidate.tablet->tablet_id() == iter->tablet->tablet_id()) {
iter = _compaction_candidates.erase(iter);
erase_num++;
has_erase = true;
break;
}
}
if (!has_erase) {
iter++;
}
}
for (auto& candidate : candidates) {
if (candidate.tablet->enable_compaction()) {
VLOG(1) << "update candidate " << candidate.tablet->tablet_id() << " type "
<< starrocks::to_string(candidate.type) << " score " << candidate.score;
_compaction_candidates.emplace(std::move(candidate));
}
}
}
_notify();
}
这个函数是队列的入队函数,它主要是将candidates
队列进行了1次循环,将所有开启了compaction的tablet进行了入队操作。
在这段代码中有一个TODO备注,根据描述未来这里会重构。
读到这里又有2个疑问需要继续阅读
candidates
队列维护机制update_candidates
调用机制