提高DB2查询性能的常用方法(二)

2009-1-15    | |
打印本文章
RSS

导读:本文从三个方面介绍一些提高DB2查询性能的方法:创建索引和合理使用include关键词创建索引等。

关键词:IBM DB2 DB2查询 DB2性能与调优 提高DB2提高性能

正在加载数据...

  下面我们将从三个方面介绍一些提高查询性能的方法。

  创建索引

  根据查询所使用的列建立多列索引

  建立索引是用来提高查询性能最常用的方法。对于一个特定的查询,可以为某一个表所有出现在查询中的列建立一个联合索引,包括出现在 select 子句和条件语句中的列。但简单的建立一个覆盖所有列的索引并不一定能有效提高查询,因为在多列索引中列的顺序是非常重要的。这个特性是由于索引的 B+ 树结构决定的。一般情况下,要根据谓词的选择度来排列索引中各列的位置,选择度大的谓词所使用的列放在索引的前面,把那些只存在与 select 子句中的列放在索引的最后。譬如清单 5 中的查询:


  清单5. 索引中的谓词位置        


  select add_date
from temp.customer
where city = 'WASHINGTON'
and cntry_code = 'USA';

  对于这样的查询可以在 temp.customer 上建立 (city,cntry_code,add_date) 索引。由于该索引包含了 temp.customer 所有用到的列,此查询将不会访问 temp.customer 的数据页面,而直接使用了索引页面。对于包含多列的联合索引,索引树中的根节点和中间节点存储了多列的值的联合。这就决定了存在两种索引扫描。回到清单 5 中的查询,由于此查询在新建索引的第一列上存在谓词条件,DB2 能够根据这个谓词条件从索引树的根节点开始遍历,经过中间节点最后定位到某一个叶子节点,然后从此叶子节点开始往后进行在叶子节点上的索引扫描,直到找到所有满足条件的记录。这种索引扫描称之为 Matching Index Scan。但是如果将 add_date 放在索引的第一个位置,而查询并不存在 add_date 上的谓词条件,那么这个索引扫描将会从第一个索引叶子节点开始,它无法从根节点开始并经过中间节点直接定位到某一个叶子节点,这种扫描的范围扩大到了整个索引,我们称之为 Non-matching Index Scan。图 5 显示了 DB2 根据不同索引生成的存取计划。


  图5. 根据不同索引生成的存取计划

  根据不同索引生成的存取计划

  根据不同索引生成的存取计划

  根据条件语句中的谓词的选择度创建索引

  因为建立索引需要占用数据库的存储空间,所以需要在空间和时间性能之间进行权衡。很多时候,只考虑那些在条件子句中有条件判断的列上建立索引会也会同样有效,同时节约了空间。譬如清单 5 中的查询,可以只建立 (city,cntry_code) 索引。我们还可以进一步地检查条件语句中的这两个谓词的选择度,执行清单 6 中的语句检查谓词选择度:

  清单 6.检查谓词选择度                
             


   Queries:
1. select count(*) from temp.customer
where city = 'WASHINGTON'
and cntry_code = 'USA';
2. select count(*) from temp.customer
where city = 'WASHINGTON';
3. select count(*) from temp.customer
where cntry_code = 'USA';
Results:
1. 1404
2. 1407
3. 128700

  选择度越大,过滤掉的记录越多,返回的结果集也就越小。从清单 6 的结果可以看到,第二个查询的选择度几乎有和整个条件语句相同。因此可以直接建立单列索引 (city),其性能与索引 (city,cntry_code,add_date) 具有相差不多的性能。表 1 中对两个索引的性能和大小进行了对比。


  表 1. 两个索引的性能和大小对比

    
索引                 查询计划总代价   索引大小
cust_i1(city,cntry_code,add_date) 28.94 timerons   19.52M
cust_i3(city)           63.29 timerons    5.48M

  从表 1 中可以看到单列索引 (city) 具有更加有效的性能空间比,也就是说占有尽可能小的空间得到尽可能高的查询速度。

  避免在建有索引的列上使用函数

  这是一个很简单的原则,如果在建有索引的列上使用函数,由于函数的单调性不确定,函数的返回值和输入值可能不会一一对应,就可能存在索引中位置差异很大的多个列值可以满足带有函数的谓词条件,因此 DB2 优化器将无法进行 Matching Index Scan,更坏的情况下可能会导致直接进行表扫描。图 6 中对比了使用 function 前后的存取计划的变化。

  使用function前后的存取计划的变化

  
  图6. 使用 function 前后的存取计划的变化
 

  在那些需要被排序的列上创建索引

  这里的排序不仅仅指 order by 子句,还包括 distinct 和 group by 子句,他们都会产生排序的操作。由于索引本身是有序的,在其创建过程中已经进行了排序处理,因此在应用这些语句的列上创建索引会降低排序操作的代价。这种情况一般针对于没有条件语句的查询。如果存在条件语句,DB2 优化器会首先选择出满足条件的纪录,然后才对中间结果集进行排序。对于没有条件语句的查询,排序操作在总的查询代价中会占有较大比重,因此能够较大限度的利用索引的排序结构进行查询优化。此时可以创建单列索引,如果需要创建联合索引则需要把被排序的列放在联合索引的第一列。图 7 对比了清单 7 中的查询在创建索引前后的存取计划。


  清单7. 查询在创建索引前后的存取计划

              

  select distinct add_date from temp.customer;

  在创建索引前后的存取计划


  图7. 在创建索引前后的存取计划

  在创建索引前后的存取计划
 
  从图 7 中我们可以看到在没有索引的情况下 SORT 操作是 24751.69 timerons,但是有索引的情况下,不再需要对结果集进行排序,可以直接进行 UNIQUE 操作,表中显示了这一操作只花费了 2499.98 timerons.

  图8对比了清单 8 中的查询在创建联合索引前后的存取计划,从中可以更好的理解索引对排序操作的优化。

  清单8. 查询示例


               
select cust_name from temp.customer order by add_date;

  创建联合索引前后的存取计划


  图8. 创建联合索引前后的存取计划

  索引的 B+ 树结构决定了索引 temp.cust_i5 的所有叶子节点本身就是按照 add_date 排序的,所以对于清单 8 中的查询,只需要顺序扫描索引 temp.cust_i5 的所有叶子节点。但是对于 temp.cust_i6 索引,其所有叶子节点是按照 cust_name 排序,因此在经过对索引的叶子节点扫描获得所有数据之后,还需要对 add_date 进行排序操作。

  合理使用include关键词创建索引

  对于类似下面的查询 :

  清单9. 查询示例               


select cust_name from temp.customer
where cust_num between '0007000000' and '0007200000'

  在第一点中我们提到可以在 cust_num 和 cust_name 上建立联合索引来提高查询性能。但是由于 cust_num 是主键,可以使用 include 关键字创建唯一性索引:


create unique index temp.cust_i7 on temp.customer(cust_num) include (cust_name)

  使用 include 后,cust_name 列的数据将只存在于索引树的叶子节点,并不存在于索引的关键字中。这种情况下,使用带有 include 列的唯一索引会带来优于联合索引的性能,因为唯一索引能够避免一些不必要的操作,如排序。对于清单 9 中的查询创建索引 temp.cust_i7 后存取计划的代价为 12338.7 timerons,创建联合索引 temp.cust_i8(cust_num,cust_name) 后的代价为 12363.17 timerons。一般情况下,当查询的 where 子句中存在主键的谓词我们就可以创建带有 include 列的唯一索引,形成纯索引访问来提高查询性能。注意 include 只能用在创建唯一性索引中。

  指定索引的排序属性

  对于下面用来显示最近一个员工入职的时间的查询:


select max(add_date) from temp.employee

  很显然这个查询会进行全表扫描。查询计划如图 9.a:

  图 9. 查询计划

  查询计划

  显然我们可以在 add_date 上创建索引。根据下面的命令创建索引后的查询计划如图 9.b。

create index temp.employee_i1 on temp.employee(add_date)

  这里存在一个误区,大家可能认为既然查询里要取得的是 add_date 的最大值,而我们又在 add_date 上建立了一个索引,优化器应该知道从索引树中直接去寻找最大值。但是实际情况并非如此,因为创建索引的时候并没有指定排序属性,默认为 ASC 升序排列,DB2 将会扫描整个索引树的叶子节点取得所有值后,然后取其最大。我们可以通过设置索引的排序属性来提高查询性能,根据下面的命令创建索引后的查询计划如图 9.c。


create index temp.employee_i1 on temp.employee(add_date desc)

  对于降序排列的索引,DB2 不需要扫描整个索引数的叶子节点,因为第一个节点便是最大的。我们同样可以使用 ALLOW REVERSE SCANS 来指定索引为双向扫描,具有和 DESC 近似的查询性能。ALLOW REVERSE SCANS 可以被认为是 ASC 和 DESC 的组合,只是在以后数据更新的时候维护成本会相对高一些。

  如果无法改变索引的排序属性,但是我们具有额外的信息,该公司每个月都会有新员工入职,那么这个查询就可以改写成:


select max(add_date) from temp.employee where add_date > current timestamp - 1 month

  这样通过限定一个查询范围也会有效地提高查询性能。


提高DB2查询性能的常用方法
 提高DB2查询性能的常用方法(一)
 提高DB2查询性能的常用方法(二)
 提高DB2查询性能的常用方法(三)

原文出处:http://www.ibm.com/developerworks/cn/db2/zones/performance/
来源:IBM    
  评论
 
IBM在升级了旗舰DB2数据库后正努力吸引竞争对手的用户,升级后的DB2数据库能够运行为其他数据库尤其是Oracle数据库编写的应用。此举有可能刺激企业把他们现有Oracle或者……
 
在V7中,存在涉及insert和键锁的并发问题,但是在V8中,由于提供了type-2索引,这些问题实际上已经不见了。如果要迁移到V8中来……
 
继前一篇文章,本文继续介绍如何在DB2中提高Insert性能的技巧。包括以下内容:找到存储行的地方、缓冲池、I/O和页清除以及锁等方面的内容。
 
怎样在DB2中提高Insert性能的技巧?首先让我们快速地看看insert一行时的处理步骤。这些步骤中的每一步都有优化的潜力,对此我们在后面会一一讨论……
 
为了增加灵活性并提供更好的模式演化,DB2 V9.5为XML模式引入了update特性。以前注册的模式能够更新为新模式,只要它们互相兼容。
在SQL Server中,我们如何进行索引设计来提升系统的性能和对查询进行优化,是许多用户非常关注的问题,在本次技术专题中,我们将分群集索引和非群集索引设计两部分来为您详细分析。
本技术专题主要为使用Oracle SQL Developer和其他开发工具。包括如何使用Oracle SQL Developer和其他开发工具、使用Oracle Database Home Page、在Oracle中使用SQL*Plus等方面。
本专题为QL SELECT语句基础。侧重概述了如何使用SELECT来访问SQL数据库中所有内容以及组成SELECT语句的许多子句名称和功能;同时还阐述了如何使用DISTINCT关键字消除重复的行,以及如何正确使用ORDER BY子句来排序数据。
最新更新
专家答疑
技巧
Brian Fedorko
Oracle数据库审计功能十分强大、灵活并且易于配置,适当并有效地使用数据库审计功能是拥有高效安全策略的基础。
Brian Peasland
Oracle数据库二进制文件和库文件是高版本版本的,而备份的数据库内部如果仍然是较低版本,那么Oracle将不允许你打开数据库,除非你完成了升级过程。
Maria Anderson
在Windows XP环境下,使用数据库升级助手将Oracle从9.2.0.1升级到10g R2,出现DBUA不能识别Oracle数据库的SID问题如何解决。

登录TechTarget中国

关闭
本服务仅向TechTarget中国的会员开放,请登录或立即免费注册
登录Email
请输入您的登录Email
密码
下次自动登录