多表关联查询的性能优化技巧:预关联
wptr33 2025-09-04 19:48 8 浏览
一、 问题背景与适用场景
SQL中JOIN的性能是个老大难问题,特别是关联表较多时,计算性能会急剧下降。
SQL实现JOIN一般是采用HASH分堆的办法,即先计算关联键的HASH值,再将相同HASH值的记录放到一起再做遍历对比。每一个JOIN都要做一轮这样的运算。
如果数据量相对于内存并不是很大,可以事先全部加载到内存中,那么可以利用内存指针的机制,事先把关联关系建立好。这样做运算时就不必再做HASH与对比运算了。具体来说,就是在数据加载时一次性把HASH和对比运算做完,用指针方式保存关联结果,然后每次运算可以直接引用到关联记录,从而提高运算的性能。
不幸的是,SQL没有指针数据类型,无法实现这个优化逻辑,即使数据量可以在内存中装下,也很难利用预关联技巧提速,基于SQL的内存数据库也大都有这个缺点。而SPL有指针数据类型,就可以实现这种机制。
我们下面来测试一下SQL实现单表计算和多表关联计算的差距,再用SPL利用预关联技巧同样做一遍,看一下两者的差距对比。
二、 测试环境
采用TPCH标准生成的8张数据表,共50G数据(要小到能放进内存)。TPCH数据表的结构网上有很多介绍,这里就不再赘述了。
测试机有两个Intel2670 CPU,主频2.6G,共16核,内存128G,SSD固态硬盘。
由于 lineitem 表数据量太大,这台服务器内存不足以将它装入,所以创建了一张表结构与它一样的表 orderdetail, 将数据量减少到内存能装下,下面就用这张表来做测试。
为方便看出差距,下面测试都用单线程计算,多核并不起作用。
三、 SQL测试
这里用 Oracle 数据库作为 SQL 测试的代表,从orderdetail表里查询每年零件订单的总收入。
1. 两表关联
查询的SQL语句如下:
select
l_year,
sum(volume) as revenue
from
(
select
extract(year from l_shipdate) as l_year,
(l_extendedprice * (1 - l_discount) ) as volume
from
orderdetail,
part
where
p_partkey = l_partkey
and length(p_type)>2
) shipping
group by
l_year
order by
l_year;
2. 六表关联
查询的SQL语句如下:
select
l_year,
sum(volume) as revenue
from
(
select
extract(year from l_shipdate) as l_year,
(l_extendedprice * (1 - l_discount) ) as volume
from
supplier,
orderdetail,
orders,
customer,
part,
nation n1,
nation n2
where
s_suppkey = l_suppkey
and p_partkey = l_partkey
and o_orderkey = l_orderkey
and c_custkey = o_custkey
and s_nationkey = n1.n_nationkey
and c_nationkey = n2.n_nationkey
and length(p_type) > 2
and n1.n_name is not null
and n2.n_name is not null
and s_suppkey > 0
) shipping
group by
l_year
order by
l_year;
3. 测试结果
两个查询语句都用了嵌套写法,Oracle自动优化后的计算性能比无嵌套时还要好一些(无嵌套时group by和select有可能会有重复计算)。
这两个测试数据是多次运行后的结果,在测试中发现,Oracle 在第一次运行某查询时,往往比第 2、3... 次要慢很多,说明在内存大于数据量时,数据库可以把全部数据都缓存进内存(Oracle的缓存很强),所以我们取多次运行中最快那一次的时间,这样就几乎没有硬盘读取时间,仅是运算时间。
同时,在上面两组测试中,过滤条件始终都为真,也就是没有对数据产生实质过滤,两个查询都涉及orderdetail表的全部记录,计算规模是相当的。
从测试结果可以看出,六表关联比两表关联慢了167/26=6.4倍!性能下降非常多。排除掉硬盘时间后,这里增加的时间主要就是表间关联以及针对关联表字段的判断,而这些判断非常简单,所以大部分时间消耗在表间关联上。
这个测试表明,SQL的JOIN性能确实很差。
四、 SPL预关联测试
1. 预关联
实现预关联的SPL脚本如下:
脚本中前7行分别将7个组表读入内存,生成内表,并设成全局变量。后5行完成表间连接。在SPL服务器启动时,就先运行此脚本,完成环境准备。
我们来看看预关联后,内存中表对象的数据结构,以orderdetail为例:
图中只列了orderdetail的第一条记录的预关联情况,其它记录与此类似。限于版面宽度,各表只列出了部分字段。
2. 两表关联
编写SPL脚本如下:
3. 六表关联
编写SPL脚本如下:
预关联后,SPL代码也非常简单,关联表的字段直接可以作为本表字段的子属性访问,很易于理解。
4. 运行结果
六表关联仅仅比两表关联慢2倍,基本上就是增加的计算量(引用这些关联表字段)的时间,而因为有了预关联,关联运算本身不再消耗时间。
五、 结论
测试结果汇总:
六表关联比两表关联,SQL慢了6.4倍,说明SQL处理JOIN消耗CPU很大,性能降低明显。而采用预关联机制后的SPL只慢2倍,多JOIN几个表不再出现明显的性能下降。
在进行关联表较多的查询时,如果内存大到足以将数据全部读入内存(内存数据库的应用场景),使用预关联技术将极大地提升计算性能!而关系数据库(包括内存数据库)用SQL语言则无法实现这一优化技术。
友乾营是专注数据技术的知识分享平台。这里,你将有机会与近百位技术专家共同沟通交流,寻找优势互补,达成资源对接。另外,友乾营将定期(每周一次,周三晚19:30)安排专题技术直播活动。
欢迎IT从业者或对数据相关技术感兴趣的人员入群交流、分享。共同打造“有热度的话题,有温度的情感,有深度的思想,有高度的评论”高品质的友乾营社群。
识别下面二维码,在页面上加友乾营小助手为好友
相关推荐
- 栋察宇宙(二十一):Python 文件操作全解析
-
分享乐趣,传播快乐,增长见识,留下美好。亲爱的您,这里是LearingYard学苑!...
- python中12个文件处理高效技巧,不允许你还不知道
-
在Python中高效处理文件是日常开发中的核心技能,尤其是处理大文件或需要高性能的场景。以下是经过实战验证的高效文件处理技巧,涵盖多种常见场景:一、基础高效操作...
- Python内置模块bz2: 对 bzip2压缩算法的支持详解
-
目录简介知识讲解2.1bzip2压缩算法原理2.2bz2模块概述...
- Python文件及目录处理方法_python目录下所有文件名
-
Python可以用于处理文本文件和二进制文件,比如创建文件、读写文件等操作。本文介绍Python处理目录以及文件的相关方法。...
- The West mustn't write China out of WWII any longer
-
ByWarwickPowellLead:Foreightdecades,theWesthasrewrittenWorldWarIIasanAmericanandEuro...
- Python 的网络与互联网访问模块及应用实例(一)
-
Python提供了丰富的内置模块和第三方库来处理网络与互联网访问,使得从简单的HTTP请求到复杂的网络通信都变得相对简单。以下是常用的网络模块及其应用实例。...
- 高效办公:Python处理excel文件,摆脱无效办公
-
一、Python处理excel文件1.两个头文件importxlrdimportxlwt...
- Python进阶:文件读写操作详解_python对文件的读写操作方法有哪些
-
道友今天开始进阶练习,来吧文件读写是Python编程中非常重要的技能,掌握这些操作可以帮助你处理各种数据存储和交换任务。下面我将详细介绍Python中的文件读写操作。一、基本文件操作...
- [827]ScalersTalk成长会Python小组第11周学习笔记
-
Scalers点评:在2015年,ScalersTalk成长会完成Python小组完成了《Python核心编程》第1轮的学习。到2016年,我们开始第二轮的学习,并且将重点放在章节的习题上。Pytho...
- ScalersTalk 成长会 Python 小组第 9 周学习笔记
-
Scalers点评:在2015年,ScalersTalk成长会完成Python小组完成了《Python核心编程》第1轮的学习。到2016年,我们开始第二轮的学习,并且将重点放...
- 简析python 文件操作_python对文件的操作方法
-
一、打开并读文件1、file=open('打开文件的路径','打开文件的权限')#打开文件并赋值给file#默认权限为r及读权限str=read(num)读文件并放到字符串变量中,其中num表...
- Python 中 必须掌握的 20 个核心函数——open()函数
-
open()是Python中用于文件操作的核心函数,它提供了读写文件的能力,是处理文件输入输出的基础。一、open()的基本用法1.1方法签名...
- python常用的自动化脚本汇总_python 自动脚本
-
以下是python常用的自动化脚本,包括数据、网络、文件、性能等操作。具体内容如下:数据处理工具网络检测工具系统任务自动化工具测试自动化工具文件管理自动化工具性能监控工具日志分析工具邮件...
- Python自动化办公应用学习笔记37—文件读写方法1
-
一、文件读写方法1.读取内容:read(size):读取指定大小的数据,如果不指定size,则读取整个文件。...
- 大叔转行SAP:好好学习,好好工作,做一个幸福的SAP人
-
我是一个崇尚努力的人,坚定认为努力可以改变命运和现状,同时也对自己和未来抱有非常高的期待。随着期待的落空,更对现状滋生不满,结果陷入迷茫。开始比较,发现周围人一个个都比你有钱,而你的事业,永远看不到明...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
Java面试必考问题:什么是乐观锁与悲观锁
-
如何将AI助手接入微信(打开ai手机助手)
-
redission YYDS spring boot redission 使用
-
SparkSQL——DataFrame的创建与使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
-
- 栋察宇宙(二十一):Python 文件操作全解析
- python中12个文件处理高效技巧,不允许你还不知道
- Python内置模块bz2: 对 bzip2压缩算法的支持详解
- Python文件及目录处理方法_python目录下所有文件名
- The West mustn't write China out of WWII any longer
- Python 的网络与互联网访问模块及应用实例(一)
- 高效办公:Python处理excel文件,摆脱无效办公
- Python进阶:文件读写操作详解_python对文件的读写操作方法有哪些
- [827]ScalersTalk成长会Python小组第11周学习笔记
- ScalersTalk 成长会 Python 小组第 9 周学习笔记
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)