Hologres RoaringBitmap在Lazada选品平台的最佳实践

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
简介: Hologres RoaringBitmap在Lazada选品平台的最佳实践

Lazada选品平台包含全网商家、商品的圈选,通过Hologres RoaringBitmap能力帮助业务突破选品池20w大小限制,6000+选品池调度完成由12h下降至1h,单个选品池调度时间由90s下降至2s。


选品平台介绍


Lazada选品平台是面向内部运营小二选品的操作系统,其核心能力是:通过用户制定的规则条件组合以筛选出满足期望的业务实体,这里的业务实体包括但不仅限于:商品、商家。站在电商运营体系上来看,选品系统的核心职能在于  选 和 供,选出合适的 “品”(商品、商家... ),供给到对应的 “渠道”  (会场、频道、JFY...)。通过供和选核心能力的组合,选品平台在营销活动链路中作为数据通路,打通从商家到消费者的核心链路,并且在导购链路赋能了会场、FlashSale、JFY等等诸多业务,覆盖了Lazada的绝大数场景化供给。


从选的角度来讲,可以用于运营范围圈定、业务准入门槛、大促会场和日常频道投放等。在大促招商以及商家运营系统中,商家范围界定就成为整个运营流程的起点也是最值得关注的一点,限定满足预期的商家会让后续的流程更加顺滑也势必会取得更好的效果;在各种官方营销活动中,各行业运营会根据商家层级、货品类目、成交表现等为商家提供特定报名入口,为消费者提供更匹配的货品,选品成为不同层次门槛制定的有力抓手。从供的角度来讲,多货品集交并差供给、离线供给、打标供给、渠道供给等能力让选品成为数据通路的统一出口。这里供给的含义不单纯是将数据以在线或者离线的方式提供给下游,可能是一次打标任务、一次 excel 导出、也可能是持续的增量打去标任务。


image.png

Hologres(原交互式分析)是一站式实时数据仓库引擎,支持海量数据实时写入、实时更新、实时分析,支持标准SQL(兼容PostgreSQL协议),支持PB级数据多维分析(OLAP)与自助分析(Ad   Hoc),支持高并发低延迟的在线数据服务(Serving),与MaxCompute、Flink、DataWorks深度融合,提供离在线一体化全栈数仓解决方案。


在过去几个月,我们把选品平台的底层数据引擎从Ha3替换成Hologres,并使用DataWorks+Flink构建了一套和Saro类似的离在线数据同步解决方案,打破了使用搜索引擎支撑选品的传统,开创了OLAP数据库在选品平台上应用的先河。


RoaringBitmap科普


Bitmap


在介绍RoaringBitmap之前,先来了解下Bitmap。


Bitmap的基本思想就是用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。


假设有这样一个需求:在20亿个随机整数中找出某个数m是否存在其中,并假设32位操作系统,4G内存在Java中,int占4字节,1字节=8位(1 byte = 8 bit)


如果每个数字用int存储,那就是20亿个int,因而占用的空间约为 (2000000000*4/1024/1024/1024)≈7.45G如果按位存储就不一样了,20亿个数就是20亿位,占用空间约为 (2000000000/8/1024/1024/1024)≈0.233G


假设有四个整数:int[ ] a = { 0, 3, 5, 7 },使用下图的Bitmap表示,表格标记了0到7的数字,其中0、3、5和7被标记为1:


image.png

因为1Int = 4Byte = 32Bit,所以一个Int所占的二进制位可以映射32个数字,相当于压缩了32倍,那么之前20亿个整数所需要的7.45GB内存可缩减至233.41MB。


Bitmap的问题在于,不管业务中实际的元素基数有多少,它占用的内存空间都恒定不变。如果Bitmap中的位的取值范围是1到100亿之间,那么Bitmap就会开辟出100亿Bit的存储空间。但是如果实际上值只有100个的话,100亿Bit的存储空间只有100个Bit为1,其余全部为0,数据存储空间浪费严重,数据越稀疏,空间浪费越严重。


RoaringBitmap


为了解决Bitmap稀疏存储浪费空间的问题,出现了很多稀疏位图的压缩算法,RoaringBitmap就是其中的优秀代表。


RoaringBitmap是高效压缩位图,简称RBM。RBM的历史并不长,它于2016年由S.  Chambi、D. Lemire、O. Kaser等人在论文《Better bitmap performance with Roaring  bitmaps》与《Consistently faster and smaller compressed bitmaps with  Roaring》中提出。


RoaringBitMap的基本思路:


将数据的前半部分,即高16位作为桶的编号,分为65536个桶,RBM中将这些小桶称之为Contriner(容器)

存储数据时,按照数据的高16位做为Contriner的编号去找对应的Contriner(找不到就创建对应的Contriner),再将低16位放入该Contriner中:


image.png

与Bitmap相比的优势


空间节省:Bitmap比较适用于数据分布比较稠密的存储场景中;RoarringBitMap在稀疏存储上空间更省,稠密和Bitmap一致。


更高效的做交、并操作:将大块的Bitmap分成各个小块,其中每个小块在需要存储数据的时候才会存在。所以当进行交集或并集运算的时候,只需要去计算存在的一些块,而不需要像Bitmap那样对整个大的块进行计算。这里既不是用空间换时间,也没有用时间换空间,而是用逻辑的复杂度同时换取了空间和时间。


集合运算性能下面这段代码测试RoaringBitmap与数组在交集运算上的性能差异:


RoaringBitmap rbAnd1 = new RoaringBitmap();
for (int k = 100000; k < 200000; k++) {
    rbAnd1.add(k);
}

RoaringBitmap rbAnd2 = new RoaringBitmap();
for (int k = 150000; k < 200000; k++) {
    rbAnd2.add(k);
}

Long start = System.currentTimeMillis();
rbAnd1.and(rbAnd2);
System.out.println("roaringBitmap and 耗时:" + (System.currentTimeMillis() - start) + "ms");


List<Integer> list1 = new ArrayList<>();
for (int k = 100000; k < 200000; k++) {
    list1.add(k);
}

List<Integer> list2 = new ArrayList<>();
for (int k = 150000; k < 200000; k++) {
    list2.add(k);
}

start = System.currentTimeMillis();
list1.retainAll(list2);
System.out.println("list and 耗时:" + (System.currentTimeMillis() - start) + "ms");


测试结果:


roaringBitmap and 耗时:1ms
list and 耗时:3056ms


下面这段代码测试RoaringBitmap与数组在差集运算上的性能差异:


RoaringBitmap rbAnd1 = new RoaringBitmap();
for (int k = 100000; k < 200000; k++) {
    rbAnd1.add(k);
}

RoaringBitmap rbAnd2 = new RoaringBitmap();
for (int k = 150000; k < 200000; k++) {
    rbAnd2.add(k);
}

Long start = System.currentTimeMillis();
rbAnd1.andNot(rbAnd2);
System.out.println("roaringBitmap andNot 耗时:" + (System.currentTimeMillis() - start) + "ms");


List<Integer> list1 = new ArrayList<>();
for (int k = 100000; k < 200000; k++) {
    list1.add(k);
}

List<Integer> list2 = new ArrayList<>();
for (int k = 150000; k < 200000; k++) {
    list2.add(k);
}

start = System.currentTimeMillis();
list1.removeAll(list2);
System.out.println("list andNot 耗时:" + (System.currentTimeMillis() - start) + "ms");


测试结果:


roaringBitmap andNot 耗时:1ms
list andNot 耗时:3350ms


从测试结果不难看出,在集合传统的交差集运算上,RoaringBitmap具有无与伦比的速度优势。


RoaringBitmap实践


指标存储


前文提到我们把选品平台的底层数据引擎升级成Hologres,Hologres数仓存储了lazada全平台的商品,按目前运营小二组合商品表现数据来选品的习惯,我们把商品的表现数据抽象成商品指标存储在大宽表(为什么叫大宽表,因为随着业务关注视角的不同,指标越来越多,目前已经接近300个字段)。


其中商品指标不乏多值字段,如:商品所属类目、商品已报名活动、商品所属行业...,存储这些多值字段,我们一开始采用数组类型字段进行存储。如果运营小二使用了多值指标进行选品,规则翻译模块会把运营的输入翻译成数组的交集运算提交到Hologres执行。直到我们从Hologres的监控发现,CPU偶有冲高到100%的情况:

image.png

通过抓取该段时间的慢SQL,我们发现导致CPU冲高的大部分SQL都是数组取交集的运算。

image.png

问题定位到了,当时想到两种优化方案:


多值字段从数组换成RoaringBitmap,RoaringBitmap是一种高效的Bitmap压缩算法,目前已被广泛应用在各种语言和各种大数据平台。适合计算超高基维的,常用于去重、标签筛选、时间序列等计算中。


多值字段依旧使用数组,数据落表时先排好序再写入,在查询取交集时,传入比较的数组也排好序,即“先排序再比较”的策略。


两种优化方案优劣比较:

image.png

离线写入:即商品大宽表数据由MaxCompute写入到Hologres,选品平台的数据层一般由离线数据和在线数据两部分组成


综上,字段类型修改为RoaringBitmap,离线写入的耗时增加1min左右 CPU消耗增加10%,对离线链路构建时长的影响可以忽略;查询性能大幅优于有序的数组和现有无序的数组。


基于此,我们把商品大宽表的23个多值字段由数组类型升级成RoaringBitmap,整体优化收益如下:


商品大宽表存储空间下降30% ~ 40%,存储文件个数下降30% ~ 40%(不同国家有所差异,整体在30% ~ 40%)。文件个数减少有以下几个好处:


提高查询性能:更少的文件意味着查询需要扫描的数据量和文件数量都较少,从而提高查询速度和系统整体性能。


减少存储开销:小文件会在硬盘上产生更多的碎片,导致磁盘空间利用率降低,文件个数减少有助于提高磁盘空间利用率。


简化管理:当文件数量较多时,管理和监控任务会变得更加复杂。文件个数减少能够简化管理和维护工作,提高运维效率。


优化备份和恢复:备份大量小文件通常比备份少量大文件所需的时间更长。文件个数减少可以缩短备份和恢复的时间,提高业务连续性。


减轻元数据压力:文件系统需要跟踪每个文件的信息(如权限、大小等),大量的小文件会增加元数据的压力。文件个数减少有助于减轻元数据服务器的压力,保证系统的稳定运行。Hologres CPU利用率下降30%


image.png

HologresRoaringBitmap类型字段建表语句参考:


BEGIN;

CREATE TABLE public.test (
    product_id bigint NOT NULL,
    product_categories roaringbitmap,
    ds text NOT NULL,
    PRIMARY KEY (product_id, ds)
) PARTITION BY LIST (ds);

CALL set_table_property('public.test', 'orientation', 'column,row');
CALL set_table_property('public.test', 'distribution_key', 'product_id');

END;


选品池运算


选品池:符合运营小二选品规则的业务实体(商品、商家...)的集合,选品池数据一般为商品ID或商家ID的集合

“供给”是选品平台的核心能力,围绕着“供给”,我们构建了选品池持续的增量打标/去标的核心能力。打标供给核心流程如下:

image.png

当前打标供给流程核心问题定义:


数据冗余:因为需要对选品池今天和昨天的数据进行比较,选品池今天的数据从选品引擎实时拉取,选品池昨天的数据冗余到Mysql以方便第二天进行比较


选品池运算:今天和昨天的选品池数据对比,在应用内存中进行,如果选品池数据量过大,容易触发应用频繁的FullGC。技术通过设置20w上限来规避这个问题,业务对突破20w的述求越来越强烈


调度效率低:6000+选品池调度完成,要12h,单个选品池调度完成花费80~90s


在了解到RoaringBitmap在集合存储具有较大的空间优势,在集合运算具有较大的时间优势,我们决定使用RoaringBitmap来存储选品池数据。


在RoaringBitmap落地过程中,遇到了很多困难和挑战,罗列其中一二:


为了解决数据冗余的问题,我们限定选品池数据不出Hologres数仓,也就是要借助Hologres的RoaringBitmap字段类型来存储选品池数据。但Hologres的RoaringBitmap字段类型只支持32位整数存储,商品ID和商家ID显然已经超过了32位整数的范围。于是,问题就演化成了RoaringBitmap怎么能存下64位整数。从实现复杂度,以及对未来SKU选品池支撑友好程度等多方面综合考虑,我们最终采用分桶法。


偏移法:


image.png

分桶法:将商品ID或商家ID拆为低位和桶号,这里我们直接将商品ID的高34位作为桶号,将低30位存入RoaringBitmap

image.png

自定义id:因为商品ID或商家ID均存在较大的断层,实际上商品数量和商家数量远未达到32位整数的上限,内部维护一个自增长ID去映射商品ID或商家ID


在攻克了诸多困难和挑战后,打标供给整体实现方案简化为如下流程:

image.png

选品池运算核心逻辑:


image.png

优化整体收益:


数据流转效率:6000+选品池调度完成由12h下降至1h,单个选品池调度时间由90s下降至2s


系统稳定性:应用层FullGC次数下降88%


存储成本:消除数据冗余,节省75GB存储空间


业务突破:帮助业务突破选品池20w大小限制,目前暂定50w


Hologres RoaringBitmap分桶表建表语句参考:



CREATE TABLE public.test_pool_dump_rb (
    pool_id bigint NOT NULL,
    pool_type text NOT NULL,
    bucket bigint NOT NULL,
    pool_data roaringbitmap NOT NULL,
    ext text NOT NULL,
    ds text NOT NULL
    ,PRIMARY KEY (pool_id, bucket, ds)
)
  PARTITION BY LIST (ds);

CALL set_table_property('public.test_pool_dump_rb', 'orientation', 'column,row');
CALL set_table_property('public.test_pool_dump_rb', 'clustering_key', 'pool_id:asc');
CALL set_table_property('public.test_pool_dump_rb', 'distribution_key', 'pool_id');
CALL set_table_property('public.test_pool_dump_rb', 'table_storage_mode', 'any');
CALL set_table_property('public.test_pool_dump_rb', 'auto_partitioning.enable', 'true');
CALL set_table_property('public.test_pool_dump_rb', 'auto_partitioning.time_unit', 'day');
CALL set_table_property('public.test_pool_dump_rb', 'auto_partitioning.time_zone', 'ASIA/SHANGHAI');
CALL set_table_property('public.test_pool_dump_rb', 'auto_partitioning.num_precreate', '2');
CALL set_table_property('public.test_pool_dump_rb', 'auto_partitioning.num_retention', '2');
CALL set_table_property('public.test_pool_dump_rb', 'auto_partitioning.num_hot', '-1');

END;


导出选品池数据到RoaringBitmap SQL参考:


insert into test_pool_dump_rb_${ds}
(pool_id, pool_type, bucket, pool_data, ext, ds)
select
#{poolId} as pool_id,
#{poolType} as pool_type,
product_id >> 30 as bucket,
rb_build(array_agg(${product_id} <![CDATA[&]]> 1073741823)) as pool_data,
#{ext} as ext,
#{ds} as ds
from (
    select product_id from test
) A
group by product_id >> 30


选品池差集运算SQL参考:


select A.pool_id, A.pool_type, A.bucket, rb_to_array(A.pool_data - if(B.pool_data is null, rb_build('{}'), B.pool_data)) as pool_data
from
(
    select pool_id, pool_type, bucket, pool_data
    from test_pool_dump_rb
    where pool_id = #{poolId}
    and ds = #{ds}
) A left join
(
    select pool_id, bucket, pool_data
    from test_pool_dump_rb
    where pool_id = #{poolId}
    and ds = #{differenceDs}
) B on A.pool_id = B.pool_id and A.bucket = B.bucket


总   结


RoaringBitmap的成功应用,不仅是对业务功能的强力助推,彰显了技术对业务突破的关键赋能作用,更是先进技术驱动生产效率提升的典范。RoaringBitmap通过其高效的压缩机制大幅优化数据存储(空间效率),以及卓越的集合处理能力显著提高操作性能(时间效率),有效克服了业务在选品池规模上的原有约束,实现了对20万条数据容量限制的突破。这不仅证明了RoaringBitmap技术的先进性,也是技术创新提升核心竞争力的生动实例。

相关实践学习
基于Hologres轻量实时的高性能OLAP分析
本教程基于GitHub Archive公开数据集,通过DataWorks将GitHub中的项⽬、行为等20多种事件类型数据实时采集至Hologres进行分析,同时使用DataV内置模板,快速搭建实时可视化数据大屏,从开发者、项⽬、编程语⾔等多个维度了解GitHub实时数据变化情况。
相关文章
|
3月前
|
SQL DataWorks 关系型数据库
DataWorks+Hologres:打造企业级实时数仓与高效OLAP分析平台
本方案基于阿里云DataWorks与实时数仓Hologres,实现数据库RDS数据实时同步至Hologres,并通过Hologres高性能OLAP分析能力,完成一站式实时数据分析。DataWorks提供全链路数据集成与治理,Hologres支持实时写入与极速查询,二者深度融合构建离在线一体化数仓,助力企业加速数字化升级。
|
8月前
|
SQL 消息中间件 Kafka
Flink+Paimon+Hologres,面向未来的一体化实时湖仓平台架构设计
本文介绍了阿里云实时数仓Hologres负责人姜伟华在Flink Forward Asia 2024上的分享,涵盖实时数仓的发展历程、从实时数仓到实时湖仓的演进,以及总结。文章通过三代实时数仓架构的演变,详细解析了Lambda架构、Kafka实时数仓分层+OLAP、Hologres实时数仓分层复用等方案,并探讨了未来从实时数仓到实时湖仓的演进方向。最后,结合实际案例和Demo展示了Hologres + Flink + Paimon在实时湖仓中的应用,帮助用户根据业务需求选择合适的方案。
1202 20
Flink+Paimon+Hologres,面向未来的一体化实时湖仓平台架构设计
|
9月前
|
SQL 监控 关系型数据库
用友畅捷通在Flink上构建实时数仓、挑战与最佳实践
本文整理自用友畅捷通数据架构师王龙强在FFA2024上的分享,介绍了公司在Flink上构建实时数仓的经验。内容涵盖业务背景、数仓建设、当前挑战、最佳实践和未来展望。随着数据量增长,公司面临数据库性能瓶颈及实时数据处理需求,通过引入Flink技术逐步解决了数据同步、链路稳定性和表结构差异等问题,并计划在未来进一步优化链路稳定性、探索湖仓一体架构以及结合AI技术推进数据资源高效利用。
655 25
用友畅捷通在Flink上构建实时数仓、挑战与最佳实践
|
7月前
|
SQL 消息中间件 Serverless
​Flink+Paimon+Hologres,面向未来的一体化实时湖仓平台架构设计
​Flink+Paimon+Hologres,面向未来的一体化实时湖仓平台架构设计
180 4
|
9月前
|
SQL 存储 JSON
实时数仓 Hologres 产品介绍:一体化实时湖仓平台
本次方案的主题是实时数仓 Hologres 产品介绍:一体化实时湖仓平台,介绍了 Hologres 湖仓存储一体,多模式计算一体、分析服务一体和 Data+AI 一体四方面一体化场景,并对其运维监控方面及客户案例进行一定讲解。 1. Hologres :面向未来的一体化实时湖仓 2. 运维监控 3. 客户案例 4. 总结
566 14
|
数据可视化 数据挖掘 OLAP
基于 Hologres 搭建轻量 OLAP 分析平台评测报告
【9月更文第6天】开作为互联网手游公司的产品经理和项目经理,数据分析对于我们的业务至关重要。我们一直在寻找高效、可靠的数据分析解决方案,以更好地了解玩家行为、优化游戏体验和提升运营效率。近期,我们体验并部署了《基于 Hologres 搭建轻量 OLAP 分析平台》解决方案,以下是我们对该方案的评测报告。
163 12
基于 Hologres 搭建轻量 OLAP 分析平台评测报告
|
12月前
|
DataWorks 数据挖掘 关系型数据库
基于hologres搭建轻量OLAP分析平台解决方案评测
一文带你详细了解基于hologres搭建轻量OLAP分析平台解决方案的优与劣
895 10
|
存储 SQL OLAP
分析性能提升40%,阿里云Hologres流量场景最佳实践
分析性能提升40%,阿里云Hologres流量场景最佳实践
|
消息中间件 Java 数据库连接
Hologres 数据导入与导出的最佳实践
【9月更文第1天】Hologres 是一款高性能的实时数仓服务,旨在提供快速的数据分析能力。无论是从外部数据源导入数据还是将数据导出至其他系统,都需要确保过程既高效又可靠。本文将详细介绍如何有效地导入数据到 Hologres 中,以及如何从 Hologres 导出数据。
480 1
|
存储 SQL 人工智能
【云栖实录】Hologres3.0全新升级:一体化实时湖仓平台
2024年云栖大会,Hologres 3.0全新升级为一体化实时湖仓平台,通过统一数据平台实现湖仓存储一体、多模式计算一体、分析服务一体、Data+AI 一体,发布 Dynamic Table、External Database、分时弹性、Query Queue、NL2SQL 等众多新的产品能力,实现一份数据、一份计算、一份服务,极大提高数据开发及应用效率。同时,Hologres 的预付费实例年付折扣再降15%,仅需7折,不断帮助企业降低数据管理成本,赋能业务增长。