逆向思维,去重Cube计算优化新技巧

场景描述

在做数据汇总计算和统计分析时,最头疼的就是去重类指标计算(比如用户数、商家数等),尤其还要带多种维度的下钻分析,由于其不可累加的特性,几乎每换一种统计维度组合,都得重新计算。数据量小时可以暴力的用明细数据直接即时统计,但当数据量大时就不得不考虑提前进行计算了。
典型场景,省、市、区等维度下的支付宝日支付用户数(其中省、市、区为用户下单时所在的地点)。

存在一个情况,某用户早上在杭州市使用支付宝支付了一次,下午跑到绍兴市时又使用支付宝线下支付了一次。那么在统计省+市维度的日支付用户数,需要为杭州市、绍兴市各计1;但在省维度下,需要按用户去重,只能为浙江省计1。针对这种情况,通常就需要以Cube的方式完成数据预计算,同时每个维度组合都需要进行去重操作,因为不可累加。本文将此种场景简称为去重Cube。
在这里插入图片描述

这三种写法其实都类似,重点都在于对数据进行膨胀,再进行去重统计。其执行流程如下图所示,核心思路都是先把数据"膨胀"拆为多行,再按照“普通”的Distinct去重统计,因此性能上本身无太大差异,主要在于代码可维护性上。
在这里插入图片描述

在实际案例中,我们发现,去重Cube的计算过程中,80%+的计算成本消耗在数据膨胀和数据传输上。比如支付宝高管核心指标场景,需要计算各种组合维度下的支付用户数以支持决策分析。实际组合维度达20+,实际执行任务如下图所示,其中R3_2为核心的数据膨胀过程,数据膨胀近10倍,中间结果数据大小由100GB膨胀至1TB、数据量由120亿膨胀至近1300亿,大部分计算资源和计算耗时都花在数据膨胀和传输上了。若实际的组合维度进一步增加的话,数据膨胀大小也将进一步增加。

一种新的思路

首先对问题进行拆解下,去重Cube的计算过程核心分为两个部分,数据膨胀+数据去重。数据膨胀解决的是一行数据同时满足多种维度组合的计算,数据去重则是完成最终的去重统计,核心思路还是在于原始数据去匹配结果数据的需要。其中数据去重本身的计算量就较大,而数据膨胀会导致这一情况加剧,因为计算过程中需要拆解和在shuffle过程中传输大量的数据。数据计算过程中是先膨胀再聚合,加上本身数据内容的中英文字符串内容较大,所以才导致了大量的数据计算和传输成本。
而我们的核心想法是能否避免数据膨胀,同时进一步减少数据传输大小。因此我们联想到,是否可以采用类似于用户打标签的数据打标方案,先进行数据去重生成UID粒度的中间数据,同时让需要的结果维度组合反向附加到UID粒度的数据上,在此过程中并对结果维度进行编号,用更小的数据结构去存储,避免数据计算过程中的大量数据传输。整个数据计算过程中,数据量理论上是逐渐收敛的,不会因为统计维度组合的增加而增加。

核心思路

在这里插入图片描述

核心计算思路如上图,普通的数据膨胀计算cube的方法,中间需要对数据进行膨胀,再聚合,其中结果统计需要的组合维度数就是数据膨胀的倍数,比如上述的“省、省+市”共计两种维度组合,数据预计要膨胀2倍。
而新的数据聚合方法,通过一定的策略方法将维度组合拆解为维度小表并进行编号,然后将原本的订单明细数据聚合至用户粒度的中间过程数据,其中各类组合维度转换为数字标记录至用户维度的数据记录上,整个计算过程数据量是呈收敛聚合的,不会膨胀。

逻辑实现

在这里插入图片描述

明细数据准备:以用户线下支付数据为例,明细记录包含订单编号、用户ID、支付日期、所在省、所在市、支付金额。最终指标统计需求为统计包含省、市组合维度+支付用户数的多维Cube。
订单编号 用户ID 支付日期 所在省 所在市 支付金额
2023111101 U001 2023-11-11 浙江省 杭州市 1.11
2023111102 U001 2023-11-11 浙江省 绍兴市 2.22
2023111103 U002 2023-11-11 浙江省 杭州市 3.33
2023111104 U003 2023-11-11 江苏省 南京市 4.44
2023111105 U003 2023-11-11 浙江省 温州市 5.55
2023111106 U004 2023-11-11 江苏省 南京市 6.66
整体方案流程如下图。

  • STEP1:对明细数据进行所需的维度提取(即Group By对应字段),得到维度集合。
    所在省 所在市
    浙江省 杭州市
    浙江省 绍兴市
    浙江省 温州市
    江苏省 南京市

  • STEP2:对得到的维度集合生成Cube,并对Cube的行进行编码 (假设最终需要所在省、所在省+所在市 2种组合维度),可以用ODPS的Cube功能实现,再根据生成的Cube维度组合进行排序生成唯一编码。
    原始维度:所在省 原始维度:所在省 Cube 维度:所在省 Cube 维度:所在市 Cube行ID(可通过排序生成)
    浙江省 杭州市 浙江省 ALL 1
    浙江省 杭州市 浙江省 杭州市 2
    浙江省 绍兴市 浙江省 ALL 1
    浙江省 绍兴市 浙江省 绍兴市 3
    浙江省 温州市 浙江省 ALL 1
    浙江省 温州市 浙江省 温州市 4
    江苏省 南京市 江苏省 ALL 5
    江苏省 南京市 江苏省 南京市 6

  • STEP3:将Cube的行编码,根据映射关系回写到用户明细上,可用Mapjoin的方式实现。
    订单编号 用户ID 支付日期 所在省 所在市 汇总Cube ID
    2023111101 U001 2023-11-11 浙江省 杭州市 [1,2]
    2023111102 U001 2023-11-11 浙江省 绍兴市 [1,3]
    2023111103 U002 2023-11-11 浙江省 杭州市 [1,2]
    2023111104 U003 2023-11-11 江苏省 南京市 [5,6]
    2023111105 U003 2023-11-11 浙江省 温州市 [1,4]
    2023111106 U004 2023-11-11 江苏省 南京市 [5,6]

  • STEP4:汇总到用户维度,并对 Cube ID集合字段进行去重 (可以用ARRAY 的DISTINCT)
    用户ID 汇总Cube ID
    U001 [1,2,3]
    U002 [1,2]
    U003 [1,4,5,6]
    U004 [5,6]

  • STEP5:按照Cube ID进行计数计算(由于STEP4已经去重啦,因此这里不需要再进行去重);然后按照映射关系进行维度还原。
    Cube ID 下单用户数指标 Cube 维度还原:所在省 Cube 维度还原:所在市
    1 3 浙江省 ALL
    2 2 浙江省 杭州市
    3 1 浙江省 绍兴市
    4 1 浙江省 温州市
    5 2 江苏省 ALL
    6 2 江苏省 江苏省

代码实现


-- CREATE TABLE IF NOT EXISTS tmp_user_pay_order_detail
-- LIFECYCLE 30 AS 
-- SELECT     '2023111101' AS trade_no,'U001' AS payer_user_id,'20231111' AS gmt_pay,1.11 AS trade_pay_amt,'浙江省' AS province_name,
--     1 AS province_code,'杭州市' AS city_name, 11 AS city_code
-- UNION ALL SELECT '2023111102','U001','20231111',2.22,'浙江省',1,'绍兴市',12
-- UNION ALL SELECT '2023111103','U002','20231111',3.33,'浙江省',1,'杭州市',11
-- UNION ALL SELECT '2023111104','U003','20231111',4.44,'江苏省',2,'南京市',22
-- UNION ALL SELECT '2023111105','U003','20231111',5.55,'浙江省',1,'温州市',13
-- UNION ALL SELECT '2023111106','U004','20231111',6.66,'江苏省',2,'南京市',22WITH dim_cube AS 
(SELECT*,DENSE_RANK() OVER(PARTITION BY 1 ORDER BY cube_province_name,cube_city_name) AS cube_id FROM (SELECT dim_key,COALESCE(IF(GROUPING(province_name) = 0,province_name,'ALL'),'na') AS cube_province_name             ,COALESCE(IF(GROUPING(city_name    ) = 0,city_name    ,'ALL'),'na') AS cube_city_name FROM (SELECT  CONCAT('',COALESCE(province_name ,''),'#' ,COALESCE(city_name,''),'#' ) AS dim_key,province_name,city_nameFROM tmp_user_pay_order_detailGROUP BY province_name,city_name)t GROUP BY dim_key,province_name,city_nameGROUPING SETS((dim_key,province_name),(dim_key,province_name,city_name)))
),detail_ext AS 
(SELECT payer_user_id,ARRAY_DISTINCT(SPLIT(WM_CONCAT(';',cube_ids),';')) as cube_id_arryFROM (SELECT  /*+ MAPJOIN(dim_cube) */ payer_user_id,cube_idsFROM (SELECT   payer_user_id,CONCAT('',COALESCE(province_name ,''),'#' ,COALESCE(city_name     ,''),'#' ) AS dim_keyFROM  tmp_user_pay_order_detail) dwd_detailJOIN (SELECT dim_key,WM_CONCAT(';',cube_id) AS cube_idsFROM dim_cube GROUP BY dim_key) dim_cubeON dwd_detail.dim_key = dim_cube.dim_key)GROUP BY payer_user_id
)
SELECT cube_id,MAX(province_name) AS province_name,MAX(city_name    ) AS city_name,MAX(uid_cnt      ) AS user_cnt
FROM 
(SELECT   cube_id                 AS cube_id,COUNT(1)                AS uid_cnt,CAST(NULL    AS STRING) AS province_name,CAST(NULL    AS STRING) AS city_nameFROM detail_extLATERAL VIEW EXPLODE(cube_id_arry) arr AS cube_idGROUP BY cube_idUNION ALL SELECT   CAST(cube_id AS STRING) AS cube_id,CAST(NULL    AS BIGINT) AS uid_cnt,cube_province_name AS province_name,cube_city_name     AS city_name    FROM dim_cube
) base 
GROUP BY cube_id```

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/415398.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HJ-FR3协作机器人为什么备受高校和科研机构青睐?

一、协作机器人研究现状 协作机器人是能够在共享空间中与人类交互并开展安全工作的新型机器人,由于轻量、安全的特点,在柔性制造、社会服务、医疗健康等领域展现出了良好的应用前景,也成为了当前学术界机器人领域的研究热点,相关研…

安卓开发-day

一、安卓项目结构 1、manifests文件夹 Android系统配置文件夹,包含一个AndroidManifest.xml文件; AndroidMainifest.xml文件是每个android项目必须要包含的文件(项目唯一),创建项目时默认就会生成这个文件&#xff0…

Rust-泄漏

在C中,如果引用计数智能指针出现了循环引用,就会导致内存泄漏。而Rust中也一样存在引用计数智能指针Rc,那么Rust中是否可能制造出内存泄漏呢? 内存泄漏 首先,我们设计一个Node类型,它里面包含一个指针,可以指向其他…

如何用ArcGIS制作城市用地适应性评价

01概述 “城市用地适宜性评价是城市总体规划的一项重要前期工作,它首先对工程地质、社会经济和生态环境等要素进行单项用地适宜性评价,然后用地图叠加技术根据每个因子所占权重生成综合的用地适宜性评价结果,俗称“千层饼模式”。 做用地适…

Qt简单使用与初识

🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风…

Ant Design Vue上传多个图片

模板代码&#xff1a; 定义变量&#xff1a; 文件限制的函数&#xff1a; 上传的函数&#xff1a; 样式函数&#xff1a; 完整代码&#xff1a; <template><div class"dialog-upload" v-if"showUploadDialog"><div class"dialog-uplo…

npm pnpm yarn 报错或常见问题处理集锦

各种卡死&#xff0c;报错问题处理汇总 1. npm 安装 卡死了怎么办&#xff0c;npm # 切换源 npm config set registry https://registry.npmmirror.com # 查看源 npm config get registry2. pnpm安装 卡死了怎么办 方法1&#xff1a;切换源 npx pnpm config set registry h…

京东ES支持ZSTD压缩算法上线了:高性能,低成本 | 京东云技术团队

1 前言 在《ElasticSearch降本增效常见的方法》一文中曾提到过zstd压缩算法[1]&#xff0c;一步一个脚印我们终于在京东ES上线支持了zstd&#xff1b;我觉得促使目标完成主要以下几点原因&#xff1a; Elastic官方原因&#xff1a;zstd压缩算法没有在Elastic官方的开发计划中&…

vue项目执行依赖安装(npm i或npm install )报ls-remote -h -t异常

从git拉取的vue项目执行依赖安装时一直报错&#xff0c; 报错如下图&#xff1a;首先&#xff0c;查看了node版本、npm配置的镜像地址均没找到解决办法。 在命令行中直接输入git发现提示于是从网上搜到了一个博文https://blog.csdn.net/weixin_49159364/article/details/118198…

【数据恢复篇】WinHex数据擦除功能

【数据恢复篇】WinHex数据擦除功能 简单写下WinHex数据擦除功能—【蘇小沐】 目录 1、实验环境 &#xff08;一&#xff09;WinHex文件"安全擦除"功能 1、安全擦除路径 2、数值填充 3、随机字符 4、模拟加密数据 &#xff08;二&#xff09;数据删除标准 1、DoD 5…

class_10:this关键字

this关键字是指向调用对象的指针 #include <iostream> #include <iostream> using namespace std;class Car{ public://成员数据string brand; //品牌int year; //年限//构造函数名与类名相同Car(string brand,int year){cout<<"构造函数中&#…

离散数学学习要点——命题逻辑

文章目录 数理逻辑命题逻辑命题命题的种类命题的表示 逻辑连接词否定联结词合取联结词∧析取联结词∨或异或 条件➡等价&#xff08;双条件&#xff09;联结词↔联结词真值表 命题逻辑中的命题的符号化命题公式及其真值表命题公式真值表 命题公式的等价重言式与重言蕴含式重言式…