lt="NPSI 的次序整个表来都是随机的,而一个 DPSI 的每个索引分区的次序只是对于自己的分区是随机的。" />
因为 NPSI 是一个单序列列表,很容易看到 DB2 如何来回切换,从表到索引、索引到表,然后以 ZIPCODE
次序返回行,而不需要排序。读取可能是随机的,但是对于一些事务,少许随机读取以很少代价就可换取大量数据排序的消除。换句话说,很多行符合条件,但是程序使用 FETCH 只能获取 10 行;例如:| SELECT … FROM BIGTABLE (for example, 60 million rows)
WHERE ZIPCODE BETWEEN 10000 AND 70000
ORDER BY ZIPCODE
FETCH FIRST 10 ROWS ONLY
|
FETCH FIRST
子句阻止 DB2 以 LIST PREFETCH 方法使用我们的索引(在这种情况下,由于介入了 RID 排序,DB2 可能需要排序来满足 ORDER BY),鼓励 DB2 以传统方法使用索引来避免大量数据排序。然而,看看备用的 DPSI;我们有 3 个有序列表(每个分区一个)。显然,如果我们使用以下代码,规避排序不会有问题:
| SELECT … FROM BIGTABLE (for example, 60 million rows)
WHERE ZIPCODE BETWEEN 10000 AND 70000 AND LASTNAME BETWEEN 'A ' AND 'G99999'
ORDER BY ZIPCODE
FETCH FIRST 10 ROWS ONLY
|
尽管 LASTNAME
是我们的 ZIPCODE 索引的 not 部分,但是 DB2 知道我们想要的行可能只是在 Partition 1 中。因此,它可以以传统方法使用我们索引的 Partition 1 来避免在 ZIPCODE上进行排序。但是如果我们编写以下 SQL 语句又会怎么样呢?| SELECT … FROM BIGTABLE (for example, 60 million rows)
WHERE ZIPCODE BETWEEN 10000 AND 70000 AND LASTNAME BETWEEN 'A ' AND 'T9999'
ORDER BY ZIPCODE
FETCH FIRST 10 ROWS ONLY
|
DB2 跨多个分区界限了解我们想要的行,而且它也知道它需要跨多个分区进行一次表空间扫描,或者使用多个索引分区,每个都有其自己的顺序,来寻找每一行。FETCH FIRST
子句鼓励规避排序。那么 DB2 该做什么呢?
DB2 的解决方案
由于假象和一些匹配/合并逻辑,DB2 可以使用一个 DPSI 的多个分区来规避排序。显然这并不像使用一个单一索引的有序列表那样简单,但是它可以做到。DB2 Optimizer 知道在我们的案例中,为了以 ZIPCODE
顺序返回前 10 行,3 个分区必须以匹配/合并的方式读取,但是 DB2 将这种可能考虑到它的基于成本的决策中(见 APAR PM25934,这是 DSPI 成本的提炼)。没有 FETCH FIRST(或者类似地,但是并不确切,是没有一个 OPTIMIZE FOR 子句),那么 DB2 毫无疑问会选择其他访问路径。这里的重点是您可以选择在不丢失索引功能的前提下将索引定义为 DPSI 来规避排序,包括 DYNAMIC SCROLL
光标所需的规避排序。您只需要认识到,我们一直警告关于跨分区处理 DPSI 的 负面效应,包括规避排序所需的多个索引。所以要格外谨慎。
DPSI 的负面效应
以防您觉得我非常赞成 DPSI,强调他们的一些负面效应是很重要的:
- 潜在地、越来越多的调查和 getpages。
- 索引后备(index lookaside)丢失。一个 NPSI 可能在索引上利用后备链表的优势,例如,使用一个嵌套循环联接,其中外部行驱动对内部行的顺序访问。对于一个 NPSI, 这将导致索引后备和序列检测。对于一个 DPSI,其中符合条件的行在多个分区中,每个探针在分区之间跳来跳去。这种分区跳跃会破坏索引后备,也可能扰乱序列检测。
总结
知道我们不会放弃排序规避对于含有 DPSI 的 ORDER BY
是一个好消息。下面几期将继续介绍我们 SQL 中其他 4 个关于 ORDER BY(或缺乏)的问题。