【数据蒋堂】第35期:JOIN提速 - 有序归并

我们再来看同维表和主子表的JOIN,这两种情况的优化提速手段是一样的。

设两个关联表的规模(记录数)分别是N和M,则HASH分段技术的计算复杂度(关联字段的比较次数)大概是SUM(NiMi),其中Ni和Mi分别是HASH值为i的两表记录数,满足N=SUM(Ni)和M=SUM(Mi),这大概率会比完全遍历时的复杂度NM要小很多(运气较好的时候会小K倍,K是HASH值的取值范围)。

如果这两个表针对关联键都有序,那么我们就可以使用归并算法来处理关联,这时的复杂度是N+M;在N和M都较大的时候(一般都会远大于K),这个数会远小于刚才那个SUM(Ni*Mi)。归并算法的细节有很多材料介绍,这里就不再赘述了。

但是,外键JOIN时不能使用这个办法,因为事实表上可能有多个要参与关联的外键字段,不可能让同一个事实表同时针对多个字段都有序。

同维表和主子表却可以!

因为同维表和主子表总是针对主键或主键的一部分关联,我们可以事先把这些关联表的数据按其主键排序。排序的成本虽然较高,但是一次性的。一旦完成了排序,以后就可以总是使用归并算法实现JOIN,效率能提高很多。

有序归并的意义还在于大数据的情况。象订单及其明细这种主子表是不断增长的事实表,时间长了常常会积累得非常大。

当要JOIN的两个表都大到内存无法放下的时候,关系数据库仍然是使用HASH分段的技术。根据关联字段的HASH值,将数据分成若干段,每段都足够小到能装入内存再实施内存的HASH分段算法。但这会发生外存倒换的问题,数据需要先分段写出再读入,多出一写一读,外存读本来就不快,写就更慢,这样性能会差出很多。运气不好时,一次HASH分段时可能会发生某段仍然太大而无法装入内存,这时就需要二次HASH,进一步加剧这个问题。而且,HASH分段算法在处理每一段时需要把整段读入内存,为了减少分段数量,就会根据内存大小尽量让分段变大,这样会用光所有内存,有并发运算时就会严重影响其它任务的性能。

归并算法则没有这个问题了,两个表的数据都只要遍历一次就行了,不仅是CPU的计算量减少,外存的IO量也大幅下降。而且,执行归并算法需要的内存很少,只要在内存中为每个表保持数条缓存记录就可以了,几乎不会影响其它并发任务对内存的需求。

SQL采用笛卡尔积定义的JOIN运算不区分JOIN类型,不假定某些JOIN总是针对主键的,就没办法从算法层面上利用这一特点,只能在工程层面进行优化。有些数据库会检查数据表在物理存储上是否针对关联字段有序,如果有序则采用归并算法,但基于无序集合概念的关系数据库不会刻意保证数据的物理有序性,许多操作都会破坏归并算法的实施条件。使用索引可以实现数据的逻辑有序,但物理无序时的遍历效率还是会大打折扣。

有序归并的前提是将数据按主键排序,而这类数据常常会不断追加,原则上每次追加后就要再次排序,而我们知道大数据排序成本通常很高,这是否会导致追加数据难度很大呢?其实,追加数据再加入的过程也是个有序归并,把新增数据单独排序后和已有序的历史数据归并,复杂度是线性的,相当于把所有数据重写一次,而不象常规的大数据排序需要缓存式写出再读入。在工程上做些优化动作还可以做到不必每次都全部重写,进一步提高维护效率。

有序归并的好处还在于易于分段并行。

现代计算机的都有多核CPU,SSD硬盘也有较强的并发能力,使用多进程(或线程)并行计算就能够显著提高性能。但传统的HASH分段技术很难实现并行,多进程做HASH分段时需要同时向某个分段写出数据,造成共享资源冲突;而计算某一段又会几乎耗光所有内存,其它并行任务就无法实施。

使用有序归并实现并行计算时需要把数据分成多段,单个表分段比较简单,但两个关联表分段时必须同步对齐,否则归并时两个表数据错位了,就无法得出正确的计算结果,而数据有序就可以保证高性能的同步对齐分段。

先按主表(同维表则取较大的即可,其它讨论不影响)分段(如何能够较平均地分段且支持数据追加,我们以后会撰文解释),读出每段第一条记录的主键值,然后用这些键值到子表用二分法寻找定位(是否可以执行二分法和数据存储格式相关,后续文章也会谈到),从而获得子表的分段点。这样可以保证主子表的分段是同步对齐的。

因为键值有序,所以主表每段的记录键值都属于某个连续区间,键值在区间外的记录不会在这一段,键值在区间内的记录一定在这一段,子表对应分段的记录键值也有这个特性,所以不会发生错位情况;而同样因为键值有序,才可以在子表中执行高效的二分查找迅速定位出分段点。即数据有序保证了分段的合理性及高效性,这样就可以放心地执行并行算法了。

时间: 2024-09-20 00:19:38

【数据蒋堂】第35期:JOIN提速 - 有序归并的相关文章

数据蒋堂 | JOIN提速 - 有序归并

我们再来看同维表和主子表的JOIN,这两种情况的优化提速手段是一样的. 设两个关联表的规模(记录数)分别是N和M,则HASH分段技术的计算复杂度(关联字段的比较次数)大概是SUM(Ni*Mi),其中Ni和Mi分别是HASH值为i的两表记录数,满足N=SUM(Ni)和M=SUM(Mi),这大概率会比完全遍历时的复杂度N*M要小很多(运气较好的时候会小K倍,K是HASH值的取值范围). 如果这两个表针对关联键都有序,那么我们就可以使用归并算法来处理关联,这时的复杂度是N+M:在N和M都较大的时候(一

开源大数据周刊-第35期

阿里云E-MapReduce实践 如何在E-MapReduce上使用引导操作安装kafka组件 当前E-MapReduce中没有kafka组件,需要额外安装.本文介绍如何用E-MapReduce引导操作来安装kafka_2.10-0.10.0.0版本. 资讯 大数据行业成资本市场新宠 数据源或成大数据公司核心竞争力 日前,大数据发展浪潮正在席卷全球,资本也敏锐追逐着高增长市场.大数据是我国的战略性新兴产业,而投资界持续看好大数据产业的发展未来,这种趋势明显增强. LinkedSee灵犀获5000

【数据蒋堂】第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.*

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

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

数据蒋堂 | 有序分组

我们知道,SQL延用了数学上的无序集合概念,所以SQL的分组并不关注过待分组集合中成员的次序.我们在前面讨论过的等值分组和非等值分组,也都没有关注过这个问题,分组规则都是建立在本身的成员取值本身上.但如果我们要拓展SQL,以有序集合为考虑对象时,那就必须考虑成员次序对分组的影响了,而且,现实业务中有大量的有序分组应用场景. 一个简单的例子:将一个班的学生平均分成三份(假定人数能被3整除).按我们在前面所说的分组定义,这也可以看成是一种分组,但这个运算在SQL中却很难写出来,因为分组依据和成员取值

【数据蒋堂】第27期:非常规聚合

标准SQL中提供了五种最常用的聚合运算:SUM/COUNT/AVG/MIN/MAX.观察这几个运算,我们发现它们都可以看成是一个以集合为参数返回单值的函数,我们就先把这个共同点理解为聚合运算的定义,把集合变成单值,多个值变成一个值,也就是发生了"聚合",所以叫聚合运算. 那么很显然,有集合的时候就可以应用聚合运算了,所以SUM/COUNT这些运算可以针对一个数据表(记录集合)实施. 分组运算的结果是一批分组子集,那么每个子集上也可以应用聚合运算,这也就是SQL的分组运算了.其实针对全集

【数据蒋堂】第13期:怎样看待存储过程的移植困难

存储过程移植困难是经常被诟病的,在罗列存储过程的缺点时,这一条几乎从来不会被遗漏. 存储过程的移植确实很困难,一般业务逻辑复杂到需要写存储过程的地步,总会不可避免地用到数据库独有的特性和语法,更换数据库时这部分代码就需要重写.如果只是简单地替换函数名和参数规则(如日期转换等),那成本还不高:如果用到了新数据库不支持的某种特性(如窗口函数),那还要重新设计算法来编写计算逻辑:如果还要再兼顾性能因素,有时候就会是个不可能完成的任务了. 不过,还好,存储过程移植的情况并不频繁. 多年前数据库市场还处于

数据蒋堂 | JOIN运算剖析

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