服务器 频道

当DB2优化器非正常工作时该怎么办?

  为什么有时表空间扫描比索引扫描更可取

  考虑相同的查询 SELECT * FROM CUSTOMER WHERE STATE = ?。不管您是否相信,有时执行表空间扫描比通过索引访问记录更有效。听起来让人吃惊吧?是的,也许要进行一些分析来得出这个结论。让我们从一些基准测试开始;然后加以说明。

  CUSTOMER 表中约 18% 的记录与条件 WHERE STATE=''IL'' 匹配。通过对查询 SELECT * FROM CUSTOMER WHERE STATE = ''IL'' 进行分析,优化器选择表空间扫描来执行它。让我们将该查询保存到名为 select.sql 的文件中,使用基准测试工具(db2batch)来测量执行该查询的实际代价。

  db2batch -d MY_DB -f select.sql -r benchmark.txt -o p3

  Number of rows retrieved is: 19998

  Number of rows sent to output is: 19998

  Elapsed Time is: 5.540 seconds

  Locks held currently = 0

  Lock escalations = 0

  Total sorts = 0

  Total sort time (ms) = 0

  Sort overflows = 0

  Buffer pool data logical reads = 2721

  Buffer pool data physical reads = 2580

  Buffer pool data writes = 0

  Buffer pool index logical reads = 0

  Buffer pool index physical reads = 0

  (有关 db2batch 的更多详细信息,请参阅[Command Reference]。)

  现在,让我们欺骗优化器,让它选择索引扫描来执行相同的查询。让我们使统计信息看上去象有一个虚构的州(STATE=''IM''),而且在这个州里有许多客户,再让我们使伊利诺斯州的客户数量(如果有的话)变得很小。因为 SYSSTAT 模式中的视图是可更新的,让我们更新其中一个:

  UPDATE SYSSTAT.COLDIST SET COLVALUE=''IM'' WHERE

  TABSCHEMA=''DB2INST1'' AND TABNAME=''CUSTOMER_DATA'' AND COLNAME=''STATE''

  --AND TYPE = ''F''

  AND COLVALUE=''''''IL''''''

  提示:在 WHERE 子句中,必须用引号括起列值(COLVALUE=''''''IL'''''')。

  现在,根据这些统计信息,优化器推断出只有很少的记录可能拥有 STATE=''IL''。因此,它选择使用 STATE 上的索引的存取方案(请参阅较小的估计基数):

  SQL Statement:

  SELECT *

  FROM CUSTOMER_DATA

  WHERE STATE=''IL''

  Estimated Cost = 50

  Estimated Cardinality = 1

  Access Table Name = DB2INST1.CUSTOMER_DATA ID = 2,5

  | #Columns = 13

  | Index Scan: Name = DB2INST1.CUST_STATE ID = 5

  | | Index Columns:

  | | | 1: STATE (Ascending)

  | | #Key Columns = 1

  | | | Start Key: Inclusive Value

  | | | | 1: ''IL''

  | | | Stop Key: Inclusive Value

  | | | | 1: ''IL''

  | | Data Prefetch: None

  | | Index Prefetch: None

  现在,让我们使用 db2batch 来执行第二个基准测试:

  Number of rows retrieved is: 19998

  Number of rows sent to output is: 19998

  Elapsed Time is: 5.976 seconds

  Locks held currently = 0

  Lock escalations = 1

  Total sorts = 0

  Total sort time (ms) = 0

  Sort overflows = 0

  Buffer pool data logical reads = 19998

  Buffer pool data physical reads = 2614

  Buffer pool data writes = 0

  Buffer pool index logical reads = 138

  Buffer pool index physical reads = 28

  显然,欺骗并没有给我们带来任何好处。在这种情况下,使用表空间扫描所耗费的时间实际上比使用索引扫描更少。

  重要事项:我们已经手工更新了统计信息来对测试数据库执行一些“假定方案(what if)”分析。这对 SYSSTAT 模式的可更新视图是完全合理的用法。但是,在生产数据库中,我们绝对不应在正常的情况下更新统计信息。

  现在,让我们解释发生了什么。我曾经听一个 5 岁的男孩说:“在监狱里待一秒钟不会有什么不良影响,所以在监狱里待两秒钟也不会有什么不良影响,那么在监狱里待三秒钟也不会有什么不良影响……”同样,通过索引读取一条记录会快一点,通过索引读取两条记录也会快一点,依此类推,但最多只能到某个数量,不能再多了。

  根据统计信息,优化器估计 18% 的记录将匹配条件 STATE=''IL''。它还预期这些记录在整个表中差不多是均匀分布的,因为 STATE 上索引的群集比率是非常低的,小于 0.1。(有关群集比率的更多信息,请参考 DB2 Administration Guide 中关于性能的章节。)这意味着:无论如何,几乎表中的每一页都至少有一条匹配的记录。表空间扫描使用预取,这意味着数据库引擎在一次有效的读操作中会读取几个相邻的页面。表空间扫描是读取表中所有页面的最有效的方法。无论索引扫描可能会多么有效,仍然存在扫描索引的额外工作。

  有关预取的更多信息,请参考:

  SQL Reference 中 CREATE TABLESPACE 语句的语法及其 PREFETCHSIZE 选项。

  DB2 Administration Guide 中关于性能的章节中缺省预取大小(DFT_PREFETCH_SZ)配置参数。

  因此,无论看起来有多令人吃惊,优化器选择表空间扫描最终是正确的。我们已经了解了在这种情况下,索引访问肯定效率比较低。

0
相关文章