Clickhouse新增bitmap分頁函式(水了個PR)

DH鑌發表於2023-04-21

起因

在做標籤引擎的時候,我們在採用了bitmap儲存物件id,基礎的結構如下

標籤型別標籤值物件id bitmap
性別[1,2,3]
性別[8,9,10]

表如下:

create table if not exists label_string_local on cluster clickhouse_cluster
(
    label_type  String comment '標籤id',
    label_value String comment '標籤值',
    object_bitmap AggregateFunction(groupBitmap, UInt32) comment '標籤值'
)
    engine = AggregatingMergeTree PARTITION BY label_type
        ORDER BY (label_type, label_value)
        SETTINGS index_granularity = 8192;

到後面需求要求對物件id分頁返回,問題就來了,clickhouse的官方沒有bitmap的分頁函式,最原始的解決方案就是把bitmap整個返回,在應用層對bitmap進行切割,這樣導致介面的效能急劇下降。開始萌生了個大膽的想法,給clickhouse新增bitmap分頁函式

開幹

透過閱讀Clickhouse的原始碼,步驟如下:

  1. 實現分頁

在Clickhouse中bitmap指向的class是RoaringBitmapWithSmallSet ,bitmap底層使用的是RoaringBitmap,github地址:https://github.com/RoaringBitmap/CRoaring.gitRoaringBitmapWithSmallSet對rb進行了包裝,在這個類下新增分頁函式

   UInt64 rb_offset_limit(UInt64 offset, UInt64 limit, RoaringBitmapWithSmallSet & r1) const
    {
        if (limit == 0 || offset >= size())
            return 0;

        if (isSmall())
        {
            UInt64 count = 0;
            UInt64 offset_count = 0;
            auto it = small.begin();
            for (;it != small.end() && offset_count < offset; ++it)
                ++offset_count;

            for (;it != small.end() && count < limit; ++it, ++count)
                r1.add(it->getValue());
            return count;
        }
        else
        {
            UInt64 count = 0;
            UInt64 offset_count = 0;
            auto it = rb->begin();
            for (;it != rb->end() && offset_count < offset; ++it)
                ++offset_count;

            for (;it != rb->end() && count < limit; ++it, ++count)
                r1.add(*it);
            return count;
        }
    }
  1. Clickhouse函式定義

FunctionsBitmap.h定義Clickhouse函式

struct BitmapSubsetOffsetLimitImpl
{
public:
    static constexpr auto name = "subBitmap";
    template <typename T>
    static void apply(
        const AggregateFunctionGroupBitmapData<T> & bitmap_data_0,
        UInt64 range_start,
        UInt64 range_end,
        AggregateFunctionGroupBitmapData<T> & bitmap_data_2)
        {
        bitmap_data_0.rbs.rb_offset_limit(range_start, range_end, bitmap_data_2.rbs);
        }
};

using FunctionBitmapSubsetOffsetLimit = FunctionBitmapSubset<BitmapSubsetOffsetLimitImpl>;
  1. Clickhouse函式註冊

FunctionsBitmap.cpp註冊函式

#include <Functions/FunctionFactory.h>

// TODO include this last because of a broken roaring header. See the comment inside.
#include <Functions/FunctionsBitmap.h>


namespace DB
{

void registerFunctionsBitmap(FunctionFactory & factory)
{
    ...
    factory.registerFunction<FunctionBitmapSubsetOffsetLimit>();
    ...
}
}

這樣就完事了,最終這部分的程式碼提交到了Clickhosue倉庫,最終得到了合併,https://github.com/ClickHouse/ClickHouse/pull/27234

後續

後面又來了個需求,要求標籤能夠修改,這又炸了,Clickhosue是不支援修改的,bitmap採用的資料結構是AggregateFunction(groupBitmap, UInt32),groupBitmap的合併邏輯是或運算,內部Clickhosue開發了一種新的資料結構xor_groupBitmap,支援合併邏輯異或運算,變相支援刪除操作,考慮這部分並不通用,所以沒有開源出來

廣州4年java求內推,https://dhbin.cn

相關文章