比 SQL 快出数量级的大数据计算技术

本文涉及的产品
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
简介: SQL 是大数据计算中最常用的工具,但在实际应用中,SQL 经常跑得很慢,浪费大量硬件资源。例如,某银行的反洗钱计算在 11 节点的 Vertica 集群上跑了 1.5 小时,而用 SPL 重写后,单机只需 26 秒。类似地,电商漏斗运算和时空碰撞任务在使用 SPL 后,性能也大幅提升。这是因为 SQL 无法写出低复杂度的算法,而 SPL 提供了更强大的数据类型和基础运算,能够实现高效计算。

SQL 经常跑得很慢
SQL 是最常用的大数据计算语言,但是,SQL 经常跑得很慢,严重浪费硬件资源。

某银行的反洗钱准备计算,36 亿行,11 节点的 Vertica 集群跑了 1.5 小时。

某电商漏斗运算,3 亿行,Snow Flake 的 Medium 型 4 节点集群超过 3 分钟没算出来。

某时空碰撞任务,250 亿行,Click House 5 节点集群跑了 1800 秒。

这些案例的数据量不算很大,小的只有几 GB,大的也就几百 GB,表现出来的性能却不如人意。

是不是这些任务过于复杂,就只能这么慢呢?并不是,这些任务用 SPL 改写后,性能都有大幅改观。

反洗钱准备计算,SPL 单机只要 26 秒,快了 208 倍,竟然把跑批任务跑成了实时查询!

漏斗运算,SPL 单机 10 秒完成。

时空碰撞任务,SPL 单机只要 350 秒,快了 5 倍。

换用 SPL 后,不仅计算性能快出了数量级,而且硬件还更少了,集群变单机。事实上,连代码也更短了。

为什么 SQL 就跑不出这个性能呢?

由 TOP N 问题探究 SQL 慢的原因
SQL 会跑这么慢,是因为写不出低复杂度的算法。

举个例子,从一亿条数据中取前十名。SQL 写出来有个 order by 字样:

SELECT TOP 10 * FROM Orders ORDER BY Amount DESC
这意味着要排序。如果真做一亿条记录的大排序,那计算量就大了去了。

其实,只要保持一个十条记录的小集合,遍历一亿条数据的时候不断比较、替换小集合中的记录,就可以得到前十名了,计算量比大排序小了 8 倍!遗憾的是,SQL 没有显式的集合数据类型,无法描述这样的计算过程,只能写成 order by,然后指望数据库去优化。还好,几乎所有数据库都会优化,采用更高效的算法,不会傻到真去做大排序。

但是,情况稍复杂些呢?比如我们把需求改成计算分组内的前 10 名,SQL 写出来是这样的。

SELECT * FROM (
    SELECT *, 
       ROW_NUMBER() OVER (PARTITION BY Area ORDER BY Amount DESC) rn  
    FROM Orders )
 WHERE rn<=10

这和前面全量数据取前 10 名的写法相差很大,要借助窗口函数造个组内序号,再用子查询过滤出符合条件的记录,有点“绕”了。

无论如何,SQL 中还是有 ORDER BY,运算逻辑还是排序。实测发现,同样的数据量,Oracle 计算组内前 10 名比全集前 10 名慢出几十倍,按说多个分组应该只慢一点点才对。Oracle 很可能做了排序,甚至是外存排序。

数据库优化引擎面对稍复杂的情况就晕了,只能老老实实按 SQL 的逻辑去执行,常常是个高复杂度的运算逻辑,所以性能就会非常差。

当然,也许哪天数据库又进化了,碰到这种情况也会优化了。但总有更复杂的情况,而数据库优化引擎的进化速度是很慢的。

更复杂的对象事件问题
用 TOP N 来举例,是因为它比较简单易于理解,事实上在现实中并不经常要做这种运算。而且,对于 TOP N 这样的简单问题,即使复杂化到分组 TOP N 的情况,SQL 优化器经过一些改良,也还是能对付的。

但实际情况中的计算常常要远比 TOP N 复杂,前面提到的案例,业务需求都有一定的复杂性,数据量不算大但计算量大。SQL 写不出好算法,只能依靠数据库的优化引擎,而优化引擎却常常靠不住。

比如典型的对象事件问题。这里的对象可以是电商系统用户、银行账号、游戏玩家、手机、车辆等等,通常会有个唯一的 ID,对象涉及的事件都记录在这个 ID 下,例如电商系统操作事件、银行账号的交易记录、手机的通话记录、用户的操作日志等。

对象事件问题的实际计算任务也很多,比如电商系统指定操作步骤的用户流失率,银行账号的交易额超过 1 万元的交易次数、节假日发生的交易额,计算每张信用卡是否连续三天都有超过 1000 元的交易,游戏用户购买某类装备的金额,新注册的游戏用户下次登录时刻的间隔天数,手机通话时间不超过 3 秒的次数,…。

这类问题非常普遍,可以说占了数据分析业务中一半以上的场景。这都是有业务意义的数据分析了,其中有相当一部分会涉及有序运算,用 SQL 都会非常难写,不仅代码繁琐,而且运算效率低下。

下面通过电商漏斗分析这个典型的事件对象问题,继续探究 SQL 慢的原因。

对象事件问题的任务
对象事件问题的任务是针对每个 ID 中符合一定条件的事件,计算出某种聚合值,然后再做 ID 的整体统计。

具体到漏斗分析,就是找出每个用户在一个较短的时间范围,按照次序完成一个给定步骤序列的最多步数。

比如给定三个步骤:浏览商品、查看详情、确认下单,要计算的聚合值是:每个用户在 3 天内按次序最多完成这三个步骤中的几步。有了每一个用户的最多步数,只要简单计数,就可以整体统计出,走到每个步骤的用户数量,进而分析哪一步的用户流失率最严重了。

对象事件计算的特征
要弄清楚 SQL 解决这个问题的难点,先看对象事件计算的几个特征:

一,ID 数量非常多,几千万到几十亿;

二,同一 ID 的事件数并不多,几条到几千条;

三,针对事件的计算复杂,步骤多;

四,计算时 ID 之间无关。

SQL 实现对象事件问题导致大表 JOIN
对象事件之间的复杂关联计算会涉及多条互相依赖的事件记录。

SQL 对跨行运算的支持很弱,要用 JOIN 把多行记录拼到一行中才能进一步运算。而且,参与运算的事件都有条件,一般要先用子查询选出来再 JOIN。计算中涉及的事件越多,参与 JOIN 的子查询越多。

比如下图中的三步漏斗,就涉及 ID1 的多条记录,先用多个子查询筛选合适的事件记录,把多行记录 JOIN 到一行中,才能做进一步运算。

这些子查询还会有依赖性,比如第 2 步事件要在第一步事件的基础上寻找,子查询本身也要用 JOIN 计算。

这些子查询都会基于巨大的事件表,大表 JOIN 本来就很难做,这里还要反复多次 JOIN,跑得慢就不奇怪了,跑崩也很正常。

SQL 实现对象事件问题涉及大结果集 GROUP BY
用 SQL 计算最后聚合值时,又会涉及大结果集的 GROUP BY。

因为期望的结果中,每个 ID 对应一套聚合值,JOIN 的结果并没有这个特征,同一个 ID 可能会 JOIN 出很多条记录。这时候,还要针对 ID 再做一次 GROUP BY。ID 数量非常多,这种大结果集的分组性能非常差,并行计算效果也不好。

有时,计算目标是对 ID 计数,GROUP BY 会退化成 COUNT DISTINCT,但复杂度数量级并没有变。

SQL 实现对象事件问题的脚本
这是一个 SQL 写的三步漏斗分析,其中有多个和 ID 相关的大表 JOIN、GROUP BY 以及 COUNT DISTINCT。

WITH e1 AS (
    SELECT userid, visittime AS step1_time, MIN(sessionid) AS sessionid, 1 AS step1
    FROM events e1 JOIN eventgroup ON eventgroup.id = e1.eventgroup
    WHERE visittime >= DATE_ADD(arg_date,INTERVAL -14 day) AND visittime < arg_date AND eventgroup.name = 'SiteVisit'
    GROUP BY userid,visittime
), e2 AS (
    SELECT e2.userid, MIN(e2.sessionid) AS sessionid, 1 AS step2, MIN(visittime) AS step2_time, MIN(e1.step1_time) AS step1_time
    FROM events e2 JOIN e1 ON e1.sessionid = e2.sessionid AND visittime > step1_time JOIN eventgroup ON eventgroup.id = e2.eventgroup
    WHERE visittime < DATE_ADD(step1_time ,INTERVAL +1 day) AND eventgroup.name = 'ProductDetailPage'
    GROUP BY e2.userid
), e3 AS (
    SELECT e3.userid, MIN(e3.sessionid) AS sessionid, 1 AS step3, MIN(visittime) AS step3_time, MIN(e2.step1_time) AS step1_time
    FROM events e3 JOIN e2 ON e2.sessionid = e3.sessionid AND visittime > step2_time JOIN eventgroup ON eventgroup.id = e3.eventgroup
    WHERE visittime < DATE_ADD(step1_time ,INTERVAL +1 day) AND (eventgroup.name = 'OrderConfirmationType1')
    GROUP BY e3.userid
)
SELECT s.devicetype AS devicetype,
    COUNT(DISTINCT CASE WHEN fc.step1 IS NOT NULL THEN fc.step1_userid  ELSE NULL END) AS step1_count,
    COUNT(DISTINCT CASE WHEN fc.step2 IS NOT NULL THEN fc.step2_userid  ELSE NULL END) AS step2_count,
    COUNT(DISTINCT CASE WHEN fc.step3 IS NOT NULL THEN fc.step3_userid  ELSE NULL END) AS step3_count,
FROM (
    SELECT e1.step1_time AS step1_time, e1.userid AS userid, e1.userid AS step1_userid, e2.userid AS step2_userid,e3.userid AS step3_userid,
           e1.sessionid AS step1_sessionid, step1, step2, step3
    FROM e1 LEFT JOIN e2 ON e1.userid=e2.userid LEFT JOIN e3 ON e2.userid=e3.userid ) fc
LEFT JOIN sessions s ON fc.step1_sessionid = s.id 
GROUP BY s.devicetype

解决对象事件问题的正确思路
其实,只要利用前面说的特征,解决对象事件问题并不难。

我们可以把事件按 ID 排序,每个 ID 的事件不多,每次把一个 ID 的所有事件读入内存。在内存中,使用过程化的语言实现这种复杂计算就很容易了。

因为 ID 之间无关,所以内存关联运算仅在一个 ID 的事件范围内进行,不会有大表 JOIN。每次都会完成一个 ID 聚合值的计算,后续不需要 GROUP BY ID,COUNT DISTINCT 也会变成简单的 COUNT。这个算法不仅计算量少,而且占用内存小,很容易并行。

可惜,用 SQL 无法实现这样的高效算法。这主要是因为 SQL 缺乏离散性,无法实现过程化的复杂跨行运算,只能借助 JOIN。而且,SQL 的集合无序,即使刻意有序存储也无法利用。

SPL 如何计算 Top N
SQL 没有显式的集合数据类型,无法描述前面说的那种低复杂度的算法,只能指望数据库去优化。而 SPL 则可以从算法层面解决这些问题。

SPL 把 Top N 理解为,与 SUM、MAX 一样的聚合运算,只不过返回值是一个集合而已。这样就相当于实现了前面提到的高效算法,代码写出来是这样的:

Orders.groups(;top(10;-Amount))

Orders.groups(Area;top(10;-Amount))

这个语句中没有 order by 字样,也就不会做大排序,而采用小集合的算法了。而且,这里分组前 10 名和全集前 10 名的写法基本一样,只是多了分组键。

SPL 解决对象事件问题
再看对象事件问题,用 SPL 就很容易实现前面说的高效算法。

SPL 的集合有序,能够刻意保证存储的次序,且提供有序游标,可以一次读入一个 ID 的事件数据。SPL 语法中强化了离散性,可以很方便按自然思维写出多步骤跨行运算的代码。

这是 SPL 实现同样漏斗分析的代码:
QQ_1731554732303.png

A8 每次读入一个 ID 的所有事件,然后用 A9 到 A12 四句代码实现复杂判断逻辑,最后 A12 分组统计时也只要简单计数就可以了,不用再考虑去重了。

SPL 解决老大难问题 JOIN
SPL 还能解决 SQL 的老大难问题 JOIN。

SQL 对于 JOIN 的定义并不涉及主键 ,但几乎所有有业务意义的 JOIN 都和主键相关 SQL 无法利用这个特征。采用双边 HASH JOIN 算法不仅计算量大,在外存和集群时的性能还不可控。

SPL 重新定义 JOIN,把 JOIN 分类,可以充分利用主键特征来降低计算量,不仅计算快,也没有不可控的问题。

结果,SPL 的关联表运算性能常常超过 SQL 的宽表。

进一步讨论
SQL 为什么写不出我们期望的高性能算法呢,表面上看是因为对某些运算的支持不足,比如有序计算,分组子集等。更深层原因在于其理论基础 - 关系代数过于简单,缺少必要的数据类型和相应的基础运算。五十年前的理论体系不能表达现代应用的计算需求,也是很正常的。

那么工程上的手段是否可以解决 SQL 慢的问题呢?

比如分布式并行计算。这种手段有时候可以提高性能,但成本太高。

SQL 调优能起到的作用也有限,对于复杂情况不管用。调优确实能通过改变执行路径来采用某些高效算法,但相当多高性能算法在 SQL 中根本没有。

用 UDF 编写高性能代码,原则上可行,但成本太高,可操作性差。集群环境中还会带来复杂的调度和运维问题。而且,如果不能改变数据库存储方案,仅用 UDF 也很难提速。

总之,工程上的优化方法成本高、效果差,没有什么有效手段。SQL 慢是理论层面的问题,工程优化只能有限改善,不能根除。

SPL 则没有继续采用关系代数,而是发明了离散数据集理论,这就会有更丰富的数据类型和基础运算:

图中带星号的都是 SPL 独创的算法。

有了这些基础,就容易写出低复杂度的代码,充分利用硬件资源,实现比 SQL 快出数量级的高性能计算。

SPL 其实提供了集群计算能力,不过在 SPL 的性能优化案例中,大部分场景单机已经足够,集群功能很少有机会得到历练,以至于 SPL 几乎不宣传分布式能力。SPL更多内容欢迎前往乾学院交流沟通!

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
1月前
|
存储 机器学习/深度学习 分布式计算
大数据技术——解锁数据的力量,引领未来趋势
【10月更文挑战第5天】大数据技术——解锁数据的力量,引领未来趋势
|
5天前
|
存储 机器学习/深度学习 SQL
大数据处理与分析技术
大数据处理与分析技术
23 2
|
24天前
|
存储 分布式计算 数据可视化
大数据常用技术与工具
【10月更文挑战第16天】
82 4
|
7天前
|
存储 分布式计算 NoSQL
【赵渝强老师】大数据技术的理论基础
本文介绍了大数据平台的核心思想,包括Google的三篇重要论文:Google文件系统(GFS)、MapReduce分布式计算模型和BigTable大表。这些论文奠定了大数据生态圈的技术基础,进而发展出了Hadoop、Spark和Flink等生态系统。文章详细解释了GFS的架构、MapReduce的计算过程以及BigTable的思想和HBase的实现。
|
1月前
|
存储 数据采集 监控
大数据技术:开启智能决策与创新服务的新纪元
【10月更文挑战第5天】大数据技术:开启智能决策与创新服务的新纪元
|
10天前
|
存储 大数据 定位技术
大数据 数据索引技术
【10月更文挑战第26天】
24 3
|
10天前
|
存储 大数据 OLAP
大数据数据分区技术
【10月更文挑战第26天】
38 2
|
13天前
|
消息中间件 分布式计算 大数据
数据为王:大数据处理与分析技术在企业决策中的力量
【10月更文挑战第29天】在信息爆炸的时代,大数据处理与分析技术为企业提供了前所未有的洞察力和决策支持。本文探讨了大数据技术在企业决策中的重要性和实际应用,包括数据的力量、实时分析、数据驱动的决策以及数据安全与隐私保护。通过这些技术,企业能够从海量数据中提取有价值的信息,预测市场趋势,优化业务流程,从而在竞争中占据优势。
45 2
|
15天前
|
存储 分布式计算 Hadoop
数据湖技术:Hadoop与Spark在大数据处理中的协同作用
【10月更文挑战第27天】在大数据时代,数据湖技术凭借其灵活性和成本效益成为企业存储和分析大规模异构数据的首选。Hadoop和Spark作为数据湖技术的核心组件,通过HDFS存储数据和Spark进行高效计算,实现了数据处理的优化。本文探讨了Hadoop与Spark的最佳实践,包括数据存储、处理、安全和可视化等方面,展示了它们在实际应用中的协同效应。
58 2
|
16天前
|
存储 分布式计算 Hadoop
数据湖技术:Hadoop与Spark在大数据处理中的协同作用
【10月更文挑战第26天】本文详细探讨了Hadoop与Spark在大数据处理中的协同作用,通过具体案例展示了两者的最佳实践。Hadoop的HDFS和MapReduce负责数据存储和预处理,确保高可靠性和容错性;Spark则凭借其高性能和丰富的API,进行深度分析和机器学习,实现高效的批处理和实时处理。
56 1