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了。


 

转载于:https://www.cnblogs.com/xshrim/archive/2012/11/17/4048959.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值