共计 9936 个字符,预计需要花费 25 分钟才能阅读完成。
简介:本文次要介绍如何通过 MaxCompute 进行海量人群的标签加工,通过 Hologres 进行剖析建模,从而反对大规模人群简单圈选场景下的交互式体验,以及基于 API 的数据服务最佳实际。
本文作者 刘一鸣 阿里云智能 高级产品专家
人群圈选零碎根本逻辑架构
人群圈选并不是一个新业务,简直所有的互联网公司都在做,因为这是一个根本营销场景,选定的人群要发代金券,要导入流量,要做针对性促销,要抉择适合的人群,那怎么做这件事件呢?实际上要通过人群的行为特色,洽购个性,关注特色,趣味个性,甚至是教育水平等等,把人群划分成不同的组。通过划分人群组,在无限的营销估算外面,将资源投放给转化率或点击率最高的人群。
根本的业务架构逻辑如下图,自下而上。首先是标签加工引擎,次要以离线加工为主。在标签加工引擎内,会对用户历史的洽购行为、拜访行为、关注行为等等做很多标签,能够统计进去,哪些人对哪些商品关注过多少次,点击过多少次,注意过多少次等等,会有很多统计性的属性在外面。这些标签会导入到在线画像服务引擎内,服务引擎是给经营人员,广告主进行交互式的查问,因为要依据用户行为特色,筛选出最关注的用户群体。这个用户群体可能是 30 天内关注某些商品然而没有买的群体,或者是相干的上下游产品,通过行为特色筛选进去。筛选过程是一个高度交互过程,因为一个人的行为特色是非常复杂的。所以须要频繁的抉择某个条件,去掉某个条件,条件和条件之间可能会做合并、去重的操作等等。直到把人群大小限定到估算可反对的范畴内,比方咱们要投递给 1 万集体广告,那要通过各种限定条件把这 1 万集体找进去。找进去还不能做间接投递,还须要对人群做更细粒度的剖析,通过历史数据行为剖析这 1 万集体是不是想要的指标人群。之后会把目标群体以投递包的模式,导出给投递零碎。这是一个根本业务逻辑。
那这个业务需要的背地技术要求是什么呢?
典型人群业务版块的外围是洞察剖析,洞察剖析个别的场景是,要撑持几万个不同的广告主,他们会在平台上自由选择更感兴趣的人群,每个广告主对人群的诉求是不一样的,某些人关注的购买力,有些关注的是珍藏行为。每天数万广告主收回数百万次的查问申请,构建数万次各种人群包,各个系统的计算复杂度是要求十分高的。这里的外围诉求蕴含几个点,毫秒级洞察,因为所有的查问心愿是交互式的,须要在界面上每一次互动,每一次下拉菜单,每一次抉择,每一次条件组合,都心愿看到一个互动后果,整个人群是变大还是变小,指标人群是不是跟冀望的类似。这一点对性能要求是十分大的。同时揭示大家,数据肯定要脱敏解决,爱护好用户的个人隐私,所有的剖析都是建设在合规数据根底之上的剖析。
人群圈选零碎服务引擎外围诉求
规模数据上的交互式剖析性能
数万广告主提交数百万次的数据查问,须要毫秒级的响应,查的快是必须的。这个快加了一个限定词,是规模数据。百万级不算规模,行为日志是十分大的,心愿是百亿级别以上,仍旧有一个很好的交互式剖析能力,可能在秒级响应。
灵便筛选能力
用户的筛选行为多种多样,等值比拟、数值大小范畴比拟、工夫范畴比拟等。各种各样的筛选条件可能灵便组合,表白这些筛选后果就体现进去计算引擎的能力。
高吞吐更新能力
用户标签并不是动态的,以后所有实时化,所有在线化,所有的行为数据变动,都心愿可能实时触发,实时反馈下一时刻的零碎决策。比方最新收藏夹里放了什么商品,这种行为能不能成为在线画像的一部分。所以对高实时的吞吐能力要求会很高。
从计算层面来讲,能够分成下图几种计算模式。
标签过滤分为等值过滤,能够用 Equal/In/Between,这些过滤能够在百亿级别上进行操作。操作之后的后果集,要做很多的交差并集,举个常见例子,一个用户既关注了竞品品牌也关注了本公司商品,却没有买,这外面其实有并的关系,有差的关系,有交的关系。所以这些人群关系之间要组合,有很高的交差并集计算。最初还有很强的准确去重的需要,因为最终要把计算结果,变成一个惟一定位用户的 ID,这个 ID 会用来做广告的投递。那这些需要,在引擎层面上就是数据读取效率怎么样,如果用行存读取是不是会呈现 IO 放大的问题,数据按行去存,真正过滤是依照某一列过滤,然而 IO 读取,会把整行读取,会呈现 IO 放大问题。列存还会有索引问题、过滤成果问题。计算算子上表连贯时是 Hash JOIN 形式还是用 Nest Loop JOIN 形式。准确去重的成果如何。这些都是对计算引擎效率上有很高的要求。所以实质上是要解决高效数据存储与过滤、关系运算内存 /CPU 耗费、准确去重内存 /CPU 耗费问题。
这里就有很多不同的解决优化思路,是用更多的内存还是 CPU。行业内大抵的思路有两种。
一种是通过预结算思路,有 Kylin/Druid 这样的技术。这些技术能够在一些预约义的维度上,进行一次提前的预加工。预加工后,数据集会在实质上进行缩小。比方要找一个用户群体,关注了第一个商品却没有关注第二个商品。每一个后果集都能够用 bitmap 数组来表白,数组之间做交差并集效率是十分高的。预计算技术实际上是把准确去重和交差并集上计算是有很大益处的。但缺点也比拟显著,最大的缺点就是不灵便,同时残缺 SQL 表达能力也比拟弱。另一种是属于 MPP 分布式数据库技术,一些通过列存、分布式、索引形式提供更好的查问性能。
所以真正落地一套人群筛选计划时,个别不是只抉择一个计划。因为不论是预计算计划还是 MPP 计划都有一些实质的缺点。
那市场上哪些技术更适宜做存储和查问呢?
第一类技术,大家都比拟相熟的事务数据库。事务数据库是行存储,对单行数据写入存储效率是十分高的,用来做查问,做过滤统计,在千万级以上会发现耗费资源是十分大的。所以个别不会拿 TP 零碎间接做剖析操作。
第二类零碎,AP 零碎,是咱们常见 OLAP 零碎。这一类零碎针对大规模数据扫描场景做了优化,包含利用分布式技术,列存技术,压缩技术、索引的技术等等。这类技术查的都很快,但实质缺点是大部分零碎更新上做的不太敌对,因为数据查的快,所以数据该紧凑紧凑,该压缩压缩,所以在更新能力上弱一些。还有一类零碎,在大数据分析也常见,咱们把它叫 Serving 零碎,反对在线业务的一类零碎,这类零碎查的是足够快,但就义的其实是查问的灵活性。比方,文档数据库、KeyValue 零碎查问形式有很大的局限,只能依照它的 key 去查问。这样灵活性缩小了,然而性能上有限放大,因为能够横向扩大,因为 key 相对来说拜访效率是最高的,而且更新效率也十分高,依照 key 更新,能够替换整条记录。咱们过来就不得不针对不同场景,把数据拆分到 TP、AP、Serving,数据在几个零碎之间来回传递。让咱们对整个零碎的依赖度变的更高,只有数据有一次依赖,就会产生一次数据不统一,产生数据不统一就意味着数据的修改,数据的开发成本变的更高。所以大家都会在很多畛域做翻新,第一类翻新是在 TP 和 AP 畛域里做一个混合负载能力。尝试通过一个技术把这两个场景解决掉。有反对事务,又能反对剖析,也心愿将来有一天这个零碎真正很好的落地。这类零碎也有肯定的局限,要反对事务操作,各种分布式锁开销还是必不可少的。这类零碎因为具备了一些能力,所以在整个并发和性能上,开销是比拟大的,所以有肯定的性能瓶颈。
在下图左侧局部也是能够做一些翻新的,左侧的翻新会发现最大的问题是不反对事务。把事务能力弱化,不须要那么多事务,心愿查的足够快,更新的足够快。所以这个中央是有可能做技术创新,这个技术既具备很好的灵便的剖析能力,也具备很好的数据写入能力,有具备残缺的 SQL 表达能力。所以左侧的交加局部的技术,很适宜方才提到的三点技术要求。这就是明天要分享的产品 Hologres。
Hologres= 向量化 SQL 引擎 + 灵便的多维分析 + 高吞吐实时更新
Hologres,一站式实时数仓,提供实时剖析(OLAP)与在线服务(点查)两种能力,与 MaxCompute 无缝买通,实现一套架构,多种负载(OLAP、在线服务、交互式剖析)共存,缩小数据孤岛,防止数据割裂,简化链路,晋升用户体验。
对立存储
一份数据反对多种负载 (OLAP、在线服务、MaxCompute 交互式剖析),缩小数据割裂
数据无孤岛,无频繁数据导入导出,进步数据开发效率、简化链路
对立接口
接口兼容开源 Postgres 协定,反对支流开发和 BI 工具,无需应用层重写,生态凋谢
对立用 SQL 形容多种场景,进步数据利用开发效率
对立数据模型,通过“表”来形容数仓模型,语义统一
实时离线一体
反对实时写入、实时更新、写入即可查,原生集成 Flink
与 MaxCompute 存储无缝买通,通明减速,无需数据挪动,反对交互式剖析能力,反对实时数据关联历史数据
高性能
OLAP 场景性能好于 Clickhouse、Impala、Presto,反对亚秒级响应与高 QPS
在线服务(点查)场景性能好于 HBase,点查反对 100K+QPS
Hologres:一站式实时数仓
Hologres 为什么能反对高性能,高吞吐写入?
实际上没有神秘的中央,Hologres 更多还是依赖于整个 IT 行业,有很多底层技术上的提高。比方,带宽变宽,提早变低。益处是之前必须依赖本地的操作,比方之前依赖本地磁盘,当初能够依赖网盘。其实 Hologres 底层的存储,分多正本存储,高牢靠存储,把这些负责状态治理的事件,都交给阿里云,底层是盘古存储引擎,自带多正本,自带压缩,自带缓存,自带高牢靠。这就会使整个计算节点的逻辑变的轻薄和简略,也让高牢靠更加简略。任何一个节点宕掉之后,能够很快从一个分布式的网盘里复原状态。会让计算层变的无状态,这是第一点。第二点是磁盘的利用,过来磁盘的转速有机械瓶颈。机械磁盘是按圈去转的,一秒钟多少转。所以咱们的 IO 场景都是面向扫描场景做了大量的优化。咱们心愿所有的数据都是以块为单位,进行更新、读写。所以在过来这种高更新场景,在整个数仓里很难实现。Hologres 是采纳 SSD 设计,固态硬盘反对更好的随机读写能力。这让咱们设计存储架构的时能够抛开过来必须依赖于这种扫描场景,去设计整个存储的数据结构。Hologres 能够行存也能够列存,别离适应不同的场景,同时也采纳 log structured merge tree 的形式。反对高吞吐数据的写入和更新的场景。第三个是 CPU 多核化,CUP 的主频曾经不会有实质的晋升。然而在多核化场景下,如果能够把一 CPU 外部多个核并行利用起来,就能把 CPU 资源充分发挥到极致。这就要求对操作系统的底层语言把握的要比拟好,Hologres 应用 C ++ 实现的数仓。Hologres 底层的算子都会用向量化形式重写,尽量施展多核化并行计算能力,吧计算力施展到极致。
从下图能够看出,咱们在网络上、存储上、计算上、硬件层面有很多改良,这些改良都充分发挥进去,可能做出一个不一样的成果的零碎。
人群圈选场景之前提到,既有预计算场景,又有 MPP 分布式计算场景。应用繁多某一个技术往往不太适宜,真正落地的时候,心愿既有预计算又有分布式计算,要把两个技术更好的整合在一起。比方维度过滤场景就很适宜用 BITMAP,因为能够在 BITMAP 上做位图索引。如 true 和 false 的场景,购买级别、对什么产品关注等等,这些须要过滤的场景就适宜做位图索引。Hologres 是反对位图索引的。
第二种是关系运算,关系运算是咱们提到的各种数据集之间的交差并,也非常适合位图计算。因为位图计算相当于是 0 和 1 之间,做很多与或差的操作,而且是并行操作,效率也是十分高的。
准确去重是 BITMAP 天生就具备的能力,因为位图在构建时,就通过下标位,就惟一确定了 ID。通过不同下标位之间下面一的值的简略累加,就能够很快计算出准确去重的值是多少。这简直是把一个 O(N)的问题变成 O(1)的场景,成果也非常明显。所以在做人群圈选场景外面,预计算是很重要的技术。Hologres 反对 RoaringBitmap 数据类型,高效率实现 Bitmap 的穿插并计算。
上文提到预计算是灵活性有余,须要通过分布式计算把计算力施展进去,就用到了 Hologres 的向量化执行引擎。对 MaxCompute 数据表面间接减速,包含 MaxCompute 数据同步到 Hologres 里,是会比 MaxCompute 同步到其它数据源性能进步 10 倍已上。
典型架构图
典型架构图如下,数据源根本是通过埋点数据,通过消息中间件 kafka,第一件工夫投递到 Flink,做一次轻量级数据加工,包含数据治理的修改,数据轻度汇总,数据维度拉宽。其中维度关联是一个很重要的场景,真正的埋点数据都是记录某些 ID,这些 ID 都要转换成有属性意义的维度信息。第一件事就是做维度拉宽,这是就能够应用 Hologres 的行存表,维度关联时,根本是通过主键去关联的,应用 Hologres 的行存表,能够存几亿几十亿的维度信息。这些信息能够实时的被更新。加工的后果集会写到 kafka 外面,因为并不是一次加工,可能是加工几个循环。通过 kafka 做音讯驱动的形式,在 Flink 外面做几次加工,加工的后果基本上双写的场景会比拟多,一部分实时写入 Hologres,另一部分以批量形式写到 MaxCompute 外面。离线数仓到实时数仓是一个很好的数据修改的场景,数据是肯定会被修改的,所以会有大量通过离线数仓对实时数仓进行修改的场景,包含标签加工也是典型的离线数仓来补充实时数仓的场景。所以一些行为是须要通过离线数仓加工好之后,把数据同步到实时数仓里。但有另外一些属性,是跟当下决策有关系的。这些是能够间接写到实时数仓 Hologres 里。所以能够把标签分为离线和实时两局部,实时写到 Hologres,离线通过 MaxCompute 加工后同步到 Hologres。
在对外提供数据服务是,有几种形式。倡议的形式是,对外提供服务时,加一个网关,网关服务外面会做很多限流、熔断等等,这也是能进步数据服务稳定性的一个很好的帮忙。如果是对内应用交互式剖析的长治,能够间接通过 JDBC 的形式连贯 Hologres,如果是一个在线利用,倡议通过 API 网关连贯到 Hologres。
数据结构层
离线数仓加工两张表,一个是用户根底属性表,记录一些用户属性,性别城市年龄等。一个是交易明细表,记录某个人在某一天针对某个商品买过多少,看过多少,珍藏多少等。这些通过离线数仓加工好后,数据导入 Hologres。在通过配置把表列形容信息以人类可读的形式形容进去,再配置相干属性标签。把标签上线后,广告主会通过交互界面进行配置筛选。这种筛选背地都是翻译成各种 SQL 语句,其实就是个各种 SQL 表达式。真正把查问下发到底层引擎。那下发时底层引擎该如何建表呢?
宽表模式
•每行形容一个用户的标签组合,每个 key 是一列,每一行对应 value。
•列不倡议超过 300 列,列多会升高实时写入的性能。分为热点标签和非热点标签
•热点标签独立为列,具备明确的数据类型,能够针对性设计索引,对查问敌对
•非热点标签,通过数组类型和 JSON 反对,适宜动静更新,但索引不是最优,可扩展性更好
•适应场景:维度属性数量较低;实时写入频繁;更新以人的单位
•劣势:开发简略疾速上线
•计划形容:
用户数据:例如 user_tags 表,宽表
行为数据:例如 shop_behavior 表,事实表
更新时,能够实时、批量更新不同的列
案例
-------------------- 用户标签维度表 ---------------------
begin;
-- 3 个热点标签字段(text、integer、boolean 类型),2 个扩大标签字段(text[]类型和 JSON 类型)create table user_tags
(
user_id text not null primary key,
city_id text,
consume_level integer,
marriaged boolean,
tag_array text[],
tag_json json
);
call set_table_property('user_tags', 'orientation', 'column');
-- 散布列
call set_table_property('user_tags', 'distribution_key', 'user_id');
-- text 类型设置 bitmap 索引
call set_table_property('user_tags', 'bitmap_columns', 'city_id,tag_array');
-- 热点标签,这是字典编码
call set_table_property('user_tags', 'dictionary_encoding_columns',‘city_id:auto’);
commit;
-------------------- 用户行为事实表 ---------------------
begin;
create table shop_behavior
(
user_id text not null,
shop_id text not null,
pv_cnt integer,
trd_amt integer,
ds integer not null
);
call set_table_property('shop_behavior', 'orientation', 'column');
call set_table_property('shop_behavior', 'distribution_key', 'user_id');
--- 聚合键 对 group by 等运算更加敌对
call set_table_property('shop_behavior', 'clustering_key', 'ds,shop_id');
Commit;
窄表模式
将 user_tag 表转为窄表,每一个标签一行记录,标签名为一列,标签值为一列。
数据类型均进化为字符串类型,适宜标签不固定,标签稠密,容许就义局部性能但进步标签定义的灵便度。反对几十到几十万不同标签规模。
•适应场景:维度属性数量高;更新以标签的单位
•劣势:开发简略疾速上线
案例
-------------------- 用户标签维度表 ---------------------
begin;
create table tag2.user_tags
(
userid text not null,
tag_key text,
tag_value text,
ds text
) partition by list(ds);
call set_table_property('tag2.user_tags', 'orientation', 'column’);
-- 散布列
call set_table_property('tag2.user_tags', 'distribution_key', 'user_id');
call set_table_property('tag2.user_tags', 'bitmap_columns', 'tag_key,tag_value');
call set_table_property('tag2.user_tags', 'dictionary_encoding_columns', 'tag_key:auto,tag_value:auto');
commit;
-- 查问例子 --
WITH
f1 AS (
SELECT userid
FROM tag2.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_single'
AND tag_value = 'myname'
),
f2 AS (
SELECT userid
FROM tag2.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_date'
AND tag_value > '20210101'
),
f3 AS (
SELECT userid
FROM tag2.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_numeric'
AND to_number(tag_value, '99G999D9S') > 90
),
f4 AS (
SELECT userid
FROM tag2.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_multi'
AND tag_value IN ('HONOR', 'MI')
)
SELECT COUNT(DISTINCT userid)
FROM ((SELECT userid FROM f1 UNION SELECT userid FROM f2) INTERSECT (SELECT userid FROM f3 EXCEPT SELECT userid FROM f4)) crowd;
预计算模式(宽表、窄表均适宜)
对维度组合的人群固化为更优的数据结构 bitmap
•适应场景:基数高,计算复杂度大,更新频率低场景
•劣势:查问性能高
计划形容:
因为 roaringbitmap 须要整数类型作为 ID 参数,因而减少 usermapping 表做用户逻辑 ID 与底层物理 ID 的映射。
案例
-------------------- 用户标签维度表 ---------------------
BEGIN;
CREATE TABLE tag3.user_tags (
"tag_key" text,
"tag_value" text,
"userlist" roaringbitmap,
ds text
) partition by list(ds);
CALL SET_TABLE_PROPERTY('tag3.user_tags', 'orientation', 'column');
CALL SET_TABLE_PROPERTY('tag3.user_tags', 'bitmap_columns', 'tag_key,tag_value');
CALL SET_TABLE_PROPERTY('tag3.user_tags', 'dictionary_encoding_columns', 'tag_key:auto,tag_value:auto');
COMMIT;
begin;
create table tag3.usermapping
(
userid_int serial,
userid text
);
commit;
-- 构建 RoaringBitmap--
INSERT INTO tag3. user_tags SELECT tag_key ,tag_value ,rb_build(array_agg(user_id::INT)) FROM tag2.user_tags GROUP BY tag_key ,tag_value
-- 查问例子 --
SELECT Rb_cardinality(Rb_and(Rb_or(t1.r, t2.r), Rb_andnot(t3.r, t4.r)))
FROM
(SELECT Rb_and_agg(userlist) AS r
FROM tag3.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_single'
AND tag_value = 'myname' ) AS t1,
(SELECT rb_and_agg(userlist) AS r
FROM tag3.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_date'
AND tag_value > '20210101' ) AS t2,
(SELECT rb_and_agg(userlist) AS r
FROM tag3.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_numeric'
AND to_number(tag_value, '99G999D9S') > 90 ) AS t3,
(SELECT rb_and_agg(userlist) AS r
FROM tag3.user_tags
WHERE ds = '20210101'
AND tag_key = 'tag_multi'
AND tag_value IN ('HONOR',
'MI') ) AS t4
用户画像与圈定的一些教训
•标签分为主画像和扩大画像多张表,辨别高频拜访和低频拜访
•标签分为实时 (Flink) 更新和离线(MaxCompute)更新两局部,两局部共享一张表,缩小运行时 Join,Flink 加工实时局部,MaxCompute 加工离线局部,在 Hologres 中合并
•宽表模式简略,善于定性分析
•窄表模式灵便,计算量大,善于定量分析
•基于 RoaringBitmap 的预计算技术,用户体验最好,开发复杂度较高(比方 bitmap 分桶),SQL 须要定制,适宜 DMP 等有封装能力的平台,善于 UV
原文链接
本文为阿里云原创内容,未经容许不得转载。