【数据蒋堂】第33期:JOIN提速 - 外键指针化

我们再来看重新定义JOIN后如何能够提高运算性能,先看外键式JOIN的情况。

设有两个表:

其中sales表中的productid是指向products表中id字段的外键,id是products表的主键。

现在我们想计算销售额有多少(为简化讨论,就不再设定条件了),用SQL写出来:

SELECT SUM(sales.quantity*products.price) FROM sales JOIN products ON sales.productid=products.id

基于笛卡尔积定义的JOIN,原则上只能两层循环全遍历来计算,不过这个计算量实在太大,关系数据库一般采用HASH分段方法优化,即分别计算两表关联字段的HASH值,将HASH同值记录拼到一起再做小范围遍历。网上有很多文章介绍这个算法,这里就不详述了。这样做后的复杂度能显著降低,但仍然要做多次HASH值计算和比对。

我们再用前述的简化的JOIN语法写出这个运算:

SELECT SUM(quantity*productid.price) FROM sales

而这个写法其实也就预示了它还可以有更好的优化方案,下面来看看怎样实现。

我们先考虑全内存的情况,如果所有数据都能够装入内存,我们可以实现外键指针化。

将事实表sales中的外键字段productid,转换成指向维表products记录的指针,即productid的取值就已经是某个products表中的记录,那么就可以直接引用记录的字段进行计算了。

用SQL不方便描述这个运算的细节过程了,我们采用过程式语法、并用文件作为数据源来说明计算过程:

上面算法中,第2步建主键索引一般也是用HASH办法,对id计算HASH值,第4步转换指针还是计算productid的HASH值与P的HASH索引表对比。这样的话,如果只做一次关联运算,指针化的方案和传统HASH分段方案的计算量基本上一样,没有根本优势。

但不同的是,如果数据能在内存中放下,这个指针一旦建立起来之后可以复用,也就是说第2和第4步只要做一次,下次再做关于这两个字段的关联运算时就不必再计算HASH值和比对了,性能就能大幅提高。而关系代数体系下没有对象指针这个概念,并且基于笛卡尔积定义的JOIN运算也无法假定外键指向记录的唯一性,没办法使用外键指针化的方法,每次关联时都要计算HASH值并比对。

而且,如果事实表中有多个外键分别指向多个维表,传统的HASH分段JOIN方案每次只能解析掉一个,有N个JOIN要执行N遍动作,每次关联后都需要保持中间结果供下一轮使用,计算过程复杂得多,数据也会被遍历多次。而外键指针化方案在面对多个外键时,只要对事实表遍历一次, 没有中间结果,计算过程要清晰很多。

还有一点,内存本来应当是很适合并行计算的,但HASH分段JOIN算法却不容易并行。即使把数据分段并行计算HASH值,但要把相同HASH值的记录归聚到一起供下一轮比对,就会发生共享资源冲突的事情,这会把并行计算的优势完全抵消掉。而外键式JOIN模型下,关联两表的地位不对等,明确区分出维表和事实表后,只要简单地将事实表分段就可以并行计算。

将HASH分段技术参照外键属性方案进行改造后,也能一定程度地改善多外键一次解析和并行能力,有些数据库能在工程层面上实施这种优化。不过,这种优化在只有两个表JOIN时问题不大,在有很多表及各种JOIN混在一起时,数据库并不容易识别出应当把哪个表当作事实表去并行遍历、而把其它表当作维表建立HASH索引,这时优化并不总是有效的。所以我们经常会发现当JOIN的表变多时性能会急剧下降的现象(常常到四五个表时就会发生,结果集并无显著增大)。而从JOIN模型上引入外键概念后,将这种JOIN专门处理时,就总能分清事实表和维表,更多的JOIN表只会导致性能的线性下降。

内存数据库是当前比较火热的技术,但上述分析表明,采用SQL模型的内存数据库在JOIN运算上是很难快起来的!

原文发布时间为:2017-12-5

本文作者:蒋步星

时间: 2024-10-02 22:23:26

【数据蒋堂】第33期:JOIN提速 - 外键指针化的相关文章

数据蒋堂 | JOIN提速 - 外键指针化

我们来看重新定义JOIN后如何能够提高运算性能,先看外键式JOIN的情况. 设有两个表: 其中sales表中的productid是指向products表中id字段的外键,id是products表的主键. 现在我们想计算销售额有多少(为简化讨论,就不再设定条件了),用SQL写出来: SELECT SUM(sales.quantity*products.price) FROM sales JOIN products ON sales.productid=products.id 基于笛卡尔积定义的JO

【数据蒋堂】功夫都在报表外--漫谈报表性能优化

应用系统中的报表,作为面向业务用户的窗口,其性能一直被高度关注.用户输入参数后都希望立即就能看到统计查询结果,等个十几二十秒还能接受,等到三五分钟的用户体验就非常恶劣了. 那么,报表为什么会慢,又应当从哪里入手进行性能调优呢? 数据准备 当前应用中的报表大都用报表工具开发,当报表响应太慢时,不明就里的用户就会把矛头指向使用报表工具的开发人员或者报表工具厂商.其实,大多数情况报表的慢只是个表现,背后的原因是数据准备太慢,在数据进入报表环节之前就已经慢了,这时再去优化报表开发或压迫报表工具并没有用处

实现删除主表数据时, 判断与之关联的外键表是否有数据

问题描述:某个基础信息表,与系统中30多个表存在外键关系,当删除基础数据时,需要判断是否已经被用过,如果用过则更改标志位,如果没有用过则直接删除,如何能很好实现这个处理?最好能够自动适应表的变化 问题解决(SQL Server 2005)-- SQL Server 2005的错误处理容易控制, 因此, SQL Server 2005中可以直接删除, 通过错误处理来确定是否需要更新. -- 示例如下.USE tempdbGO CREATE TABLE m(    id int PRIMARY KE

开源大数据周刊-第33期

阿里云E-MapReduce实践 使用E-MapReduce服务将Kafka数据导入OSSkafka是一个开源社区常用的消息队列,对阿里云文件存储系统OSS没有官方的支持.本文通过一个例子,实现了kafka的数据写入阿里云OSS. 资讯 能源行业将被重构,大数据有哪些"挖"法?能源互联网的风口已来.在能源 互联网应用中非常重要的一点,是要对每一个节点进行精准画像,以能源用户为中心,将每个用能设备各个环节数据化,提高管理能力,产生新的价值. 到2017年,大数据有望实现这六大预言大数据正

【数据蒋堂】第31期:JOIN简化 - 维度对齐

那么问题来了,这显然是个有业务意义的JOIN,它算是前面所说的哪一类呢? 这个JOIN涉及了表Orders和子查询A与B,仔细观察会发现,子查询带有GROUP BY id的子句,显然,其结果集将以id为主键.这样,JOIN涉及的三个表(子查询也算作是个临时表)的主键是相同的,它们是一对一的同维表,仍然在前述的范围内. 但是,这个同维表JOIN却不能用上一期说的写法简化,子查询A,B都不能省略不写. 可以简化书写的原因在于:我们假定事先知道数据结构中这些表之关联关系.用技术术语的说法,就是知道数据

【数据蒋堂】第30期:JOIN简化 - 消除关联

我们将等值JOIN分成三种情况来分别讨论,分情况相当于加强了条件,我们可以充分利用每种情况下的特征. 1. 外键属性化 先看个例子,设有如下两个表: employee表和delpartment表的主键都是其中的id字段,employee表的department字段是指向department表的外键,department表的manager字段又是指向employee表的外键.这是很常规的表结构设计. 现在我们想问一下:哪些美国籍员工有一个中国籍经理? 用SQL写出来是这样的: SELECT A.*

【数据蒋堂】第32期:JOIN简化 - 意义总结

我们重新审视和定义了等值JOIN运算,并简化了语法.一个直接的效果显然是让语句书写和理解更容易.外键属性化.同维表等同化和主子表一体化方案直接消除了显式的关联运算,也更符合自然思维:维度对齐则可让程序员不再关心表间关系,降低语句的复杂度. 简化JOIN的好处不仅在于此,还能够降低出错率. 我们知道,SQL允许用WHERE来写JOIN运算的过滤条件(回到原始的笛卡尔积式的定义),很多程序员也习惯于这么写.当JOIN表只有两三个的时候,那问题还不大,但如果JOIN表有七八个甚至十几个的时候,漏写一个

【数据蒋堂】第36期:JOIN延伸:维度概念

谈到数据分析时常常会用到维度这个词,针对数据立方体的钻取.旋转.切片等操作都是围绕维度进行的,几乎所有的数据分析人员都知道并会运用这个术语,但要问及它的定义,却几乎没有人能给出来. 通俗来讲,我们把用来分类的属性(字段)称为维度,比如地区.年度.产品类型等:而另外一些用于聚合运算的属性则称为测度,比如销售额.产量.考试成绩等.维度不能做聚合运算,比如计算地区合计是没有意义的:测度则不能用于分类,比如按销售额分类也没什么业务意义.我们通常就是用是否"可用于分类"来判定一个属性是不是维度,

数据蒋堂 | JOIN运算剖析

JOIN是SQL中用于多表关联的运算,无论从程序员编写还是数据库实现角度来看,JOIN都是SQL中最难的运算. 其实,SQL对JOIN的定义非常简单,就是对两个集合(表)做笛卡尔积后再按某种条件过滤,写出来的语法也就是A JOIN B ON ...的形式.原则上,笛卡尔积后的结果集应当是以两集合成员构成的二元组为成员,不过由于SQL中的集合成员总是有字段的记录,而且也不支持泛型数据类型来描述成员为记录的二元组,所以就简单地把结果集处理成由两表记录的字段合并后构成的新记录集合.这也是JOIN一词在