0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SQL为什么不可能跑得快

jf_ro2CN3Fa 来源:github.com 2024-01-24 09:41 次阅读

我们讨论过代码编写的难和繁的原理问题,现在关注性能问题,运行速度当然是非常重要的事情。

我们知道,软件不能改变硬件的性能,CPU 和硬盘该多快就多快。不过,我们可以设计出低复杂度的算法,也就是计算量更小的算法,计算机执行的动作变少,自然也就会快了。本来要做 1 亿次运算,如果有个好算法能把计算量降低到 100 万次,那快出 100 倍就不奇怪了。但是,光想出算法还不够,还要把这个算法实实在在地用某种程序语言写出来,否则计算机不会执行。

然而,如果采用的程序语言不给力,就有可能真地写不出来,这时候就干瞪眼忍受低速度。

SQL 就会经常发生这种事。我们用这个简单例子来说明:从 1 亿条数据中取出前 10 名,用 SQL 写出来并不复杂:

SELECT TOP 10 * FROM Orders ORDER BY Amount DESC

这个语句里有个 ORDER BY 的字样,对应的执行逻辑就是要对 1 亿条数据做大排序。我们知道,排序是个非常慢的动作,要把数据遍历多次(具体复杂度是 N*logN 次 ),如果数据量大到内存装不下,那还需要外存做缓存,性能还会急剧下降。如果严格按这句 SQL 描述的逻辑去执行,这个运算无论如何是跑不快的。

其实,我们知道,只是取前 10 名并不需要将所有数据做大排序,只要在遍历时始终保持一个前 10 名的小集合,遍历过程中不断地修正这个小集合,一次遍历可以了,复杂度是 N*log10,和 log1亿相比差了 8 倍左右,也就是计算量能小 8 倍,而且这种算法完全不需要外存做缓存。

遗憾的是,用 SQL 无法描述这样的计算过程,只能写成上面那个样子,然后指望数据库去优化。所幸,几乎所有数据库都会优化这个句子,没有傻到去做大排序了,所以也能跑得比较快。

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

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

这和刚才针对全集取前 10 名的写法相差就很大了,这是我们之前说的缺失离散性导致的。要先借助窗口函数造一个组内序号出来,再用子查询过滤出符合条件的记录,也就是有点“绕”了。

无论如何,这里还是有 ORDER BY 字样,其中的运算逻辑还是要排序。我们实际测试发现,在 Oracle 中,同样的数据量,计算这种分组前 10 名要比上面那个全集前 10 名慢出几十倍,按说多个分组应该只慢一点点才对。Oracle 有很大可能性真地去做了排序甚至是外存排序(当然我们没读过 Oracle 的源代码并不能确定),数据库优化引擎在这种稍复杂的情况下就晕掉了,只能老老实实按 SQL 写的逻辑去执行,性能就会陡降。

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

比如这么一个更复杂的需求,做电商系统中的漏斗分析,计算每一步动作后的用户流失率。下面是一个实际业务中发生的三步漏斗计算,SQL 会写成这样:

WITH e1 AS (
    SELECT userid, visittime AS step1_time, MIN(sessionid) AS sessionid, 1 AS step1
    FROM defined_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 defined_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 defined_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 funnel_conversions.step1 IS NOT NULL THEN funnel_conversions.step1_userid  ELSE NULL END) AS step1_count,
    COUNT(DISTINCT CASE WHEN funnel_conversions.step2 IS NOT NULL THEN funnel_conversions.step2_userid  ELSE NULL END) AS step2_count,
    COUNT(DISTINCT CASE WHEN funnel_conversions.step3 IS NOT NULL THEN funnel_conversions.step3_userid  ELSE NULL END) AS step3_count,
    COUNT(DISTINCT CASE WHEN funnel_conversions.step3 IS NOT NULL THEN funnel_conversions.step3_userid  ELSE NULL END) 
    / COUNT(DISTINCT CASE WHEN funnel_conversions.step1 IS NOT NULL THEN funnel_conversions.step1_userid  ELSE NULL END) AS step3_rate
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, tep3
    FROM e1 LEFT JOIN e2 ON e1.userid=e2.userid LEFT JOIN e3 ON e2.userid=e3.userid ) funnel_conversions
LEFT JOIN sessions s ON funnel_conversions.step1_sessionid = s.id 
GROUP BY s.devicetype

这个 SQL“绕”得很严重了,看懂非常费劲,摆在这里就是感受一下。这还只是三步,想再多算几步还得写更多子查询,那就摆不出来了。这种复杂的 SQL,真想不出这能怎么做优化。结果,这句 SQL 在 Snwoflake 的 Medium 集群上(4 节点)跑了 3 分钟没出来,用户只能放弃。

所以,不能也不要指望数据库优化,还是要自己写出高性能算法出来。

那么,什么样的程序语言才能写出刚才说的这些算法呢?

很多,C++Java 这些都可以。理论上用 SQL 写的存储过程也可以,但工程实现的结果还是会太慢,这还是因为缺失离散性。比如 TopN 问题,在 SQL 中要保存那 10 个成员的小集合也得用临时表,很难写也很慢。

那就不用 SQL,用 C++,Java 这些去写好了。

这又回到我们之前说的集合化特性了,这些语言缺乏集合化,写出来很繁琐。像分组 TopN,如果要考虑各种数据类型、多个分组键、还可能带着计算式,几千行也未必写得出来。而漏斗分析这种复杂运算,还要考虑大数据存储问题,写个几万行也很正常。开发成本极高,以后维护时也累死人。

这样,在现实中就没有可操作性了。

所以,代码能写着简单就变得非常有意义了。一方面是短小,这意味着工作量少,另一方面还要容易,这意味着更多的程序员可以写。从这个角度上看,写着简单和跑得快是一回事。想跑得快,就是要有一种程序语言能让高性能算法写着简单,这才有可操作性。

这样的标准,可能只有 esProc SPL 适合了,它同时拥有集合化和离散性两种特性,又提供相当多固有的高性能算法库函数,这才能写着简单,从而跑得快。

前面的前 10 名问题用 SPL 写出来是这样的:

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


SPL 把 TopN 理解为聚合计算,这个语句中没有排序的字样,也就不会做大排序,而采用刚才说的快速算法了。而且,这里分组前 10 名和全集前 10 名的写法基本一样,只是多了分组键。这也是在集合化的基础上支持了离散性的结果。

漏斗分析用 SPL 写出来是这样:

A
1 =now()
2 =eventgroup=file("eventgroup.btx").import@b()
3 =devicetype=file("devicetype.btx").import@b()
4 =long(elapse(arg_date,-14))
5 =long(arg_date)
6 =long(arg_date+1)
7 =A2.(case(NAME,"SiteVisit":1,"ProductDetailPage":2,"OrderConfirmationType1":3;null))
8 =file("defined_events.ctx").open()
9 =A8.cursor@m(USERID,SESSIONID,VISITTIME,EVENTGROUPNO;VISITTIME>=A4 && VISITTIME
10 =sessions=file("sessions.ctx").open().cursor@m(USERID,ID,DEVICETYPENO;;A9)
11 =A9.joinx@m(USERID:SESSIONID,A10ID,DEVICETYPENO)
12 =A11.group(USERID)
13 =A12.new(~.align@a(3,EVENTGROUPNO):e,e(1).select(VISITTIME
14 =A13.run(e=join@m(e1:e1,SESSIONID;e2:e2,SESSIONID).select(e2=e2.select(VISITTIME>e1.VISITTIME && VISITTIME
15 =A14.run(e0=e1.id(DEVICETYPENO),e1=e.min(e1.VISITTIME),e2=e.min(e2),e=e.min(e1.SESSIONID),e3=e3.select(SESSIONID==e && VISITTIME>e2 && VISITTIME
16 =A15.news(e;~:DEVICETYPE,e2,e3)
17 =A16.groups(DEVICETYPE;count(1):STEP1_COUNT,count(e2):STEP2_COUNT,count(e3):STEP3_COUNT,null:STEP3_RATE)
18 =A17.run(DEVICETYPE=devicetype.m(DEVICETYPE).DEVICETYPE,STEP3_RATE=STEP3_COUNT/STEP1_COUNT)
19 =interval@s(A1,now())

这个代码要比前面的 SQL 更短,而且更灵活,再增加几步也还是这段代码。实测的结果,这段代码用单台服务器 10 秒就跑完了。 有了集合化和离散性的 SPL,才能做到写着简单又跑得快。


本文来源
github.com

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10442

    浏览量

    206549
  • 计算机
    +关注

    关注

    19

    文章

    6649

    浏览量

    84523
  • SQL
    SQL
    +关注

    关注

    1

    文章

    738

    浏览量

    43461
  • 程序
    +关注

    关注

    114

    文章

    3631

    浏览量

    79541

原文标题:写着简单和跑得快是一回事,SQL 为什么不可能跑得快?

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    反转“不可能”,硬件创新以你为中心

    回到“大众创业,万众创新”风口下的智能硬件创业也是如此,从最初的idea到demo,从路演到资金成功注入,从起初供应链搭建直至完善,太多的“不可能”到“可能”,2015中国硬件创新大赛陪你一起见证。
    发表于 09-22 11:48 1185次阅读

    深井中的深度学习:MCU+AI,让“不可能”的田园机井智能抄表成为可能

    深井中的深度学习:MCU+AI,让“不可能”的田园机井智能抄表成为可能
    的头像 发表于 09-21 17:41 542次阅读
    深井中的深度学习:MCU+AI,让“<b class='flag-5'>不可能</b>”的田园机井智能抄表成为<b class='flag-5'>可能</b>!

    在PSoC Creator中实现电阻是不可能的吗

    你好,我使用PSoC3来实现一些模拟滤波器和其他子电路。这些滤波器,当然,包括OAS和无源元件,如电阻器和电容器。据我所知,在PSoC Creator中实现电阻是不可能的,对吗?电容器呢?有没有
    发表于 04-08 09:55

    STM32小系统实现wifi无线通讯程序怎么才能跑得起来

    等大神帮助,做了个STM32小系统,主要实现wifi无线通讯,使用的是锂电池供电,但目前问题是设备端必须要有一根线与jlink共地(jlink与电脑相连 )程序才能跑得起来,不然就无法执行进去,网上说的要拔排线试过了(不起作用),求大家帮助
    发表于 05-08 06:35

    如何让不可能成为可能

    我们应当张开双臂拥抱快节奏的技术变革,它推动科学技术的进步,让人们更加紧密相连并感到安全自信,它改变了我们此前认为的不可能。这些成果的影响不再只孤立于一个狭窄的垂直市场,它渗透进了各行各业,对现有
    发表于 10-15 06:12

    如果个人去流片的话可不可能啊?

    如果个人去流片的话可不可能啊?自己设计一个芯片去流片,可能吗?
    发表于 06-18 06:30

    锤子新机不可能是T3!应是坚果2或新系列

      春天了,老罗之前说过在春天会发布一款新机,所以网上就爆出了许多关于新机的消息,但是有很多消息说即将发布的新机是传闻已久的T3,这个是不可能的,春天要不发布的机器根本不可能是T3。
    发表于 03-16 10:10 2845次阅读

    什么是区块链不可能三角为什么不可突破

    CAP定理证明了:当网络存在分区时,提供可靠的原子一致性数据是不可能的,但是想要实现一致性、可用性、分区容错性,三个属性中的两个是可行的。在异步通信系统中,当没有锁提供时,如果出现消息丢失,即使允许过时的数据返回,提供一致性数据也是不可能的。在同步通信系统中,可以在一致性
    发表于 02-26 11:03 2923次阅读
    什么是区块链<b class='flag-5'>不可能</b>三角为什么<b class='flag-5'>不可</b>突破

    自动驾驶车上路需智能路网配套

    路好,车才能跑得快
    的头像 发表于 05-14 11:31 2045次阅读

    如何解决公链面临的不可能三角模型问题

    「底层公链 → 解决方案 → 行业应用」是区块链业内公认的发展逻辑。作为底层技术,公链支撑整个行业的发展,决定区块链应用生态的发展进程。然而,公链面临「不可能三角模型」、「根特别多、没长叶子的市场」等困境,极大的限制了区块链技术的商用落地进程。
    发表于 07-19 10:58 1986次阅读
    如何解决公链面临的<b class='flag-5'>不可能</b>三角模型问题

    马云与马斯克上演了一出鸡同鸭讲式的“双马尬聊”

    “和计算机下棋这很愚蠢,像100年前人们创造了机器,人们说我可以比汽车跑得快这是不可能的,只有傻子才会去和汽车赛跑……我们要做我们擅长的事情。”
    的头像 发表于 09-03 14:49 4348次阅读

    什么是区块链中的不可能三角

    区块链本质上是一个去中心化的分布式账本数据库,它也存在“不可能三角”。今天,我们就来讲讲“不可能三角”在区块链世界是如何权衡和妥协的。
    发表于 12-13 08:59 8220次阅读

    区块链如何解决医疗数据中的不可能三角

    不可能三角”一词,最早来自金融经济领域,指的是资本自由流动、汇率稳定和货币政策独立性三者不可能兼得。
    发表于 01-17 10:26 1455次阅读

    超轻量分组密码算法GRANULE的不可能差分分析

    GRANULE算法是一个超轻量分组密码算法,有着较好的软硬件实现性能,但目前尚没有该算法在不可能差分分析下的安全性评估结果。为此,利用中间相错技术,找到 GRANULE64算法多条5轮不可能差分区
    发表于 06-01 14:27 3次下载

    英飞凌主驱逆变器助力电动汽车跑得快跑得

    电动汽车越来越受欢迎。如今电动汽车的发展趋势是,电机功率越来越大,但为了保证续航里程,行驶中的电耗也要越来越低。这看似不可能完成的任务,背后的最大功臣正是主驱逆变器。
    的头像 发表于 04-05 13:46 108次阅读
    英飞凌主驱逆变器助力电动汽车<b class='flag-5'>跑得快跑得</b>远