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

简介:

03d2311ae523b1333cf487162b2b465edb2cc6b9

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

设有两个表:

5bdbbf09ba06117635f58bcd8e6deb047583d3f1

其中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不方便描述这个运算的细节过程了,我们采用过程式语法、并用文件作为数据源来说明计算过程:

60c82b4d76a4103d74e4bf881f7ba3604ece9e9b

上面算法中,第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

本文作者:蒋步星

本文来自云栖社区合作伙伴“数据蒋堂”,了解相关信息可以关注“数据蒋堂”微信公众号

相关文章
|
1月前
|
C语言
指向指针数据的指针变量
指向指针数据的指针变量
10 0
|
8天前
|
存储 搜索推荐 C语言
运用指针轻松实现多个数据排列
这段内容介绍了一种使用PLC编程进行数据排序的方法,特别是通过冒泡排序算法来实现。文中以一个包含5个数字(90, 21, 132, -58, 34)的序列为例,详细解释了排序过程,包括多轮比较和数据交换。在每一轮中,最大的数字会移动到右侧。通过嵌套的FOR循环结构实现程序逻辑,其中外层循环控制轮次,内层循环处理每轮的比较和交换。最终,程序能够将序列从小到大排序。
22 6
|
1月前
|
分布式计算 Hadoop Java
Flink CDC产品常见问题之tidb cdc 数据量大了就疯狂报空指针如何解决
Flink CDC(Change Data Capture)是一个基于Apache Flink的实时数据变更捕获库,用于实现数据库的实时同步和变更流的处理;在本汇总中,我们组织了关于Flink CDC产品在实践中用户经常提出的问题及其解答,目的是辅助用户更好地理解和应用这一技术,优化实时数据处理流程。
|
4月前
指向结构体类型数据的指针
指向结构体类型数据的指针。
29 3
|
5月前
【指针的进阶(3)】回调函数和qsort排序各种类型的数据
【指针的进阶(3)】回调函数和qsort排序各种类型的数据
34 0
|
5月前
|
机器学习/深度学习 存储 人工智能
【网安AIGC专题11.8】论文15 ChatGPT在软件工程中的全面作用:程序语法(AST生成、表达式匹配) 静态行为、动态分析(数据依赖和污点分析、指针分析) 提示设计(角色提示、指令提示)
【网安AIGC专题11.8】论文15 ChatGPT在软件工程中的全面作用:程序语法(AST生成、表达式匹配) 静态行为、动态分析(数据依赖和污点分析、指针分析) 提示设计(角色提示、指令提示)
73 0
|
9月前
|
存储
学C的第二十三天【继续深度剖析数据在内存中的存储:3. 浮点型在内存中的存储(重点);练习:1. 有序序列判断;2. 获得月份天数(多组输入);3. 使用指针打印数组内容;4. 使用指针使字符串逆序】-2
(4). 取出内存中的 指数E(三种情况):E全为1 指数E 是通过 真实值+中间值 算出来的,如果E全是1,(32位系统)说明E的真实值是 128,指数是128说明这个值是非常大的。 这时,如果 有效数字M 全为0,表示 ±无穷大(正负取决于符号位s)
|
10月前
|
SQL 数据库
springboot+mybatis,mapper调用查询的数据为空,报空指针异常
springboot+mybatis,mapper调用查询的数据为空,报空指针异常
703 0