数据蒋堂 | 遍历复用

640?wx_fmt=png

作者:蒋步星

来源:数据蒋堂

本文共2500字,建议阅读7分钟减少外存(硬盘)访问量一直是提高大数据计算性能的永恒话题。


640?wx_fmt=png


减少外存(硬盘)访问量一直是提高大数据计算性能的永恒话题,我们也讨论过列存、压缩等直接减少访问量甚至存储量的手段。除了这些存储层面的方法外,在算法和计算实现环节,也可以想办法减少外存的访问量。


遍历是大数据计算中必不可少的环节。有时候,我们会发现在一个计算任务中,会有两次(或更多)涉及针对同一批数据的遍历动作。如果我们能有办法让两次遍历合并成一次,那么总的计算量(CPUT的动作)并没有差别,但硬盘的访问量会减少了一半,这样计算性能还是能得到提升,对于数据密集型计算的提升效果还相当明显。


设有简化的帐目表T的数据结构中如下字段:账号A、日期D、发生地P,金额M。


现在我们想统计账号a1和a2的余额,用SQL写出来是这样:

SELECT SUM(M) FROM T WHERE A=a1

SELECT SUM(M) FROM T WHERE A=a2

这样两句计算就会导致遍历两次表T,如果表T非常大,计算效率就很低了。


如果我们把这句SQL写成这样:

SELECT SUM(CASE WHEN A=a1 THEN M ELSE 0 END),

        SUM(CASE WHEN A=a2 THEN M ELSE 0 END) FROM T

一个语句把这两个统计值都计算出来,句子复杂了不少,数据库的总计算量也反而略有变大(判断次数相同,累计次数变多,要多加很多次0),但是表T却只要遍历一次就可以了,最后获得的运算效率却要高很多。


作为数据库程序员,要学会这种技巧。


不过,并不是所有运算都可以用CASE WHEN来对付。


我们想分别统计每天的金额合计和每个发生地的金额合计,写出SQL是:

SELECT D,SUM(M) FROM T GROUP BY D

SELECT P,SUM(M) FROM T GROUP BY P

SQL没有直接提供遍历复用的语法,不同的WHERE还可以用CASE WHEN去绕,但不同的GROUP BY就无法再合并起来了,只能遍历两次表T。


理论上,使用数据库游标可以做到这一点,定义一个基于SELECT D,P,M FROM T的游标,一行行取数,然后分别针对D和P去做GROUP BY运算。这个运算用SQL写起来实在太麻烦了,而且游标遍历的性能很差,结果不仅繁琐而且更慢了。


SQL的体系下解决不了这个问题了,我们需要设计新的概念和语法来实现遍历复用。


在游标机制中引入管道的概念。游标遍历数据实施某个运算的同时,将数据压入到一个管道中,而管道上可以再定义另一个运算,这样,数据在一次遍历时可以同时获得游标本身以及附加的管道上的两个运算结果。上面的的运算写出来的大体代码结构如下:


cs = T.cursor() 	
ch = channel(cs).groups( P; sum(M) ) 	
dg = cs.groups( D; sum(M) ) 	
pg = ch.result()


channel(cs)在游标cs上绑定一个管道ch,并且定义一个针对P的分组运算,然后游标cs照常遍历并实施针对D的分组运算,遍历完毕后,从管道ch中取了相关结果就可以了。


前面那个不同条件汇总的问题当然也可以用游标和管道机制写出来


cs = T.cursor() 	
ch = channel(cs).select( A==a2 ).sum(M)) 	
m1 = cs.select( A==a1 ).sum(M) 	
m2 = ch.result()


代码结构都是一样的。


当然,一个游标上还可以附加多个管道,比如刚才这两件事(条件汇总和不同分组)也可以一次遍历做完:


cs = T.cursor() 	
ch1 = channel(cs).select( A==a2 ).sum(M)) 	
ch2 = channel(cs).groups( P; sum(M) ) 	
ch3 = channel(cs).groups( D; sum(M) ) 	
m1 = cs.select( A==a1 ).sum(M) 	
m2 = ch1.result() 	
dg = ch2.result 	
pg = ch3.result()



再举一个计算中位数的例子。


计算中位数时需要排序,但一般情况下排序运算只管排序本身,并不管计数,排序完成了甚至还不知道总共有多少数据, 这时候要找中位数,就还得再做一次COUNT遍历数据,浪费时间。如果有管道机制,我们就可以在排序的同时把计数也做完了。


cs = T.cursor()  	
ch = channel(cs).count()  	
s = cs.sortx(M) //遍历排序过程中把管道上的计数也完成 	
k = ch.result()  	
m = s.skip( (k-1)\2 ).fetch@x(2-k%2).avg(M) //找出中间一个或两个数

专栏作者简介

640?

润乾软件创始人、首席科学家

清华大学计算机硕士,中国大数据产业生态联盟专家委员,著有《非线性报表模型原理》等,1989年,中国首个国际奥林匹克数学竞赛团体冠军成员,个人金牌;2000年,创立润乾公司;2004年,首次在润乾报表中提出非线性报表模型,完美解决了中国式复杂报表制表难题,目前该模型已经成为报表行业的标准;2014年,经过7年开发,润乾软件发布不依赖关系代数模型的计算引擎——集算器,有效地提高了复杂结构化大数据计算的开发和运算效率;2015年,润乾软件被福布斯中文网站评为“2015福布斯中国非上市潜力企业100强”;2016、2017年,荣获中国电子信息产业发展研究院评选的“中国软件和信息服务业十大领军人物”;2017年度中国数据大工匠、数据领域专业技术讲堂《数据蒋堂》创办者。

数据蒋堂

《数据蒋堂》的作者蒋步星,从事信息系统建设和数据处理长达20多年的时间。他丰富的工程经验与深厚的理论功底相互融合、创新思想与传统观念的相互碰撞,虚拟与现实的相互交织,产生出了一篇篇的沥血之作。此连载的内容涉及从数据呈现、采集到加工计算再到存储以及挖掘等各个方面。大可观数据世界之远景、小可看技术疑难之细节。针对数据领域一些技术难点,站在研发人员的角度从浅入深,进行全方位、360度无死角深度剖析;对于一些业内观点,站在技术人员角度阐述自己的思考和理解。蒋步星还会对大数据的发展,站在业内专家角度给予预测和推断。静下心来认真研读你会发现,《数据蒋堂》的文章,有的会让用户避免重复前人走过的弯路,有的会让攻城狮面对扎心的难题茅塞顿开,有的会为初入行业的读者提供一把开启数据世界的钥匙,有的甚至会让业内专家大跌眼镜,产生思想交锋。

数据蒋堂第二年往期回顾:

数据蒋堂 | 莫非我就是被时代呼唤的数学人

数据蒋堂 | SQL是描述性语言?

数据蒋堂 | 存储和计算技术的选择

数据蒋堂 | 人工智能中的“人工”

数据蒋堂 | 中国报表漫谈

数据蒋堂 | 内存数据集产生的隐性成本

数据蒋堂 | 多维分析预汇总的功能盲区

数据蒋堂 | 多维分析预汇总的存储容量

数据蒋堂 | 多维分析预汇总的方案探讨

数据蒋堂 | 数据库的封闭性

数据蒋堂 | 内存数据集产生的隐性成本

数据蒋堂 | 前半有序的大数据排序

数据蒋堂 | “后半”有序的分组

数据蒋堂 | 时序数据从分表到分库

数据蒋堂 | BI系统的前置计算

数据蒋堂 | 性能优化是个手艺活

数据蒋堂 | 数据分布背后的逻辑

数据蒋堂 | 从一道招聘考题谈起

数据蒋堂 | 为什么我们需要C程序员

数据蒋堂 | 报表工具的SQL植入风险

数据蒋堂 | 内置的数据无法实现高性能

数据蒋堂 | 怎样生成有关联的测试数据

640?wx_fmt=jpeg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值