乐趣区

关于sql:Hologres如何基于roaringbitmap实现超高基数UV计算

简介: 本文将会介绍 Hologres 基于 roaringbitmap 实现超高基数的 UV 计算

RoaringBitmap 是一种压缩位图索引,RoaringBitmap 本身的数据压缩和去重个性非常适宜对于大数据下 uv 计算。其次要原理如下:

  • 对于 32bit 数, RoaringBitmap 会结构 2^16 个桶,对应 32 位数的高 16 位;32 位数的低 16 位则映射到对应桶的一个 bit 上。单个桶的容量由桶中的已有的最大数值决定
  • bitmap 把 32 位数用 1 位示意,能够大大地压缩数据大小。
  • bitmap 位运算为去重提供了伎俩。

主体思维(T+1):把上一天的所有数据依据最大的查问维度聚合出的 uid 后果放入 RoaringBitmap 中,把 RoaringBitmap 和查问维度寄存在聚合后果表(每天百万条)。之后查问时,利用 Hologres 弱小的列存计算间接依照查问维度去查问聚合后果表,对其中要害的 RoaringBitmap 字段做 or 运算进行去重后并统计基数,即可得出对应用户数 UV,count 条数即可计算得出 PV,达到亚秒级查问。

只需进行一次最细粒度的预聚合计算,也只生成一份最细粒度的预聚合后果表。得益于 Hologres 的实时计算能力,该计划下预计算所需的次数和空间都达到较低的开销。

Hologres 计算 UV、PV 计划详情

图 1 Hologres 基于 RoaringBitmap 计算 pv uv 流程

1. 创立相干根底表

1)应用 RoaringBitmap 前须要创立 RoaringBitmap extention,语法如下,同时该性能须要 Hologres  0.10 版本。

CREATE EXTENSION IF NOT EXISTS roaringbitmap;

2)创立表 ods\_app 为明细源表,寄存用户每天大量的明细数据(按天分区),其 DDL 如下:

BEGIN;
CREATE TABLE IF NOT EXISTS public.ods_app (
     uid text,
     country text,
     prov text,
     city text,
     channel text,
     operator text,
     brand text,
     ip text,
     click_time text,
     year text,
     month text,
     day text,
     ymd text NOT NULL
);
CALL set_table_property('public.ods_app', 'bitmap_columns', 'country,prov,city,channel,operator,brand,ip,click_time, year, month, day, ymd');
--distribution_key 依据需要设置,依据该表的实时查问需要,从什么维度做分片可能获得较好成果即可
CALL set_table_property('public.ods_app', 'distribution_key', 'uid');
-- 用于做 where 过滤条件,蕴含残缺年月日工夫字段举荐设为 clustering_key 和 event_time_column
CALL set_table_property('public.ods_app', 'clustering_key', 'ymd');
CALL set_table_property('public.ods_app', 'event_time_column', 'ymd');
CALL set_table_property('public.ods_app', 'orientation', 'column');
COMMIT;

3)创立表 uid\_mapping 为 uid 映射表,uid 映射表用于映射 uid 到 32 位 int 类型。

RoaringBitmap 类型要求用户 ID 必须是 32 位 int 类型且越浓密越好(用户 ID 最好间断),而常见的业务零碎或者埋点中的用户 ID 很多是字符串类型,因而应用 uid\_mapping 类型构建一张映射表。映射表利用 Hologres 的 SERIAL 类型(自增的 32 位 int)来实现用户映射的主动治理和稳固映射。

注: 该表在本例每天批量写入场景,可为行存表也可为列存表,没有太大区别。如须要做实时数据(例如和 Flink 联用),须要是行存表,以进步 Flink 维表实时 JOIN 的 QPS。

BEGIN;
 CREATE TABLE public.uid_mapping (
     uid text NOT NULL,
     uid_int32 serial,
            PRIMARY KEY (uid) 
 );
 -- 将 uid 设为 clustering_key 和 distribution_key 便于疾速查找其对应的 int32 值
CALL set_table_property('public.uid_mapping', 'clustering_key', 'uid');
CALL set_table_property('public.uid_mapping', 'distribution_key', 'uid');
CALL set_table_property('public.uid_mapping', 'orientation', 'row');
COMMIT;

3)创立表 dws\_app 根底聚合表,用于寄存在根底维度上聚合后的后果

根底维度为之后进行查问计算 pv 和 uv 的最细维度,这里以 country,prov,city 为例构建聚合表

begin;
create table dws_app(
    country text,
  prov text,
  city text, 
  ymd text NOT NULL,  -- 日期字段
  uid32_bitmap roaringbitmap, -- UV 计算
  pv integer, -- PV 计算
  primary key(country, prov, city, ymd)-- 查问维度和工夫作为主键,避免反复插入数据
);
CALL set_table_property('public.dws_app', 'orientation', 'column');
--clustering_key 和 event_time_column 设为日期字段,便于过滤
CALL set_table_property('public.dws_app', 'clustering_key', 'ymd');
CALL set_table_property('public.dws_app', 'event_time_column', 'ymd');
--distribution_key 设为 group by 字段
CALL set_table_property('public.dws_app', 'distribution_key', 'country,prov,city');
end;

2. 更新 dws 表及 id\_mapping 表

每天从上一天的 uid 中找出新客户(uid 映射表 uid\_mapping 中没有的 uid)插入到 uid 映射表中

WITH
-- 其中 ymd = '20210329' 示意上一天的数据
    user_ids AS (SELECT uid FROM ods_app WHERE ymd = '20210329' GROUP BY uid)
    ,new_ids AS (SELECT user_ids.uid FROM user_ids LEFT JOIN uid_mapping ON (user_ids.uid = uid_mapping.uid) WHERE uid_mapping.uid IS NULL )
INSERT INTO uid_mapping SELECT  new_ids.uid
FROM    new_ids
;

更新完 uid 映射表后,将数据做聚合运算后插入聚合后果表,次要步骤如下:

  • 首先通过源表 inner join uid 映射表,失去上一天的聚合条件和对应的 uid\_int32;
  • 而后依照聚合条件做聚合运算后插入 RoaringBitmap 聚合后果表,作为上一天的聚合后果;
  • 每天只需进行一次聚合,寄存一份数据,数据条数最坏等于 UV 的量。以案例阐明,明细表每天几亿的增量,在聚合后果表每天只需寄存百万级数据。
WITH
    aggregation_src AS(SELECT country, prov, city, uid_int32 FROM ods_app INNER JOIN uid_mapping ON ods_app.uid = uid_mapping.uid WHERE ods_app.ymd = '20210329')
INSERT INTO dws_app SELECT  country
        ,prov
        ,city
        ,'20210329'
        ,RB_BUILD_AGG(uid_int32)
        ,COUNT(1)
FROM    aggregation_src
GROUP BY country
         ,prov
         ,city
;

3.UV、PV 查问

查问时,从汇总表 dws\_app 中依照查问维度做聚合计算,查问 bitmap 基数,得出 Group by 条件下的用户数

-- 运行上面 RB_AGG 运算查问,可先敞开三阶段聚合开关性能更佳(默认敞开)set hg_experimental_enable_force_three_stage_agg=off    
-- 能够查问根底维度任意组合,任意时间段的 uv pv
SELECT  country
        ,prov
        ,city
        ,RB_CARDINALITY(RB_OR_AGG(uid32_bitmap)) AS uv
        ,sum(1) AS pv
FROM    dws_app
WHERE   ymd = '20210329'
GROUP BY country
         ,prov
         ,city;
-- 查一个月         
SELECT  country
        ,prov
        ,RB_CARDINALITY(RB_OR_AGG(uid32_bitmap)) AS uv
        ,sum(1) AS pv
FROM    dws_app
WHERE   ymd >= '20210301' and ymd <= '20210331'
GROUP BY country
         ,prov;
该查问等价于
SELECT  country
        ,prov
        ,city
        ,COUNT(DISTINCT uid) AS uv
        ,COUNT(1) AS pv
FROM    ods_app
WHERE   ymd = '20210329'
GROUP BY country
         ,prov
         ,city;
SELECT  country
        ,prov
        ,COUNT(DISTINCT uid) AS uv
        ,COUNT(1) AS pv
FROM    ods_app
WHERE   ymd >= '20210301' and ymd <= '20210331'
GROUP BY country
         ,prov;         

4. 可视化展现

计算出 UV、PV 和,大多数状况须要用 BI 工具以更直观的形式可视化展现,因为须要应用 RB\_CARDINALITY 和 RB\_OR\_AGG 进行聚合计算,须要应用 BI 的自定义聚合函数的能力,常见的具备该能力的 BI 包含 Apache Superset 和 Tableau,上面将会讲述这两个 BI 工具的最佳实际。

4.1 应用 Apache Superset

Apache Superset 对接 Hologres 的形式,请参考产品手册。在 Superset 中能够间接应用 dws\_app 表作为 Dataset 应用

并且在数据集中,创立一个独自 Metrics,名为 UV,表达式如下:

RB_CARDINALITY(RB_OR_AGG(uid32_bitmap))

而后您就能够开始摸索数据了

当然也能够创立 Dashborad:

4.2 应用 Tableau

Tableau 对接 Hologres 的形式,请参考产品手册。能够应用 Tableau 的直通函数间接实现自定义函数的能力,具体介绍请参照 Tableau 的手册。在 Tableau 对接 Hologres 后,能够创立一个计算字段,表达式如下

RAWSQLAGG_INT("RB_CARDINALITY(RB_OR_AGG(%1))", [Uid32 Bitmap])
 而后您就能够开始摸索数据了 

当然也能够创立 Dashborad

版权申明: 本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

退出移动版