基于Spark系统的查询分析及优化研究

研究背景

  • 在Spark大数据平台中,等值连接作为其数据分析以及处理中最常用、代价最高的操作之一,对于Spark大数据处理平台的数据处理及分析性能有着很大影响。在Spark系统上目前被广泛采用的Broadcast join和Hash join在对包含较少数据量的数据表进行操作时,有良好的性能,但事实上,在如今大数据时代,数据量往往都是非常大的,且呈现数量级的增长,Spark提供的等值连接方法在大表连接上存在性能不佳的问题,该问题会导致整个Spark大数据平台的性能变差。如何高效处理大笔间的等值连接操作是Spark平台当前的重要挑战

  • Broadcast Join缺陷

    Broadcast Join原理是将连接中的一张数据表广播到Spark集群的所有节点上,再去执行Join操作。这就存在一个很大的问题,当两个连接的数据表都很大时,再将一张表进行广播开销会很大,导致Join操作性能较差

  • Hash Join的缺陷

    在Hash Join方式中,以数据表的连接属性值为Key,其他属性值为Value,生成Key/Value对,然后调用Spark Core中的Join方法,对RDD中的Key进行连接操作。主要原理就是根据Key的哈希值对RDD进行重分区,将哈希值相同的Key分到同一个分区中,然后进行连接操作。

    Hash Join方式能够适应大部分等值连接操作,但是也有一些缺陷,其中最重要的一点是需要将所有的Key/Value根据Key值进行重分区,这一步涉及到Shuffle操作,并且Shuffle阶段的数据量较大,再传送到Reduce中。在整个运行过程中会产生网络通信开销和磁盘IO开销。当Shuffle阶段的数据量很大时,会直接影响到整个系统的表间连接操作的性能。

  • 如何在Spark计算框架中对Shuffle阶段前的大数据表做高效的预处理操作,从而更高效地实现Join操作。

  • 研究内容与意义

    • 当前,Spark系统中对大表之间的关联操作不是非常理想。Hash Join能较好地处理大表之间的关联操作,但大表中的数据量很大时,会导致其Shuffle阶段的数据量很大

相关技术简介

  • SQL查询优化概念和技术

    • SQL语句是我们对数据进行操作的一个最直接重要的方法。我们在使用时往往过于关注正确与否,而忽视了不同实现方法之间可能存在的性能差异。
    • 在如今的大数据时代,这种性能差异会导致整个大数据平台对数据的处理能力大打折扣,所以对SQL语句的查询优化的分析是提升大数据平台对数据处理能力的关键所在。
    • 每个查询都会有许多的执行策略,而查询优化就是在这些执行策略中选择一个可高效执行的查询处理策略。查询优化按照优化的层次可分为代数优化和物理优化。
    • 代数优化是按照一定的规则,把代数表达式中的操作次序和组合进行一定的改变,从而使查询执行更高效;物理优化则是指存储路径和底层操作算法的选择,选择的依据可以是基于代价的、基于规则的、以及基于语义的
    • 在查询处理中,连接操作是最耗时的操作之一,也是执行效率较多的操作之一,所以对于等值连接的优化操作是查询优化中的重要内容。
  • Spark Join例子
    在这里插入图片描述

    这里有两个简单的RDD实例,命名为rdd1和rdd2,在两个rdd进行join的过程中,会产生Shuffle操作,显然在这里,rdd1中的(3,‘d’)和(8,‘b’)参与了Shuffle操作,但在rdd2中并没有,同样的在rdd2中的(5,‘d’)和(6,‘c’)也参与了Shuffle,但在rdd1中也没有key为5和6的数据对,因此Spark Join操作的Shuffle阶段会包含一些不符合Join条件的数据,从而导致了Shuffle操作产生了大量不必要的网络通信和磁盘开销,这必将导致Spark Join操作运行效率大幅度的降低。

Bloom Filter

  • Bloom Filter是一种空间效率很高的随机数据结构,其使用位数组很简洁地表示一个集合,并能对一个元素进行判断是否属于该集合。

    • 但是Bloom Filter也存在一定的缺陷,即在判断一个元素是否属于这个集合时,有一定的可能性会把不属于这个集合的元素误认为属于这个集合。

    • 由于这个缺陷导致Bloom Filter不适合那些零错误的应用场合。在接受低错误率的应用场合下,Bloom Filter通过其低错误率换来了存储空间的极大节省

  • 实例

为了说明Bloom Filter存在的重要意义,举一个实例:

假设要你写一个网络蜘蛛(web crawler)。由于网络间的链接错综复杂,蜘蛛在网络间爬行很可能会形成“环”。为了避免形成“环”,就需要知道蜘蛛已经访问过那些URL。给一个URL,怎样知道蜘蛛是否已经访问过呢?稍微想想,就会有如下几种方案:

1. 将访问过的URL保存到数据库。

2. 用HashSet将访问过的URL保存起来。那只需接近O(1)的代价就可以查到一个URL是否被访问过了。

3. URL经过MD5或SHA-1等单向哈希后再保存到HashSet或数据库。

4. Bit-Map方法。建立一个BitSet,将每个URL经过一个哈希函数映射到某一位。

方法1~3都是将访问过的URL完整保存,方法4则只标记URL的一个映射位。

以上方法在数据量较小的情况下都能完美解决问题,但是当数据量变得非常庞大时问题就来了。

方法1的缺点:数据量变得非常庞大后关系型数据库查询的效率会变得很低。而且每来一个URL就启动一次数据库查询是不是太小题大做了?

方法2的缺点:太消耗内存。随着URL的增多,占用的内存会越来越多。就算只有1亿个URL,每个URL只算50个字符,就需要5GB内存。

方法3:由于字符串经过MD5处理后的信息摘要长度只有128Bit,SHA-1处理后也只有160Bit,因此方法3比方法2节省了好几倍的内存。

方法4消耗内存是相对较少的,但缺点是单一哈希函数发生冲突的概率太高。还记得数据结构课上学过的Hash表冲突的各种解决方法么?若要降低冲突发生的概率到1%,就要将BitSet的长度设置为URL个数的100倍。

实质上上面的算法都忽略了一个重要的隐含条件:允许小概率的出错,不一定要100%准确!也就是说少量url实际上没有没网络蜘蛛访问,而将它们错判为已访问的代价是很小的——大不了少抓几个网页呗。

  • Bloom Filter的算法

    其实上面方法4的思想已经很接近Bloom Filter了。方法四的致命缺点是冲突概率高,为了降低冲突的概念,Bloom Filter使用了多个哈希函数,而不是一个。

  • 算法过程

    • Bloom Filter。其实上面方法4的思想已经很接近Bloom Filter了。方法四的致命缺点是冲突概率高,为了降低冲突的概念,Bloom Filter使用了多个哈希函数,而不是一个。

    • 创建一个m位BitSet,先将所有位初始化为0,然后选择k个不同的哈希函数。第i个哈希函数对字符串str哈希的结果记为h(i,str),且h(i,str)的范围是0到m-1 。

    • 下面是每个字符串处理的过程,首先是将字符串str“记录”到BitSet中的过程:

      对于字符串str,分别计算h(1,str),h(2,str)…… h(k,str)。然后将BitSet的第h(1,str)、h(2,str)…… h(k,str)位设为1。

在这里插入图片描述

这样就将字符串str映射到BitSet的k个二进制位了
  • 检查字符串是否存在

    • 对于字符串str,分别计算h(1,str),h(2,str)…… h(k,str)。然后检查BitSet的第h(1,str)、h(2,str)…… h(k,str)位是否为1,若其中任何一位不为1则可以判定str一定没有被记录过。若全部位都是1,则“认为”字符串str存在。
    • 若一个字符串对应的Bit不全为1,则可以肯定该字符串一定没有被Bloom Filter记录过。(这是显然的,因为字符串被记录过,其对应的二进制位肯定全部被设为1了)
    • 但是若一个字符串对应的Bit全为1,实际上是不能100%的肯定该字符串被Bloom Filter记录过的。(因为有可能该字符串的所有位都刚好是被其他字符串所对应)这种将该字符串划分错的情况,称为false positive 。
  • Bloom Filter参数选择

    • 哈希函数选择

      哈希函数的选择对性能的影响应该是很大的,一个好的哈希函数要能近似等概率的将字符串映射到各个Bit。选择k个不同的哈希函数比较麻烦,一种简单的方法是选择一个哈希函数,然后送入k个不同的参数。

    • Bit数组大小选择

      哈希函数个数k、位数组大小m、加入的字符串数量n的关系可以参考参考文献1。该文献证明了对于给定的m、n,当 k = ln(2)* m/n 时出错的概率是最小的。

      同时该文献还给出特定的k,m,n的出错概率。例如:根据参考文献1,哈希函数个数k取10,位数组大小m设为字符串个数n的20倍时,false positive发生的概率是0.0000889

基于Partial Bloom Filter的SPark大表间关联优化

  • 问题:实际上,参与Shuffle操作的Key/Value数据中存在很大一部分不满足连接条件的,完全没有必要进行Shuffle操作。

  • PBF Join算法

    其主要思想是利用PBF数据结构预先过滤掉大表中大部分不符合过滤条件的Key/Value数据对,经过滤操作后有Map端输出并参与Shuffle操作的无用数据量将会得到大量的减少,进而提升等值连接操作的执行效率

  • PBF是一种具有很好空间和时间效率的数据结构,是Bloom Filter的一种优化形式,被用来检测某个元素是否属于一个集合。如果检测结果为是,该元素很有可能在集合中,但并不保证一定在集合中,但如果检测结果为否,该元素一定不在集合中,该元素可被过滤

  • PBF和Bloom Filter的区别

    Partial Bloom Filter和标准的Bloom FIlter的不同点在于哈希函数的映射范围。

    • 在PBF数据结构中,位数组被分为k个区域,每个哈希函数只能映射到其中的一个区域,也就是说每个哈希函数映射范围都是{0,…,m/(k-1)},但是互不重叠,各个哈希函数负责各自的区域。

    • 在大数据应用中,Partial Bloom Filter比标准的Bloom Filter的优势在于,一旦哈希函数的映射范围独立开来,k个哈希函数就可以并行访问位数组,从而提高程序性能。

    • 但与Bloom Filter相同的是,PBF同样存在一定的误判率,对于一个有n条记录的数据集,当位数组大小为m,哈希函数个数为k时,Bloom Filter的误判率P1可以计算如下:

      P 1 = 1 − ( 1 − 1 / m k n ) k ≈ ( 1 − e k n / m ) k P1 = 1-(1-1/m^{kn})^k \approx (1-e^{kn}/m)^k P1=111/mkn)k(1ekn/m)k
      对于相同的情况下时,Partial Bloom Filter位数组中的某一位只可能被一个哈希函数选中,选中的概率为 k / m k/m k/m,所以其不被选中的概率为 ( 1 − k / m ) (1-k/m) (1k/m)。假设集合有n个元素,那么在存储整个集合后还保持为0的概率为 ( 1 − k / m ) n (1-k/m)n (1k/m)n,所以可得Patrial Bloom Filter的误判率P2可以计算如下:

      P 2 = 1 − ( 1 − k m n ) ≈ 1 − ( 1 − 1 m k n ) k ≈ ( 1 − e k n m ) k P2=1-(1-\frac k{m^n})\approx1-(1-\frac 1{m^{kn}})^k\approx (1-e^{\frac {kn}m})^k P2=1(1mnk)1(1mkn1)k(1emkn)k

      由上两个错误率公式,可得,Patrial Bloom Filter和标准的Bloom Filter的错误率很近似,对于Shuffle阶段来说,错误率是可以被接受的

  • 算法过程

    • 第一阶段

      首先分别对两张大数据表抽取连接属性,然后将连接属性去重,再对去重后的连接属性进行Partial Bloom Filter压缩处理,然后得到两张大表对应的两个位数组,对这两个位数组做与运算,生成最终的Partial Bloom Filter位数组,使用这个最终生成的PBF数组,就可以对待连接的两张大数据表进行过滤操作。

    在这里插入图片描述

    • 第二阶段

      将利用位数组进行过滤后得到的新的大数据表进行Hash Join操作,生成最终的大数据表等值连接结果。

  • PBF 算法实现

    • 位数组生成

      1. 提取TableA和TableB的连接属性,生成两个新的RDD,分别是FactKeyA和FactKeyB
      2. 对FactKeyA和FactKeyB进行去重操作,得到FactKeyA和FactKeyB的无重集
      3. 对去重后的FactKeyA和FactKeyB进行Partial Bloom Filter压缩处理,生成位数组PBF A和PBF B
      4. 对PBF A和PBF B进行与运算,得到最终的PBF数组,并将其广播到集群上的所有节点。

在这里插入图片描述

  • 操作详细描述

    • 从数据表中抽取连接属性生成FactKeyA,可以使用RDD的Map操作来实现,Map操作将RDD中类型为T的元素,一对一地映射为类型为U的元素。在本文提出的算法中,T类型为数据表的数据行,U类型为数据表的连接属性

    • 在对FactKeyA执行去重的操作中,可以使用Spark自身提供的Distinct操作,Distinct去重能对RDD进行完整的去重,但是需要Shuffle操作和Reduce操作。

      文献[15]的研究中使用一种分区级别的去重,该文献中使用的去重操作在各个分区内进行去重,不会产生Shuffle操作和Reduce操作,但是这种去重操作不能实现完全去重。

      当RDD中数据重复率较高时,Distinct操作的Shuffle操作数据量通常不会很大。如Sogou用户搜索日志数据的重复率通常较高,对大小为3GB的Sogou用户搜索日志数据进行Distinct操作,Shuffle阶段数据量约为100KB~1MB,在这种情况下,分区级别去重的性能提升不是很明显。而且在本文提出的算法中,分区级别去重不能完全去重,因此导致Partical Bloom Filter过滤阶段的数据量较大,增加计算成本。综合考虑,在本文提出的优化算法中,采用Spark自身提供的Distinct进行去重操作

    • 最核心生成阶段就是对去重后的FactKey A和FactKey B生成位数字的过程。在Spark中,一个RDD包含多个分区,FactKey A和FactKey B也是如此。在生成位数组之前,需要选取K个独立的哈希函数,再将得到的PBF A等分成K个区域,使得每个哈希函数映射到有且仅有其中一个区域,也就是说,每个哈希函数的映射范围都是{0,1,…,m/(k-1)},但是每个哈希函数互不重叠,大家各自负责各自的区域。也因为K个哈希函数的映射范围相互独立,所以K个哈希函数可以实现并行访问数组,对于大数据表间的等值连接操作性能具有较好的提高

      一个哈希函数映射到一个分区的位数字过程如下:

      1. 为FactKey A的每个分区创建一个Mbit数组
      2. 将每个分区的位数组再均分为K个区域,使每个哈希函数负责一个区域
      3. 依次读取每个分区中的记录,用d表示,分别计算该记录的K个独立的Hash值H1(d),H2(d),H3(d)…Hk(d),并将每条记录得到的K个Hash值对M/K取余,得到K个独立区域所对应的0~m-1/k的数值H‘1(d),H’2(d),H‘3(d)…H’k(d)
      4. 将计算记录得到的Hash值对应到位数组的相应位置上且把该位置设为1(若一个位置被多次设为1,则只有第一次起作用)。得到FactKeyA的一个分区的位数组
      5. 假设FactKeyA共有m个分区,那么总共会生成m,个位数字,PBF A(1),PBF A(2),PBF A(3)…PBF A(m),对所有得到的m个位数字执行或运算后,就得到整个数据表的位数字PBF A。同理,PBF B也是这样生成的

      在Spark中,可以使用RDD的aggregate操作完成上述步骤

      1. aggregate操作需要提供两个函数,一个是seqOp函数,其将RDD中的每一个分区的数据聚合成U类型的值

      2. 另一个函数为combOp函数,其将各个分区聚合起来的值合并在一起得到最终类型为U的返回值。

      3. 在本文中,seqOp函数对单独分区中的元素进行Partial Bloom Filter压缩处理,生成一个位数组,combOp函数对每个分区生成的位数组执行或操作。

在这里插入图片描述

  • PBF A和PBF B分别是数据表A和数据表B的连接属性经过PBF算法压缩处理后生成的位数组。在数据表过滤时,需要过滤掉不满足连接条件的数据对,即两个数据不相同的数据对。由于对两个数据表使用了相同的Hash函数进行PBF压缩处理,所以相同的Key在位数组中有相同的表示。因此对位数组PBF A和PBF B执行与运算就可以得到最终的PBF位数组。

    • 为了加速后续过滤阶段对PBF位数组的读取,本算法是在过滤操作前提前将PBF位数组广播到集群上的所有节点,由于PBF位数组的大小不大,对其进行广播操作的网络通信开销是完全可以接受的
  • 过滤与连接

    • 过滤操作是使用最终生成的PBF数组,对待连接的两张数据表进行过滤操作,过滤掉两张数据表中大部分不符合连接条件的记录,从而得到JoinKeyA和JoinKeyB

      进行过滤时,需要使用之前生成PBF位数组的K个相互独立的哈希函数,对每条记录的连接属性分别计算出每个哈希函数对应的K个区域内的K个Hash值,然后对M取模,得到K个0~M-1/K的值,最后检查PBF位数组中,这K个位置的值是否都为1,若不全为1,则该记录就是无用记录,将该记录进行过滤

      此过滤操作可以使用RDD的filter’操作,只有当RDD中的元素经过func计算后返回值为true,该元素才会保留

    • 等值连接操作是最后一步,使用Spark自身提供的Hash Join操作即可

算法理论分析

符号描述
aTableA的记录数
bTableB的记录数
c一条记录的大小
m位数组的大小
H a H_a HaTableA中符合连接条件的记录占总记录的百分比
H b H_b HbTableB中符合连接条件的记录占总记录的百分比
DDistinct操作产生的Shuffle数据量
PPartical Bloom Filter数据结构的误判率
NSpark集群的计算节点数目
  • Hash Join操作方式

    Hash Join操作方式直接对原数据表进行Join操作,对数据表的重分区操作中会有Shuffle操作,因而产生网络通信开销。这一部分的网络通信开销与数据表的数据量有关,大小为:

    O ( a ∗ c + b ∗ c ) O(a*c+b*c) O(ac+bc)

    Spark采用成本较低的Shffle机制,因此通常要比 a ∗ c + b ∗ c a*c+b*c ac+bc小很多,网络通信开销约为数据集大小的5%

  • 基于PBF算法的大表等值连接优化

    PBF Join算法的总的开销分为两个部分

    • Distinct操作产生的开销:这部分的开销与选取的数据集重复率有关,如果重复率很高,该阶段的网络通信开销较小,可以忽略不计

    • 位数组合并阶段产生的开销:需要将K个Mbit的位数组由Spark平台的Excutor节点经过网络传输到Diver节点,因此网络通信开销为 K ∗ M b i t K*Mbit KMbit。位数组生成后,需要将其广播到所有的N个计算节点上,所以位数组广播阶段的网络通信开销为 N ∗ M b i t s N*Mbits NMbits。可得第一阶段网络通信开销的总和为 2 ∗ K ∗ M + N ∗ M 2*K*M+N*M 2KM+NM。第二个网络通信开销是过滤后的数据表JoinKeyA和JoinKeyB执行Hash Join操作时Shuffle操作产生的总开销量,这是整个算法中开销最大的一部分。Shuffle操作阶段所包含的数据有两类,第一类是符合连接条件的数据,第二类为不符合连接条件的数据但被PBF误判而进入Shuffle的无用数据。

      • 符合连接条件的数据大小为:

        a ∗ c ∗ H a + b ∗ c ∗ H b a*c*H_a+b*c*H_b acHa+bcHb

      • 不符合连接条件但被误判进入Shuffle阶段的无用数据大小为:

        a ∗ c ∗ P ∗ ( 1 − H a ) + b ∗ c ∗ P ∗ ( 1 − H b ) a*c*P*(1-H_a)+b*c*P*(1-H_b) acP(1Ha)+bcP(1Hb)

      • Shuffle阶段网络通信开销的总和是两数据的开销总和,为:

        O ( a ∗ c ∗ ( H a + P ∗ ( 1 − H a ) ) + b ∗ c ∗ ( H b + P ∗ ( 1 − H b ) ) ) O(a*c*(H_a+P*(1-H_a))+b*c*(H_b+P*(1-H_b))) O(ac(Ha+P(1Ha))+bc(Hb+P(1Hb)))

      • 由两个阶段的网络通信开销可得总的网络通信开销为

        2 ∗ K ∗ M + N ∗ M + O ( a ∗ c ∗ ( H a + P ∗ ( 1 − H a ) ) + b ∗ c ∗ ( H b + P ∗ ( 1 − H b ) ) ) 2*K*M+N*M+O(a*c*(H_a+P*(1-H_a))+b*c*(H_b+P*(1-H_b))) 2KM+NM+O(ac(Ha+P(1Ha))+bc(Hb+P(1Hb)))

    PBF数据结构的特点是具有较小的空间代价,所以位数组生成及广播过程中的网络通信开销相对较小,即 2 ∗ K ∗ M + N ∗ M 2*K*M+N*M 2KM+NM较小。

    在使用过程中,误判率P通常会根据实际需要来设置,但一般都比较小

    而对于 H a H_a Ha H b H_b Hb,由于数据集中符合连接条件的记录通常较少,因此也不会很大

  • 对比分析这两种连接方式的网络开销,可以发现,Hash Join操作的网络通信开销与元数据的大小有关。PBF Join的网络通信开销代价中,D(Distinct操作产生的开销)和 2 ∗ K ∗ M 2*K*M 2KM都比较小,PBF的误判率也比较小,所以对PBF Join网络通信开销影响较大的是 H a H_a Ha H b H_b Hb两个参数,当数据表中符合连接条件的记录非常多时,效果不太明显。此时PBF第二阶段的Hash Join操作就和原数据表直接进行Hash Join的网络通信开销相当,性能提升不明显或是由于第一阶段的网络通信开销,反而出现性能降低的情况

    而当连接条件的记录占比不是很高时,PBF算法相对于原Hash Join有较大的性能提升

应用场景分析

  • Hash Join是对数据表进行以连接属性为Key的哈希值重分区,这一部分会产生Shuffle操作,不能对原数据表中不符合连接条件的记录进行过滤操作,因此Shuffle阶段会有大量不符合条件的Key/Value数据对参与该阶段的操作,导致整个Join操作的效率较低,性能较差。
    • Hash Join操作适用于数据表中的数据对基本符合连接条件的应用场景,在实际应用中,这种场景一般较少,所以Hash Join有其不足之处
  • PBF Join首先对数据表进行过滤,将大部分不符合连接条件的记录在Shuffle前就先过滤掉了,再通过过滤后的基本符合连接条件的数据表进行Hash Join操作。
    • PBF Join连接方式适用于原数据表存在大量数据记录并且其中有一定数据量的记录不符合连接条件的应用场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值