`

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

    博客分类:
  • DB2
阅读更多

索引和表的维护

 

重新组织索引

 

随着数据的不断删除,插入和更新,索引页会变得越来越零散,索引页的物理存储顺序不再匹配其逻辑顺序,索引结构的层次会变得过大,这些都会导致索引页的预读取变得效率低下。因此,根据数据更新的频繁程度需要适当的重新组织索引。可以使用 REORG INDEXES 命令来重新组织索引结构,也可以删除并重新创建索引达到相同的目的。同样的,对表进行重新组织也会带来性能的改善。

 

新组织某一个表的所有索引的命令如下:REORG INDEXES ALL FOR TABLE table_name。

 

重新组织一个表的数据的命令如下,在下面的命令还可以为其指定一个特定的索引,REORG 命令将会根据这个索引的排序方式重新组织该表的数据。

 

REORG TABLE table_name INDEX index_name。

 

重新收集表和索引的统计信息

 

和在2.1中提到的原因类似,当一个表经过大量的索引修改、数据量变化或者重新组织后,可能需要重新收集表以及相关索引的统计信息。这些统计信息主要是关于表和索引存储的物理特性,包括记录数目,数据页的数目以及记录的平均长度等。优化器将根据这些信息决定使用什么样的存取计划来访问数据。因此,不能真实反映实际情况的统计信息可能会导致优化器选择错误的存取计划。收集表及其所有索引的统计信息的命令如下:RUNSTATS ON TABLE table_name FOR INDEXES ALL。

 

上述两个命令具有复杂的参数选择,用户可以参阅DB2 Info Center来根据实际情况使用这两个命令。

 

修改查询

 

合理使用NOT IN和NOT EXISTS

 

一般情况下NOT EXISTS具有快于 NOT IN 的性能但是这并不绝对。根据具体的数据情况、存在的索引以及查询的结构等因素,两者会有较大的性能差异,开发人员需要根据实际情况选择适当的方式。

 

譬如下面的查询:

 

清单10. 查询示例           

 

 


    表结构:temp.customer(cust_num) 主键:cust_num
表结构:temp.contact(cnt_id,cust_num) 主键:cnt_id
表结构:temp.contact_detail(cnt_id,address,phone) 主键:cnt_id
查询 :
select cust_num
from temp.customer cust
where not exists (select 1 from temp.contact cont
here cust.cust_num = cont.cust_num)

 

此查询用来列出所有不存在联系人的客户。对于这样的需求,开发人员会最自然的写出清单 10 中的查询,的确,对于大部分情况它具有最优的性能。该查询的查询代价为 178,430 timerons。让我们再来看看使用 NOT IN 后查询的总代价,请看清单 11。

 

清单11. 查询示例             
         


       查询:
select cust_num
from temp.customer cust
where cust.cust_num not in (select cont.cust_num from temp.contact cont)
代价:12,648,897,536 timerons

 

可以看到 NOT EXISTS 的性能要比 NOT IN 高出许多。NOT IN 是自内向外的操作,即先得到子查询的结果,然后执行最外层的查询,而 NOT EXISTS 恰好相反,是自外向内的操作。在上述例子中,temp.contact 表中有 65 万条记录,使得 10.2 查询中的 NOT IN 列表非常大,导致了使用 NOT IN 的查询具有非常高的查询代价。下面我们对 10.1 和 10.2 的查询进行修改,将 temp.contact 表中的记录限制到 100 条,请看下面的查询:

 

清单12. 查询示例
                
            


    查询:
select cust_num
from temp.customer cust
where not exists (select 1 from temp.contact cont
here cust.cust_num = cont.cust_num
and cont.cnt_id < 100)
代价:42,015 timerons


  清单13. 查询示例:              
 

               查询:
select cust_num
from temp.customer cust
where cust.cust_num not in (select cont.cust_num from temp.contact cont
where cont.cnt_id < 100)
代价:917,804 timerons
 

 

从 12 和 13 中可以看出 NOT EXISTS 的查询代价随子查询返回的结果集的变化没有大幅度的下降,随着子查询的结果集从 65 万下降到 100 条,NOT EXISTS 的查询代价从 178,430 下降到 42,015,只下降 4 倍。但是 NOT IN 的查询代价却有着极大的变化,其查询代价从 12,648,897,536 下降到 917,804,下降了 13782 倍。可见子查询的结果集对 NOT IN 的性能影响很大,但是这个简单的查询不能说明 NOT EXISTS 永远好于 NOT IN,因为同样存在一些因素对 NOT EXISTS 的性能有很大的影响。我们再看下面的例子

 

清单14. 查询示例
             

 

 


   
               查询:
select cust_num
from temp.customer cust
where not exists (select 1 from temp.contact cont
where cust.cust_num = cont.cust_num
and cont.cnt_id in (select cnt_id from temp.contact_detail
where cnt_id<100))
代价:5,263,096 timerons

 

清单15. 查询示例        

 

 


                查询:
select cust_num
from temp.customer cust
where cust_num not in (select cust_num from temp.contact cont
where cont.cnt_id in (select cnt_id from temp.contact_detail
where cnt_id<100))
代价:4,289,095 timerons

 

在上面的例子中,我们只是对查询增加了一个小改动,使用一个嵌套查询限制了在 temp.contact 中扫描的范围。但是在这两个新的查询中,NOT IN 的性能却又好于 NOT EXISTS。NOT EXISTS 的代价增加了 125 倍,而 NOT IN 的代价却只增加了 4 倍。这是由于 NOT EXISTS 是自外向内,嵌套查询的复杂度对其存在较大的影响。因此在实际应用中,要考虑子查询的结果集以及子查询的复杂度来决定使用 NOT EXISTS 或者 NOT IN。对于 IN,EXISTS 和 JOIN 等操作,大多数情况下 DB2 优化器都能形成比较一致的最终查询计划。

 

合理使用子查询减少数据扫描和利用索引

 

某些情况下可以将查询中的某一部分逻辑提取出来作为子查询出现,能够减少扫描的数据量,以及利用索引进行数据检索。请看清单 16 中的查询:

 

清单 16:
                      

 

 


 索引:temp.cust_i1 on temp.customer(add_date)
temp.order_i1 on temp.order(sold_to_cust_num)
temp.order_i2 on temp.order(add_date)
查询:
select cust.cust_num
from temp.customer cust
left join temp.order ord
on cust.cust_num = ord.sold_to_cust_num
where cust.add_date > current timestamp - 2 months
or ord.add_date > current timestamp - 2 months

 

上面的查询用来选择所有两个月内新增加的用户以及在两个月内定购了产品的用户。从图 10.a 的查询计划中可看出没有任何索引被使用。

 

查询计划

 

图 10. 查询计划

 


  使用子查询对该查询重新改写后,请看清单 17:

 

清单17.           
  


              查询:
with tmp as(
select sold_to_cust_num from temp.order 
where add_date > current timestamp - 2 months)
select cust.cust_num from temp.customer cust
where cust.add_date > current timestamp - 2 months
or cust.cust_num in (select sold_to_cust_num from tmp )

 

在清单 17 的查询中,我们使用子查询预先限定了要扫描 temp.order 表中的记录数目,而不是像清单 16 中的查询那样对 temp.order 表进行全表扫描。同时,在预先限定数据范围的时候,能够利用 temp.order_i2 索引。请看其查询计划,如图 10.b。可以看到查询代价有大幅度下降。其实,即使没有 temp.order_i2 索引,修改后的查询也仍然由于前者,因为它预先限定了数据的扫描范围,也减少了后续连接处理的数据量,请看图 10.c。

 

重新排列各个表的连接顺序,尽量减小中间结果集的数据量

 

一般情况下,DB2 会根据各表的 JOIN 顺序自顶向下顺序处理,因此合理排列各表的连接顺序会提高查询性能。譬如清单 18 中的查询:

 

清单18.    

 

 


         
                查询:
select cust.cust_name, ord.order_num, cnt.cnt_first_name
from temp.customer cust
left join temp.order ord
on cust.cust_num = ord.sold_to_cust_num
join temp.contact cnt
on cust.cust_num = cnt.cust_num
where cnt.mod_date > current timestamp - 1 months

 

清单 18 中的查询用来选择出所有最近一个月内修改过联系人信息的客户的订单信息。此查询会按照链接的顺序先将 temp.customer 表和 temp.order 表进行 LEFT JOIN,然后使用结果集去 JOIN temp.contact 表。由于该查询使用了 LEFT JOIN,因此在生成中间结果集的时候不会有任何记录会被过滤掉,中间结果集的记录数目大于等于 temp.customer 表。了解到了 DB2 是如何解释和执行这样的查询后,很自然的我们就会想到将 JOIN 提前。请看清单 19。

 


  清单19.

 

             


                查询:
select cust.cust_name, ord.order_num, cnt.cnt_first_name
from temp.customer cust
join temp.contact cnt
on cust.cust_num = cnt.cust_num
left join temp.order ord
on cust.cust_num = ord.sold_to_cust_num
where cnt.mod_date > current timestamp - 1 months

 

图 11.a 和图 11.b 分别为清单 18 和 19 的查询的存取计划。在 19 的查询中,在形成中间结果集的时候也应用到了 WHERE 语句中的条件,而不是在所有 JOIN 都结束以后才被应用去除记录的。

 

查询计划

 


  图11. 查询计划

 

另外,在修改查询尽量减少中间结果集的记录条数的时候还要考虑中间结果集的数据总量,譬如中间结果集需要保存的每条记录的长度。如果我们把 JOIN temp.contact 提前以后,由于中间结果集需要保存过多的 contact 表的列反而使得结果集的数据总量变大,可能不会带来性能上的改善。

 

使用UDF代替查询中复杂的部分

 

由于 UDF 是预先编译的,性能普遍优于一般的查询,UDF 使用的存取计划一经编译就会相对稳定。笔者在工作中曾多次发现,使用 UDF 代替查询或者视图中的复杂部分会提高几倍甚至几十倍的性能,主要原因是迫使 DB2 使用指定的存取计划来充分利用 index 或者调整其访问过程(如 Join 顺序, Filter 位置等)。使用 UDF 进行优化的基本思路是,将复杂查询分解为多个部分执行,针对每个部分优化处理,将各部分组合时能够避免存取计划的一些不必要变化,优化整体性能。譬如清单 20 中的查询:

 


  清单20.

 

                


                查询:select * from temp.customer where cust_num in (
select distinct sold_to_cust_num from temp.order
where add_date > current timestamp - 2 months
union
select distinct cust_num from temp.contact
where add_date > current timestamp - 2 months
)


 

 

这个查询会导致优化器生成比较复杂的查询计划,尤其是 temp.customer 是一个比较复杂的视图的时候。这种情况下我们可以通过创建 UDF,将其分步执行:先执行子查询获得 cust_num 值的列表,然后执行最外层的查询。下面的例子是通过 UDF 对清单 20 的查询的改写:

 


  清单21.

 

 


                CREATE FUNCTION temp.getCustNum(p_date timestamp)
RETURNS
TABLE (cust_num CHARACTER(10))
RETURN
select distinct sold_to_cust_num from temp.order
where add_date > p_date
union
select distinct cust_num from temp.contact
where add_date > p_date;
select * from customer where cust_num in (
select cust_num from table(temp.getCustNum(current timestamp - 2 months)) tbl
)


  改写前后的查询代价分别是 445,159.31 和 254,436.98。当面对比较复杂的查询时考虑使用 UDF 将其拆分为多步执行常常会带来意想不到的效果。在实际的项目中,如果数据处理和查询调用是包含在其他应用程序中如 Unix 脚本,Java 程序等,同样可以考虑采用分步数据处理的方式来调用数据库,以优化应用性能。

 

总结

 

本文主要介绍了如何使用 DB2 提供的各种查看存取计划的工具,并根据作者在 DB2 方面的开发经验总结了一些提高查询性能的方法和技巧。如果能够有效地利用 DB2 提供的各种工具,理解 DB2 中索引的结构,以及查询将如何被解释,数据库开发人员可以更好的提高查询性能来满足需求

分享到:
评论

相关推荐

    DB2高性能最优方法

    DB2高性能最优方法是数据库管理员和IT专业人员在管理和优化大型数据库...对DB2性能优化的深入理解和实践,是IT专业人士不可或缺的技能。在实际工作中,结合具体环境和需求,灵活运用这些方法,才能最大化DB2的潜力。

    DB2 通用数据库性能调整的常用方法

    ### DB2 通用数据库性能调整的常用方法 #### 统计值更新——runstats 在DB2数据库中,保持统计信息的最新状态是确保查询优化器能够做出最佳决策的关键。统计信息反映了数据库中数据分布的特性,如表的行数、各列值...

    DB2数据库性能调整和优化

    DB2数据库是IBM公司开发的一款关系型数据库管理系统,广泛应用于企业级数据存储和管理。...《DB2数据库性能调整和优化(第2版).pdf》这本书应该会提供更详尽的指导和案例分析,是学习和提升DB2性能优化技能的重要资源。

    DB2 数据库常用命令

    在DB2中,`RUNSTATS`是一个重要的实用程序,用于收集表和索引的统计信息,这些信息对优化查询计划和提高查询性能至关重要。当数据库的大小或数据分布发生变化时,运行`RUNSTATS`可以确保数据库管理系统(DBMS)拥有...

    db2性能调优之Sql执行监控.doc

    《DB2性能调优之SQL执行监控》 在数据库管理领域,DB2作为一款强大的关系型数据库管理系统,其性能调优是一项至关重要的任务。本文将深入探讨如何通过监控SQL执行来提升DB2的性能,帮助数据库管理员更好地理解和...

    linux下DB2常用命令

    Linux 下 DB2 常用命令 DB2 是一个 relation database management system(关系数据库管理系统),广泛应用于企业级的数据存储和管理中...这些命令可以帮助用户快速管理和维护 DB2 数据库,提高数据库的性能和安全性。

    db2数据库sql语句大全

    这有助于提高基于`column1`查询时的速度。 #### 7. 运行统计分析 ```sql runstatsontable&lt;&gt; for index &lt;&gt; shrlevel reference; ``` 此命令用于运行统计分析,它针对表`&lt;&gt;`及其索引`&lt;&gt;`执行,并设置共享级别为`...

    DB2常用命令集

    ### DB2常用命令集 #### 1. 停止 DB2 实例 - **命令**: `db2stop` - **描述**: 该命令用于停止正在运行的DB2实例。 - **注意事项**: 如果实例中有未完成的事务或连接,`db2stop`会等待这些事务完成或超时后自动...

    基于ORACLE与DB2的数据性能 调整和优化问题研究

    3.2 提高 DB2 查询性能的常用方法 DB2的优化包括使用索引、优化查询语句、启用并行执行、设置合适的统计信息等。例如,通过调整SQL语句的JOIN顺序,可以显著降低查询成本。 4. 性能分析和调优的命令和工具 4.1 ...

    DB2常用命令,DB2 v8数据库基础

    例如,分区功能允许将大型表分散到多个物理设备上,以提高查询性能。 文档"03_DB2 V8数据库基础.doc"可能涵盖了DB2 v8的基础架构、安装步骤、实例创建和管理、安全性配置等内容。而"02_DB2 常用命令.doc"则详细解释...

    DB2性能安全文件

    ### DB2性能安全知识点概述 #### 一、SERV_ID等常用字段的长度为何需要统一? 在实际生产环境中,为了确保数据的一致性和查询效率,DBA常常会规范某些常用字段(如`SERV_ID`)的长度。这样做主要是为了减少因字段...

    DB2 UDB AS400版数据性能和查询优化

    本文档主要针对DB2 UDB在AS/400上的应用,重点介绍如何进行数据性能优化和查询优化,旨在帮助用户提高数据库运行效率,减少响应时间,提升用户体验。 #### 二、关键概念与术语 1. **AS/400**: IBM推出的一种集成化...

    db2数据库常用命令汇总

    `db2 create index &lt;索引名&gt; on &lt;表名&gt;(&lt;字段名&gt;)`:为表创建索引,提高查询速度。 `db2 drop index &lt;索引名&gt; on &lt;表名&gt;`:删除已有的索引。 8. **权限管理** `db2 grant &lt;权限&gt; on &lt;对象&gt; to &lt;用户&gt;`:赋予用户...

    提高DB2存储过程性能和健壮性.txt

    ### 提高DB2存储过程性能和健壮性的关键策略 在数据库管理与优化领域,DB2作为IBM的一款高性能关系型数据库管理系统,其存储过程的优化是确保应用响应速度与数据处理效率的关键。以下是对如何提高DB2存储过程性能和...

    Db2中的常用命令和导入导出

    本文将详细介绍DB2中常用的命令及其导入导出方法。 #### 常用命令 ##### 启动与停止数据库服务 - **db2start**:启动当前的DB2数据库实例。 - **db2stop**:停止当前的DB2数据库实例。 - **db2stop force**:强制...

    DB2数据库常用命令手册

    - 重新组织表可以提高查询性能,运行统计信息有助于优化查询计划。 #### 十、导出数据 **命令:** - `db2 export to &lt;file_path&gt; of del select * from &lt;table_name&gt;`:将数据导出到指定文件。 **注意事项:** - `...

    DB2学习资料以及常用命令

    `reorgtable` 命令用于优化表的物理存储,减少碎片,提高查询性能。`runstats` 则更新表的统计信息,帮助DB2更准确地选择查询执行计划。 ##### 4. 锁定表(LOCKTABLE) ``` LOCKTABLETABLE_NAMEINEXCLUSIVEMODE//...

    db2 常用命令集合

    - `db2 reorg table ydd`:整理表`ydd`的空间,以提高查询性能。 - `db2 runstats on table ydd with distribution and indexes all`:收集统计信息并分析表`ydd`的数据分布,以优化查询计划。 #### 七、导出与导入...

    IBM db2 常用命令大全

    缓冲池是 DB2 中的一个重要概念,用于提高数据库的性能。下面是创建缓冲池的命令: `create bufferpool ibmdefault8k IMMEDIATE SIZE 5000 PAGESIZE 8 K ;` `create bufferpool ibmdefault16k IMMEDIATE SIZE 5000...

    DB2 常用命令汇总

    在IT行业中,DB2作为IBM的一款高性能关系型数据库管理系统,其广泛应用于企业级数据处理场景。对于初学者而言,掌握DB2的基本命令是入门的关键。以下是从给定文件中提取并详细阐述的DB2常用命令知识点: ### 1. ...

Global site tag (gtag.js) - Google Analytics