ClickHouse 源代码零散笔记:mark 与 final mark

根据

ClickHouse 的 MergeTree 基本概念和基本原理 https://www.cnblogs.com/jthmath/p/16809828.html

所述,ClickHouse 用 mark 记录每个颗粒在列存文件的位置。本文叙述 ClickHouse 代码中对 mark 的处理。

行数与行号的计算

ClickHouse 中有个比较重要的类

class MergeTreeIndexGranularity
{
private:
    std::vector<size_t> marks_rows_partial_sums;
    
// 其他
}

marks_rows_partial_sums 指的是,每个 mark 以及之前的 mark 对应的行数之和。它的长度就是 mark 的数量。

marks_rows_partial_sums 简记为 s,则根据它的含义,有:

  • 0 号 mark 的行数是 \(s_0\)
  • \(i\) 号 mark 的行数是 \(s_i−s_{i−1}\)
  • 0 号 mark 的起始行号自然是 0,
  • \(i\) 号 mark 的起始行号是 \(0+s_{i−1}=s_{i−1}\)

这里的 \(i > 0\)

final mark 指的是结尾处额外一个 mark,它的值和上一个相等。也就是说,mark 号加了 1,但总行数没增加,是一个傀儡 mark。从我的日志调试结果来看,final mark 总是存在的。或许是旧版本的实现中没有,

目前来看,作用是快速判断是否终止,快速获取行数。

上面的计算式对应代码:

// 计算行数
size_t MergeTreeIndexGranularity::getMarkRows(size_t mark_index) const
{
    if (mark_index >= getMarksCount())
    {
        throw Exception(ErrorCodes::LOGICAL_ERROR, "Trying to get non existing mark {}, while size is {}", mark_index, getMarksCount());
    }
    if (mark_index == 0)
    {
        return marks_rows_partial_sums[0];
    }
    else
    {
        return marks_rows_partial_sums[mark_index] - marks_rows_partial_sums[mark_index - 1];
    }
}

// 计算起始行号
size_t MergeTreeIndexGranularity::getMarkStartingRow(size_t mark_index) const
{
    return mark_index == 0 ? 0 : marks_rows_partial_sums[mark_index - 1];
}

final mark 的处理

class MergeTreeIndexGranularity
{
public:
    // 获取最后一个 mark(无论是否 final)的行数
    size_t getLastMarkRows() const
    {
        size_t last = marks_rows_partial_sums.size() - 1;
        return getMarkRows(last);
    }

    size_t getLastNonFinalMarkRows() const
    {
        size_t last_mark_rows = getLastMarkRows();
        if (last_mark_rows != 0)
        {
            return last_mark_rows;
        }
        return getMarkRows(marks_rows_partial_sums.size() - 2);
    }

    bool hasFinalMark() const
    {
        return getLastMarkRows() == 0;
    }
    
// 其他
}

设索引颗粒度 g = 20(这里是为了计算简便,实际没有这么干的)。而当前的 data part 中有 r = 62 行,且每个颗粒中的行数相同,则需要 ceil(r/g) = ceil(62 / 20) = 4 个 mark,还需要一个 final mark。其值分别为 20,40,60,62,62。

参考日志

2022.06.14 09:07:31.711734 total_mark = 60
2022.06.14 09:07:31.711782 num_rows_total = 481808

2022.06.14 09:07:31.715601 mark = 55, start_row = 450560, mark_rows = 8192
2022.06.14 09:07:31.715669 mark = 56, start_row = 458752, mark_rows = 8192
2022.06.14 09:07:31.715737 mark = 57, start_row = 466944, mark_rows = 8192
2022.06.14 09:07:31.715784 mark = 58, start_row = 475136, mark_rows = 6672
2022.06.14 09:07:31.715854 mark = 59, start_row = 481808, mark_rows = 0

可以看到,final mark 的起始行和总行数一样,它的行数为 0。

posted @ 2022-11-20 20:01  风华神使  阅读(276)  评论(0)    收藏  举报