zoukankan      html  css  js  c++  java
  • [原创] Parallel Query & Bitmap

        Degree of Parallelism(并行度)
        一个查询使用并行来处理时,SQL Server为该查询分配多个线程,每个线程使用一个CPU进行操作。Degree of Parallelism就是SQL Server为并行查询分配的线程数量,也表明这个并行查询将使用多少个CPU进行并行处理。
     
        Exchange Oprators(交换操作)
        查询语句的执行计划中,通常是并行操作和串行操作结合在一起。并行操作要求将输入数据流(data stream)切分成多个(即degree of parallelism)部分,分别分配给各个线程进行并行处理。并行查询包括几个数据流的交换操作(exchange operator),用以管理并行计划的执行。
        Distribute Steams
        执行计划中显示为Parallelism/Distribute Steams。通常情况下,如果在一个串行操作之后紧接着一个并行操作,则这个并行操作将从前一个串行操作接收一个input stream。Distribute Steams就是并行查询中将单个input stream分发到多个output stream中的操作。
        例如一个serial table scan产生一个4000记录的output stream,假设在这个table scan之后是一个并行操作,则在这两个操作之间必须需要一个Distribute Steams操作,向并行操作的各个线程分发input stream。如果degree of parallelism为4,SQL Server根据关键字段,将这个4000记录的stream分发成4个大致相当的stream,分别作为4个线程的input。
        Distribute Streams操作之后,每一条input stream中的记录,将出现在某一个output stream中,记录的内容和格式不会发生任何变化。SQL Server自动在output stream中保留input stream中各记录的相对顺序。
        通常,将使用Hashing以确定每一条输入记录被分发到哪一个output stream中。
        Gather Streams
        执行计划中显示为Parallelism/Gather Streams。在并行操作结束之后,输出结果保留在多个stream中。Gather Streams将并行操作的多个output stream收集到单个stream中。
        Gather Streams对多个stream进行合并过程中,记录的内容和格式不会发生任何变化。如果各个input stream都是排序的,则Gather Streams的output stream也是排序的。
        Redistribute Streams/Repartition Streams
        执行计划中显示为Parallelism/Repartition Streams。在一系列的并行操作中,在某个并行操作之后,可能需要对各个stream进行重新组合,才能够进入下一个并行操作。
        例如Merge Join、Hash Join,都需要两个input。这些操作的并行计划中,每个并行的线程都应当拥有两个独立的input,各自执行Merge Join、Hash Join操作,在并行Merge/Hash Join之后,使用Gather Streams操作,就得到完整的Merge/Hash Join的结果。如果在Merge/Hash Join前是一系列的并行计划,则在进入Merge/Hash Join时,各个并行线程拥有的stream可能不满足"独立的input"这个条件,此时就需要对进入Merge/Hash Join的各个stream进行重组,以使并行的各个线程独立的完成Merge/Hash Join中的一部分。
        每一条input stream中的记录,在Repartition Streams操作之后将出现在其中一个output stream中,记录的内容和格式不会发生任何变化。如果各个input stream都是排序的,则Repartition Streams的各个output stream也是排序的。
     
        示例
        示例中要使用到2个table:
        TblBuyerItem(
            UserID NVARCHAR(40) NOT NULL,
            OrgID NVARCHAR(40) NOT NULL,
            ItemID NVARCHAR(40) NOT NULL)
        这个表的Clustered Index:OrgID,ItemID,记录数为150万。
        #alert_asn_shipoverdue(
            ORG NVARCHAR(40) NOT NULL,
            ITEM NVARCHAR(40) NOT NULL,
            VENDOR NVARCHAR(40) NOT NULL)
        这是个临时表,没有PK,没有任何index,记录数150。
        执行的SQL如下(没有使用到TblBuyerItem的Clustered Index):
        SELECT a.UserID
        FROM TblBuyerItem a
        INNER JOIN #alert_asn_shipoverdue b ON b.ORG=a.OrgID AND b.ITEM=a.ItemID
        OPTION(MERGE JOIN)
     
        执行计划如下图(裁减掉了个别步骤,点击可以放大查看):
        第一部分:
       
        第二部分:
       

        执行计划中,图标右下脚有个黄色小圆圈,里面有三个并行小箭头,则表明这个操作是并行执行的。
        并行处理中的stream数据流如下图所示:
       
         备注: 原图丢了,从MSDN找的这张图替代一下

        上图示例degree of parallelism为3时的执行情况。执行过程介绍如下:
        先在#alert_asn_shipoverdue上执行table scan,然后根据key column ORG、ITEM的值,为并行的Merge Join操作将#alert_asn_shipoverdue的数据分发到三个stream中,假设编号分别为B1、B2、B3。三个线程各自对所获得的stream创建bitmap,然后进行排序。
        接下来,三个线程并行的在TblBuyerItem上执行Clustered Index Scan。操作完成后每个线程获得一个output stream,假设编号分别为A1'、A2'、A3'。现在A1'、A2'、A3'还不能与B1 、B2、B3匹配成A1'-B1、A2'-B2、A3'-B3,独立的进行Merge Join操作,因此根据key column OrgID、ItemID的值,对A1'、A2'、A3'执行Repartition Streams操作,为接下来的Merge Join重组A1'、A2'、A3',并且使用bitmap过滤记录。假设重组后的stream分别编号为A1、A2、A3,此时,三个线程分别使用A1 -B1、A2-B2、A3-B3作为输入,执行Merge Join操作。在Merge Join操作前,两个input都必须是经过排序的,因此每一个stream在进入Merge Join操作前都有一个Sort操作。
        最后,Gather Streams操作从三个线程的output stream中收集合并记录集,得到完整的Merge Join结果记录集。
     
        Parallel query并不能节约内存、CPU的资源开销,因为将stream进行Distribute、Repartition、Gather都需要消耗更多的资源。但是Parallel query也许可以节约query的执行时间。
       
        Bitmap(位图)
        在上面的示例中,可以看到Bitmap/Bitmap Create的操作。
        在MANY-TO-MANY的Join中,两个表可能存在大量不匹配的记录。在上面的示例中,#alert_asn_shipoverdue的记录只有150,而TblBuyerItem的记录是1,500,000,如果直接对两个表进行 Merge Join,就必须为TblBuyerItem的1,500,000记录进行排序,这是一个成本非常高的操作。而在1,500,000记录中,只有少量记录符合匹配条件。Bitmap操作在Join操作前,快速的对数据进行一次初步的过滤,减少Join操作的开销。
        Bitmap in Merge Join
        仍以上面的示例来说明,在Parallelism/Distribute Streams操作之后,每个线程得到一个input stream,接下来各个线程为自己的stream创建bitmap。我们假设分配给线程1的stream编号为B1。Bitmap创建完成后,包含一系列0、1的值,为1的位代表对应于这一位,该stream中存在相应的记录;为0的位代表对应于这一位,该stream中不存在相应的记录。在 Parallelism/Repartition Streams操作时,从input stream中循环取出每条记录,先确定该记录应当进入哪一个output stream中。我们假设某一条记录被确定应当放入编号为A1的output stream,这个output stream A1将与stream B1匹配,被分配给线程1,作为线程1 Merge Join的两个输入。接下来SQL Server使用这条记录,查询与output stream A1对应的stream B1的bitmap。如果相应的位值为0,则说明这条记录在B1中不可能存在匹配的记录,因此这条记录被忽略掉,不会放入output stream A1中;如果相应的位值为1,则说明这条记录在B1中可能存在匹配的记录,该记录被放入output stream A1,继续在后续的Merge Join中进行精确的匹配。
        关于bitmap具体如何创建还不清楚,猜想大致应当是如下的一个过程:使用hash算法进行操作,在查询优化期间基于#alert_asn_shipoverdue 的Join字段ORG、ITEM可能出现的唯一值数量而确定bitmap的大小。这个过程和Hash Join中确定hash table buckes数量(和大小)有点类似。执行期间,先使用这个bitmap大小的值创建bitmap,初始化各个位均为0。然后为#alert_asn_shipoverdue循环,对每一个ORG、ITEM的组合值进行hash,得到的hash value对应于bitmap中的某一个位,将这个位设为1。
        基于上面bitmap的创建过程,在Parallelism/Repartition Streams操作时,某一条记录被确定进入哪一个output stream之后,即可以找到相对应stream的bitmap。对这条记录OrgID、ItemID的值,使用在bitmap创建时相同的hash算法,用得到的hash value查找对应的位。
     
        下面看一下示例中bitmap的使用带来的效果(点击可以放大查看):
     
        在对TblBuyerItem进行scan时,Row Count为1,543,050。在Repartition Streams操作中,WHERE:(PROBE([Bitmap1002])=TRUE)表明对bitmap的使用,Row Count 3,138。另外,从上图中可以看出,示例的SQL实际执行时使用了8 CPU。
        附加说明:在上面的示例中,#alert_asn_shipoverdue很小,并且bitmap的create 和probe过程,其实已经和Hash Join差不多。事实上,让SQL Server自动选择Join Type时,使用的是Hash Join(参考[Join Type实例说明],使用的是同样的示例)。使用Hash Join时用的是serial方式,耗时4秒多;上面的示例耗时2秒;上面示例不使用并行处理(MAXDOP 1)时,耗时1分30多秒。
     
        Merge Join中,在outer input的分支进入Merge Join前,必须有一个Sort操作,SQL Server才能使用bitmap。这是因为这个Sort操作使得整个outer input的分支处理完毕之后,才开始inner input分支的执行,最后再进入Merge Join操作。这样inner input分支执行时,才能使用在outer input分支上创建的bitmap。如果没有这个Sort操作,SQL Server会同时开始处理outer input分支和inner input分支,这样是无法使用bitmap的。
        我们可以在上面的示例上做一个验证。假如在#alert_asn_shipoverdue有一个clustered index(ORG,ITEM),那示例中的SQL语句执行时会是什么状况?在(ORG,ITEM)上创建clustered index后,执行计划变成下图所示(点击可以放大查看):
        对#alert_asn_shipoverdue的Table Scan变成了Clustered Index Scan,得到的stream是按照ORG、ITEM排序的。
           
        现在,在Parallelism/Distribute Streams操作前的stream是按ORG、ITEM排序的,因此Distribute Streams的各个output stream也是按照ORG、ITEM排序的,因此这个outer input分支在Merge Join前不再需要Sort操作,这样的话就无法使用bitmap了。
        从执行计划图中可以看到,inner input的分支,在TblBuyerItem的Table Scan之后,箭头的大小一直到Merge Join操作没有变化,说明在这个分支上一系列的操作中,记录数基本上没有什么变化。下图是TblBuyerItem的Table Scan和Parallelism/Repartition Streams的详细信息(点击可以放大查看):
     
        在这个验证中,没有使用bitmap的平均执行时间是1分10秒。
     
        Bitmap in Hash Join
        Hash Join中的bitmap,总体上来讲跟Merge Join中的处理是一样的。在build input(outer input)上构造hash table时是并行处理的,构造hash table的同时也为每个build input的stream创建bitmap。当整个build阶段完成后,再开始probe阶段,因此在probe阶段执行时已经可以使用bitmap,接下来的操作就跟Merge Join中Probe Bitmap操作完全一样。
     
        SQL Server只在Parallel Query中使用bitmap,估计是Probe Bitmap结合Parallel Query中的Repartition操作,可节约的成本比较明显。在Serial Query中,额外的去Probe Bitmap,可能对查询的执行并不能带来明显的改善。
     
        OK,It's over now. 下面回顾一下示例的语句在各种情况下的执行效果:
            
     
        参考:
  • 相关阅读:
    操作系统原理
    Linux三剑客正则表达式
    Linux通配符知识深度实践详解
    Linux文件属性之时间戳及文件名知识详解
    Linux系统文件权限
    date:显示与设置系统时间
    正则表达式--三剑客简单应用
    Linux习题小结
    Linux系统文件属性知识
    Linux系统目录结构知识
  • 原文地址:https://www.cnblogs.com/RicCC/p/492377.html
Copyright © 2011-2022 走看看