乐趣区

关于clickhouse:Clickhouse添加bitmap分页函数水了个PR

起因

在做标签引擎的时候,咱们在采纳了 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.git,RoaringBitmapWithSmallSet 对 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

退出移动版