zoukankan      html  css  js  c++  java
  • DB2数据库查询过程(Query Processing)多索引访问(Multiple Index Access)

    引言

    考虑下面的情况:

    一张表T,有列C1,C2,C3,C4,C5。其中C1上有索引C1X,C2上有索引C2X,C3,C4,C5上有索引C345X。表中有100000000行数据。

    查询语句:

    Select * From T Where C1=20 And C2=5 And C3=11;

    前面介绍过,对于一张表,只能使用它的一个索引进行索引扫描,上面查询的三个谓词都是可索引谓词,假设这三个谓词的过滤因子都是1/100,优化器选择使用C1X索引。那么就需要读入100000000*1/100=1000000行数据。I/O开销无疑是可观的。

    对于这样的访问方案,我们认为它太浪费索引了!三个索引,只用一个,导致另外两个谓词连筛选谓词都算不上(筛选谓词起码能对索引项进行筛选呢)。

    如果能够同时使用三个索引,那么三个谓词合起来的过滤因子就是1/100*1/100*1/100=1/1000000,最终需要读入的数据行为100000000*1/1000000=100行,效率根本不能相提并论!

    但是,这样的访问方式存在吗?在DB2中,是存在的。这就是所谓的多索引访问。


    多索引访问(Multiple Index Access)

    多索引访问的思想是:对表中数据分别使用各个索引,最后将满足条件的进行交集或并集操作。

    当然了,如果是对数据行直接分别使用多个索引,以上面的例子,那至少得先读1000000+1000000+1000000=3000000行数据,然后继续交集操作,绝对是不可取的。

    DB2采用的策略是将各个索引中满足相应的匹配谓词的RID(行指示器)分别提取出来(要知道,索引树的叶结点页中的索引项中是包含了表中所有数据行的RID的),然后对这些RID做交集或并集操作,最后将得到的RID对应的数据行使用列表预取读入缓冲池。


    还是以上面的例子为例具体解释一下多索引访问的步骤:

    1.将索引C1X的所有叶结点索引页使用I/O顺序预取读入缓冲池(叶结点索引页通常是有序存储的,所以可以使用I/O顺序预取),根据”C1=20“这个匹配谓词提取出符合条件的RID,将这些RID存储在RID候选列表(RID candidate list,简称RID list)中,这个RID list存在于缓冲池中的一块单独区域:RID pool。

    2.同理,将索引C2X中符合C2=5条件的RID提取出来存储在另一个RID list中。

    3.同理,将索引C345X中符合C3=11条件的RID提取出来存储在另一个RID list中。

    这些RID list被存储在一个堆栈中,最新的RID list位于堆栈的顶端,如图:


    4.将堆栈中最上层的两个RID list(即C345X-RID list和C2X-RID list)弹出,进行交集操作,得到的新的RID list重新压入堆栈中。如图:


    5.同理,将堆栈最上层两个RID list弹出,进行交集操作,新的RID list压入堆栈中,就能得到最终满足C1=20 and C2=5 and C3=11的所有RID了。如图:


    6.根据最终得到的RID list,使用列表预取将对应的数据页读入缓冲池中。

    下表是整个执行过程的过程表:

       操作表          访问类型              匹配列             使用的索引           预取类型        执行顺序


     

    具体的开销估算过程就不写了,前面的文章已经演算过很多次了。对于有大量数据行的表,多索引访问的效率提升还是非常明显的。不过有几个问题需要说明一下:

    A.使用索引提取RID的过程中,对所有索引页的读入都是使用的I/O顺序预取,它比随机I/O快10倍,达到800 次/秒。

    B.能够用于提取RID的条件只能是匹配谓词,筛选谓词是不能用来筛选RID的,另外In-List谓词也不能用来筛选RID。

    C.对于语句”Select * From T Where C1=20 And C2=5 And C3=11“,我们对三个RID list取交集,相应的,如果是”Select * From T Where C1=20 Or C2=5 Or C3=11“语句,那就需要对RID list取并集了;混合的自然就是交集,并集混合使用了,比如:Select * From T Where C1=20 And (C2=5 Or C3=11),多索引访问过程如下表:

       操作表          访问类型              匹配列             使用的索引           预取类型        执行顺序



    列表预取的限制(List Prefetch Limit)

    得到满足条件的RID list后,选择了使用列表预取读入数据页,没有使用顺序预取是因为数据页地址不是有序的,没有使用随机I/O是因为列表预取效率更高(200次/秒)。

    那么,既然列表预取比随机I/O效率高,随机I/O还有什么存在的必要呢?

    这是因为,使用列表预取也是有前提条件的:它需要事先知道需要取哪些数据页,然后才能为磁盘控制器提供区块请求列表。否则胡乱提供请求列表,将带来大量不必要的I/O,效率不升反降。(参看:《DB2数据库查询过程(Query Processing)----表扫描与I/O(Table Scan and I/O)》)

    对于多索引的情况,很显然,事先已经得出了RID list,自然就能够使用列表预取了。但是对于一般查询(如Select * From T),没有明确的地址列表,就不能使用列表预取,只能选择随机I/O了。


  • 相关阅读:
    java native方法
    linux free命令
    gdb使用
    java锁——wait,notify,synchronized
    java面试——问题回溯
    (转)每天一个linux命令(44):top命令
    java面试——jvm
    java面试——多线程
    数据库面试总结
    CMakeLists.txt使用
  • 原文地址:https://www.cnblogs.com/xshrim/p/4048959.html
Copyright © 2011-2022 走看看