从 TPCH 测试学习性能优化技巧之 Q2

一、     查询要求

Q2语句查询获得最小代价的供货商。得到给定的区域内,对于指定的零件(某一类型和大小的零件),哪个供应商能以最低的价格供应它,就可以选择哪个供应商来订货。

Q2语句的特点是:带有排序、聚集操作、子查询并存的多表查询操作。查询语句没有从语法上限制返回多少条元组,TPC-H标准规定,查询结果只返回前100行即可(通常依赖于应用程序实现)。


二、     Oracle执行

Oracle编写的查询SQL语句如下:

select * from (

         select   /*+ parallel(n) */

                   s_acctbal,s_name,n_name,p_partkey,p_mfgr,s_address,s_phone,s_comment

         from part,supplier,partsupp,nation,region

         where

                   p_partkey = ps_partkey

                   and s_suppkey = ps_suppkey

                   and p_size = 25

                   and p_type like '%COPPER'

                   and s_nationkey = n_nationkey

                   and n_regionkey = r_regionkey

                   and r_name = 'ASIA'

                   and ps_supplycost = (

                            select

                                     min(ps_supplycost)

                            from

                                     partsupp,

                                     supplier,

                                     nation,

                                     region

                            where

                                     p_partkey = ps_partkey

                                     and s_suppkey = ps_suppkey

                                     and s_nationkey = n_nationkey

                                     and n_regionkey = r_regionkey

                                     and r_name = 'ASIA'

                   )

         order by

                   s_acctbal desc,n_name,s_name,p_partkey

)

where rownum <= 100;

其中/*+ parallel(n) */ 是Oracle的并行查询语法,n是并行数。

脚本执行时间,单位:秒

并行数124812

Oracle5635231627


三、     SPL优化

仔细分析这句SQL,如果把子查询

                            select

                                     *

                            from

                                     part,

                                     partsupp,

                                     supplier,

                                     nation,

                                     region

                            where

                                     p_partkey = ps_partkey

                                     and s_suppkey = ps_suppkey

                                     and s_nationkey = n_nationkey

                                     and n_regionkey = r_regionkey

                                     and r_name = 'ASIA'

                                     and p_size = 25

                                      and p_type like '%COPPER'

看成是某个视图V,原来查询主体可以改写成:

         select   /*+ parallel(n) */

                   s_acctbal,s_name,n_name,p_partkey,p_mfgr,s_address,s_phone,s_comment

         from V

         where

                   ps_supplycost = (

                            select

                                     min(ps_supplycost)

                            from

                                     V V1

                            where

                                     V.p_partkey = V1.p_partkey

                   )

这样将原查询变成一个单表查询,相当于找出V中这样一些记录,使得这些记录的ps_supplycost值在所有与该记录的partkey值相同的记录中取值最小。这个运算的本质是对V按partkey分组后对每组聚合,计算出每组中ps_supplycost最小的那条记录。但是,SQL不支持这种聚合运算,于是只能写成子查询的情况(即使转换成单表运算后)。

如果数据库优化引擎不好,严格按这个子查询描述的方法去遍历计算,就会导致N*N的复杂度(N是V的记录数);即使数据库优化引擎较好,也需要先对V按partkey分组求ps_supplycost的最小值后做形成中间结果集再做索引,然后再次遍历V,计算量也不少。


解决这个问题更好的办法就是支持这种返回记录本身的聚合计算,一次分组聚合即可完成运算。SPL有集合和引用数据类型,也支持聚合出最小值所在记录的聚合运算,可以实现这个想法,整体复杂度就会低很多。

SPL脚本如下:

A

1=1

2>size=25

3>type="*COPPER"

4>name="ASIA"

5=now()

6=file(path+"region.ctx").create().cursor().select(R_NAME==name).fetch()

7=file(path+"nation.ctx").create().cursor().switch@i(N_REGIONKEY,   A6:R_REGIONKEY).fetch().keys@i(N_NATIONKEY)

8=file(path+"part.ctx").create().cursor@m(P_PARTKEY,P_MFGR;P_SIZE==size   && like(P_TYPE,type);A1).fetch().keys@i(P_PARTKEY)

9=file(path+"supplier.ctx").create().cursor@m(S_SUPPKEY,S_NAME,S_ADDRESS,S_NATIONKEY,S_PHONE,S_ACCTBAL,S_COMMENT;S_NATIONKEY:A7;A1)

10=A9.fetch().keys@i(S_SUPPKEY)

11=file(path+"partsupp.ctx").create().cursor@m(PS_PARTKEY,PS_SUPPKEY,PS_SUPPLYCOST;PS_PARTKEY:A8,PS_SUPPKEY:A10;A1)

12=A11.groups(PS_PARTKEY;top(1;PS_SUPPLYCOST):rs).conj(rs)

13=A12.new(PS_SUPPKEY.S_ACCTBAL,PS_SUPPKEY.S_NAME,PS_SUPPKEY.S_NATIONKEY.N_NAME,PS_PARTKEY.P_PARTKEY,PS_PARTKEY.P_MFGR,PS_SUPPKEY.S_ADDRESS,PS_SUPPKEY.S_PHONE,PS_SUPPKEY.S_COMMENT)

14=A13.sort(S_ACCTBAL:-1,N_NAME,S_NAME,P_PARTKEY).to(100)

15=now()

16=interval@s(A5,A15)

需要解释的是,SPL相对于SQL更底层一些,SPL不象SQL有元数据概念,也没有系统级的表概念,数据访问直接从文件开始读取数据,这时前面准备数据的代码就显得稍微冗长一点。实际应用中可以通过使用SPL预定义全程变量或虚表语法来简化,达到类似SQL直接使用数据表的效果。但这不是本篇的重点,而且为了让读者更方便地看出数据的原始流向,这里就采用了直接文件访问的语法。


代码中A6-A11用于定义上述视图V的游标,A12中用groups内的top函数实现分组的同时聚合出最小值所在记录(而不是最小值本身)。

A7中的switch@i函数将把外键不能匹配的记录过滤掉,同时将能匹配的关联字段转换成外键表记录的指针,这样在后面可以直接用.的形式访问外键表的字段。SPL看待JOIN运算的思路和SQL不一样,如果数据能事先加载进内存,SPL以利用预关联提高运算性能。不过,本系列例子均假定从外存取数计算,本问题中SPL和SQL在JOIN运算性能也没有算法上的区别,只是写法不同。详细解释可参考SPL教案中关于JOIN的部分。

另外,在A8中也使用了Q1中提到的在游标建立时使用过滤条件的技巧。A9和A11中将这个技巧与前面的switch@i方法结合起来(第2组参数),在游标建立时做外键匹配,不能匹配者直接过滤掉,不再读取其它字段且不再生成该记录,能匹配时则将关联字段转换成指针。


脚本执行时间,单位:秒

并行数124812

Oracle5635231627

SPL组表2014854

这个问题的数据量不大,几次执行后,操作系统可以把数据都缓存进内存,列存在这里不是重点,带来的访问量优势可以忽略,性能优势主要是算法优化带来的。

从表中还能看出,这种分组聚合的并行效果也较好。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容