本文为您介绍Hologres中BSI(Bit-sliced Index)扩展函数的使用。

原理介绍

BSI本质上是对(cid::int, value::bigint)键值数据的压缩。下面以表数据为例为您介绍BSI的原理与结构。

cid

value(十进制)

value(二进制)

1

3

0011

2

6

0110

3

4

0100

4

10

1010

5

7

0111

6

NULL

-

表中包含cid和value列。其中,value的最大值为10,即二进制最大位数为4位,因此将所有value的二进制值补充为4位。然后对二进制数据从低位向高位遍历,记录每位值为1时对应的cid数组的roaringbitmap,形成位切片索引Bit-sliced Index(BSI)。上表数据最终建立了如下切片索引:

  • slice 0:roaringbitmap '{1,5}'

  • slice 1:roaringbitmap '{1,2,4,5}'

  • slice 2:roaringbitmap '{2,3,5}'

  • slice 3:roaringbitmap '{4}'

此外,BSI中还会存储如下信息:

  • Existence bitmap(ebm):存储cid数组(值非空)的roaringbitmap,本示例中为roaringbitmap '{1,2,3,4,5}'。

  • max:value的最大值,本示例为10。

  • min:value的最小值,本示例为3。

  • bitCount:value的二进制位数,本示例为4。

基于上述原理与示例,如果已经通过RoaringBitmap进行位图计算,得到某个人群圈选结果为crowd='{1,3,5}',通过BSI即可继续针对value的高效计算,快速得到结果。比如:

  • 计算圈选的人群对应的value之和:rb_and_cardinality(crowd, slice0) * 20 + ... + rb_and_cardinality(crowd, slice3) * 23 = 14

  • 计算圈选的人群对应的value的Top 2:

    crowd & slice3 = NULL
    crowd & slice2 = {3,5}		-- top2为{3,5}
    {3,5} & slice1 = {5}		-- top1为{5}

通过BSI算法,对键值数据进行压缩存储,可以将cid的RoaringBitmap计算与value的计算完美结合,有效提升用户画像分析场景下“属性标签”与“行为标签”结合的计算效率。基于BSI函数的用户画像分析方案请参见画像分析 - BSI优化方案(Beta)

使用限制

  • 仅Hologres V2.1版本起支持BSI函数,如果您的实例版本为V2.1以下版本,请先升级实例版本,详情请参见实例升级

  • BSI函数中的各个值仅支持正整数(1 ~ 231-1),不支持0和负数。

  • 需要Superuser执行以下语句开启两个Extension。创建Extension是DB级别,一个DB只需要开启一次即可。

    --创建extension。创建bsi需要先创建roaringbitmap
    CREATE EXTENSION roaringbitmap;
    CREATE EXTENSION bsi;
    
    --删除extension
    DROP EXTENSION roaringbitmap;
    DROP EXTENSION bsi;
    重要

    不推荐使用DROP EXTENSION <extension_name> CASCADE;命令级联卸载Extension。CASCADE(级联)删除命令不仅会删除指定扩展本身,还会一并清除扩展数据(例如PostGIS数据、RoaringBitmap数据、Proxima数据、Binlog数据、BSI数据等)以及依赖该扩展的对象(包括元数据、表、视图、Server数据等)。

函数列表

BSI构造函数

函数名

输入

输出

描述

示例

返回结果

bsi_build

integer[], bigint[]

bsi

通过两个一维数据创建一个BSI。

SELECT bsi_iterate(bsi_build('{1,2,3}','{2,4,6}'));
{1,2}
{2,4}
{3,6}

bsi_add_value

bsi, integer, bigint

bsi

为BSI增加一个键值对。

SELECT bsi_iterate(bsi_add_value(bsi_build('{1,2,3}','{2,4,6}'),4,8));
{1,2}
{2,4}
{3,6}
{4,8}

BSI展开函数

函数名

输入

输出

描述

示例

返回结果

bsi_iterate

bsi

set of integer[]

将BSI按键值展开。

SELECT bsi_iterate(bsi_build('{1,2,3}','{2,4,6}'));
{1,2}
{2,4}
{3,6}

bsi_show

bsi/bytea, integer

text

将BSI按键值展开,并展示前integer个键值对。

SELECT bsi_show(bsi_build('{1,2,3}','{2,4,6}'),2);
1=2,2=4...left 1

BSI查询函数

函数名

输入

输出

描述

示例

返回结果

bsi_ebm

bsi/bytea

roaringbitmap

查询BSI的ebm数组的roaringbitmap。

SELECT rb_to_array(bsi_ebm(bsi_build('{1,2,3}','{2,4,6}')));
{1,2,3}

bsi_eq

bsi, bigint [, bytea]

roaringbitmap

查询BSI value等于bigint的部分,返回对应ebm数组的roaringbitmap。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行比较查询。

SELECT rb_to_array(bsi_eq(bsi_build('{1,2,3,4}','{2,4,4,8}'),4));
{2,3}

bsi_filter

bsi/bytea, bytea

bsi

查询BSI的ebm和指定bytea的交集部分,返回新的BSI。

SELECT bsi_iterate(bsi_filter(bsi_build('{1,2,3}','{2,4,6}'),rb_build('{1,2}')));
{1,2}
{2,4}

bsi_ge

bsi, bigint [, bytea]

roaringbitmap

查询BSI的value大于等于bigint的部分,返回对应ebm组成的roaringbitmap。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行比较查询。

SELECT rb_to_array(bsi_ge(bsi_build('{1,2,3,4}','{2,4,4,8}'),4));
{2,3,4}

bsi_gt

bsi, bigint [, bytea]

roaringbitmap

查询BSI的value大于bigint的部分,返回对应ebm组成的roaringbitmap。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行比较查询。

SELECT rb_to_array(bsi_gt(bsi_build('{1,2,3,4}','{2,4,4,8}'),4));
{4}

bsi_le

bsi, bigint [, bytea]

roaringbitmap

查询BSI的value小于等于bigint的部分,返回对应ebm组成的roaringbitmap。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行比较查询。

SELECT rb_to_array(bsi_le(bsi_build('{1,2,3,4}','{2,4,4,8}'),4));
{1,2,3}

bsi_lt

bsi, bigint [, bytea]

roaringbitmap

查询BSI的value小于bigint的部分,返回对应ebm组成的roaringbitmap。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行比较查询。

SELECT rb_to_array(bsi_lt(bsi_build('{1,2,3,4}','{2,4,4,8}'),4));
{1}

bsi_neq

bsi, bigint [, bytea]

roaringbitmap

查询BSI的value不等于bigint的部分,返回对应ebm组成的roaringbitmap。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行比较查询。

SELECT rb_to_array(bsi_neq(bsi_build('{1,2,3,4}','{2,4,4,8}'),4));
{1,4}

bsi_range

bsi, bigint, bigint [, bytea]

roaringbitmap

查询BSI的value介于两个bigint之间(两端闭区间)的部分,返回对应ebm组成的roaringbitmap。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行比较查询。

SELECT rb_to_array(bsi_range(bsi_build('{1,2,3,4}','{2,4,4,8}'),3,5));
{2,3}

bsi_compare

text, bsi, [bytea,] bigint, bigint

roaringbitmap

对BSI进行比较过滤查询。

  • text定义比较类型,支持LT/LE/GT/GE/EQ/NEQ/RANGE。

  • bytea(可选)用于对bsi进行过滤。

  • bigint定义用于比较的值,仅RANGE比较需要两个bigint入参。

SELECT rb_to_array(bsi_compare('RANGE',bsi_build('{1,2,3,4}','{2,4,4,8}'),3,5));
{2,3}

BSI聚合分析函数

函数名

输入

输出

描述

示例

返回结果

bsi_add

bsi, bsi

bsi

将两个BSI相同ebm对应的value相加,返回新的BSI。

SELECT bsi_iterate(bsi_add(bsi_build('{1,2,3}','{2,4,6}'),bsi_build('{1,2}','{2,4}')));
{1,4}
{2,8}
{3,6}

bsi_add_agg

bsi

bsi

求和聚合计算。

SELECT bsi_iterate(bsi_add_agg(bsi_build('{1,2,3}','{2,4,6}')));
{1,2}
{2,4}
{3,6}

bsi_merge

bsi, bsi

bsi

将两个BSI合并,要求两个BSI的ebm没有交集。

SELECT bsi_iterate(bsi_merge(bsi_build('{1,2}','{2,4}'),bsi_build('{3,4}','{6,8}')));
{1,2}
{2,4}
{3,6}
{4,8}

bsi_merge_agg

bsi

bsi

合并聚合计算,要求BSI的ebm没有交集。

SELECT bsi_iterate(bsi_merge_agg(bsi_build('{1,2,3}','{2,4,6}')));
{1,2}
{2,4}
{3,6}

bsi_stat

bigint[], bsi/bytea [, bytea]

text

BSI value的分布统计。分布区间根据输入的边界值数组切分。

如果有第三入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再进行分布统计。

SELECT bsi_stat('{1,3,5}',bsi_build('{1,2,3,4}','{2,4,6,8}'));
(0,1]=0;(1,3]=1;(3,5]=1;(5,8]=2

bsi_sum

bsi/bytea [, bytea]

bigint[]

返回BSI value之和sum以及ebm基数cardinality组成的数组。

如果有第二入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再计算sum与基数。

SELECT bsi_sum(bsi_build('{1,2,3,4}','{2,4,6,8}'));
{20,4}

bsi_topk

bsi/bytea, [bytea, ] integer

roaringbitmap

返回BSI top k个最大value对应的ebm组成的roaringbitmap。

如果有第二入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再计算top K。

SELECT rb_to_array(bsi_topk(bsi_build('{1,2,3,4,5}','{2,4,6,8,10}'),3));
{3,4,5}

bsi_transpose

bsi/bytea [, bytea]

roaringbitmap

返回BSI的value转置结果,即去重后组成的roaringbitmap。

如果有第二入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再计算转置。

SELECT rb_to_array(bsi_transpose(bsi_build('{1,2,3,4,5}','{2,4,4,8,8}')));
{2,4,8}

bsi_transpose_with_count

bsi/bytea [, bytea]

bsi

将BSI的value转置并统计次数,返回新的BSI。

如果有第二入参roaringbitmap类型序列化的bytea,则先查询BSI的ebm和bytea的交集部分,再计算转置并统计。

SELECT bsi_iterate(bsi_transpose_with_count(bsi_build('{1,2,3,4,5}','{2,4,4,8,8}')));
{2,1}
{4,2}
{8,2}