首页  ·  知识 ·  数据库
DB2SQL开发:最佳实践
记者  CIOZJ  DB2  编辑:dezai   图片来源:网络
在编写代码时,您应该始终牢记两个目标。第一个也是最重要的目标是获取正确的数据。处理或检索的所有数据都必须是正确的。数据完整性和正确的报告是要处理的头等大事。第二个目标是尽可能快地执

我个人同意 DB2 关系数据库开发中的大多数性能问题都是由程序代码造成的观点。在我曾提供性能和调优咨询工作的所有 IT 商店中,大多数性能问题都直接关系到修改应用程序和 SQL 代码、添加和更改索引,或改善数据统计。改进主要体现在应用程序和 SQL 代码方面。

免费下载: IBM® IBM Data Studio 试用版 下载更多的IBM 软件试用版,并加入 IBM 软件下载与技术交流群组 ,参与在线交流。

我发现,有相当高比例的开发人员其实不知道性能问题涉及到 SQL 编程,也不知道如何解决它们。该为这些开发人员辩护一下的是,将变更和新代码迁入生产环境的压力太大了,而且根本没有任何时间进行性能调优。但也有一些基本知识是所有开发人员都应该知道、并且商店需要接受教育的。这可以节省 IT 成本,最大程度地减少生产事故报告。

回页首

SQL 101 规则

所有开发人员都应该知道我称之为 SQL 101 规则的其中几条:

  • 千万不要将 SQL 标量函数放在 WHERE 谓词中的列上。例如:WHERE YEAR(HIREDATE) = 2009 应该被重新编码为 WHERE HIREDATE BETWEEN '2009-01-01'and '2009-12-31'。如果将 SQL 标量函数放在 SQL 语句 SELECT 部分中的列上,的确会产生一些最起码的开销,但是,如果将它应用于 WHERE 子句中的某个列上,则会导致谓词在第 2 阶段成为不可索引的。
  • 同样的规则也适用于数学。例如:WHERE HIREDATE – 7 DAYS > :HV-DATE 应该被重新编码为 WHERE HIREDATE > :HV_DATE + 7 days。在主机变量上利用数学运算并不是什么问题。
  • 只对 SQL SELECT 中需要的列进行编码。只有在执行处理的过程中、具有较大的排序成本的时候,以及有时在选择联接类型的时候,额外的列才有可能影响优化器的索引选择。
  • 注意所有 ORDER BY、GROUP BY、DISTINCT、UNION、INTERSECT 和 EXCEPT 语句。这些语句可能导致在处理过程中执行排序。确保在查询真正需要它们。
  • 尽量减少来自应用程序的 SQL 请求。尤其是在执行批处理的过程中,执行的语句量通常较多。每次 SQL 请求被发送到 DB2 都会涉及一些开销,因为它必须从操作系统中的一个地址空间(例如 COBOL 或 Java™)发送到 DB2 地址空间。通常,开发人员应该减少打开和关闭游标的次数、执行 select 语句等等,以便尽量减少这些请求。DB2 因为这个原因专门为开发人员提供了多行处理,以便可以一次处理多个行。开发人员需要以关系更强和过程更少的方式编写代码。

所有开发人员都应该知道如何阅读 DB2 Explain。Explain 提供了很多信息,但您应该知道如何读懂基本的信息。例如:

  • 是否正在使用索引或表空间扫描?
  • 选择了哪个索引,匹配的列是什么?
  • 是否正在执行任何排序?为什么?进行排序的行的数量是多少?
  • 是否有任何第 2 阶段的谓词?

如果进行了表空间扫描,则需要弄清楚为什么 DB2 优化器会选择了此访问路径:

  • 所涉及的表小吗?有时 DB2 选择对一个索引执行扫描的原因是行数太少。
  • 是否即使表中有很多行,Runstat 统计数据仍然显示行数很少?检查所涉及的表,查看最后一次运行 Runstats 实用程序的时间。
  • 是否有任何适当的索引可以匹配 SQL 谓词?表上的索引是什么?
  • 查询是否从有问题的表中检索了很多行,或者 DB2 是否认为会返回很多行?DB2 认为针对某个谓词返回的行数被称为 Filter Factor(过滤系数),而且在 Explain 中,该系数显示为百分比。在 Data Studio 的 Visual Explain 输出中可以看到过滤系数,在执行 Explain 以后,也可以通过查询 DSN_PREDICAT_TABLE 查看此系数。
  • SQL 谓词是否涉及以较差的代码、以不可索引的方式编写的表?例如,标量函数是否在谓词中的列上?
  • 优化器是否选择了一个非聚类的索引?要返回的数据量有多少?有时,优化器可能会看到某个表包含 1500 页的数据,而查询返回了许多的行数,最终导致大部分这些页面都被访问。

您需要查看选中了哪些索引,然后再进一步研究 Explain,了解探测索引的效率有多高:Explain 输出中有一列指出了在所选定索引中使用的匹配的列。越多的列得到匹配,索引探测就会越快。如果 Explain 指出匹配的列数为 0,则会进行索引扫描。这通常不是一个良好的访问路径,您应该研究一下,了解为什么没有匹配的列。

如果执行任何排序,则需要了解为什么进行排序,并查看能否消除它们。排序和大小一样,都会带来昂贵开销。在 DB2 中,排序处理可以非常快,但很明显,行数较多和较大的行会带来很大的差别。如果执行排序,并且行数较少,则不太可能带来性能问题,但最高效的排序是从不执行排序。人们会问的问题包括:是否可以消除排序?排序是否需要使用 Distinct、Group By、Order By、Union、Intersect、Except,等等?您能否消除所有这些问题,或者是否可以用不同的方式重写查询,从而避免排序?

第 1 阶段与第 2 阶段的谓词对 SQL 查询的性能可以有不同的影响。DB2 在 SQL 谓词处理中采用了由两个阶段组成的架构。在一般情况下,不会执行第 2 阶段的谓词,并且会使用额外的 CPU 资源。IBM Data Studio 工具突出显示了查询中的所有第 2 阶段的谓词,也可以在执行 Explain 后查询 DSN_PREDICAT_TABLE。许多第 2 阶段的谓词都可以重写为第 1 阶段的谓词。如果您搜索 DB2 Summary of Predicate Processing(谓词处理的 DB2 摘要),系统会将您带到列出所有第 1 阶段和第 2 阶段谓词的 IBM 手册。

回页首

开发人员目标

在编写代码时,您应该始终牢记两个目标。第一个也是最重要的目标是获取正确的数据。处理或检索的所有数据都必须是正确的。数据完整性和正确的报告是要处理的头等大事。第二个目标是尽可能快地执行处理。很多时候,您可能会忽略第二个目标。要么不知道该怎样做才能让进程运行得更快,要么没有时间去担心它或责怪环境的其他部分。您往往可能会认为,与所处理的行数相比,处理所花费的时间还算不错,您却从来不知道,如果一切都正确的话,DB2 处理数据的速度可以要快得多。您始终应该知道的一件事(标准)是,在运行时处理了多少数据。程序处理了 10,000 行数据还是1000 万?发生了多少 OPEN CURSORS、SELECTS、UPDATES、DELETES,等等?在出现性能问题时,人们会问的第一个问题通常是:处理了多少数据?每个程序都应该在某处显示或记录每次执行处理时所处理的数据量。

如果您正在处理查询或程序,以提高性能,那么应该从哪里开始呢?我建议按照以下步骤执行:

  • 检查每个谓词。它们是否是可索引的,是否是第 1 阶段的谓词?您能够以更高效的方式重写它们吗?所有第 2 阶段的谓词有哪些?
  • 您能否以不同方式重写所有谓词,但包含同样的逻辑?有时,甚至重写一个谓词就可能让优化器走上不同的访问路径。
  • 检查 SQL 请求被发送到 DB2 的次数。有没有什么办法重写/重新设计程序,将要处理的请求数量降至最低?
  • 检查 DB2 统计信息。检查它们是否最新的,是否正确?在测试环境中,Runstat 统计数据往往不是最新的。
  • 检查 DB2 Explain 输出。查看是否在执行表空间扫描?是否在执行任何排序?任何索引处理的匹配列数是否为 0?大多数时候,开发人员希望看到为其查询选择的索引处理。
  • 您能否以不同的方式重写任何查询,但获得同样的结果?有时,可以采用两种、三种、四种,甚至更多种不同的方法来编写查询,并实现相同的输出,但它们的优化方式并不总是相同的。某种方法可能比其他方法要快得多。
  • 是否涉及任何子查询?尝试将 IN 子查询重写为 EXISTS 子查询。很多时候,它们的优化方式是各不相同的。
  • 是否有多个子查询?使两个子查询的类型相同(相关与非相关),然后将最严格的子查询放在第一。

回页首

结束语

我曾参加过众多有许多开发人员(本地及外包)参与的大型项目,并发现许多开发人员并不知道 SQL 开发的基本知识和 DB2 Explain 工具。以我的经验,在生产环境中实现项目时,拥有 SQL 编程标准和指南以及代码演练的项目可以节省 CPU 成本并减少事故报告。通常情况下,管理层会说开发人员根本没有时间执行 DB2 Explains 和代码演练。而我说肯定有时间,并且我们仍然可以在上周二完成工作。我也经常在生产中看到写得不好的代码。我认为教授开发人员(无论是本地还是外包)SQL 开发基础知识是 IT 商店的责任。这一切都增加了 CPU 成本。我发现,一点点的培训、商店代码标准和程序演练可以完全改变 IT 开发部门的文化,最大程度地减少性能问题。

综上所述,以下是您应该遵循的 SQL 优化代码标准和指南的一个简表:

  • 不要在谓词的列上编写函数代码。
  • 不要在谓词的列上编写数学代码。
  • 注意所有表空间扫描。
  • 尽量减少发送 DB2 SQL 语句的次数。
  • 注意所有排序。
  • 只对需要的列进行编码。
  • 只对需要的列进行排序。当可以执行 ORDER BY EMPNO 时,不需要使用 ORDERY BY EMPNO, LASTNAME。
  • 注意 Union 与 Union all。为了实现惟一性,Union 会导致排序。INTERSECT 和 EXCEPT 也有同样的效果。
  • 在代码中只使用第 1 阶段的谓词。重写所有第 2 阶段的谓词。
  • 如果有可能,在连接谓词中尽量不要使用 'OR' 逻辑。Boolean 术语谓词是最好的谓词。
  • 注意 'LIKE' 谓词。'Begins With' 逻辑是可索引的。'Contains' 是不可索引的。'Ends With' 是不可索引的。
  • 在代码中不要使用 'Not Between'。将它重写为 > HV 或 < HV。
  • 尽可能使用 'Fetch First XX Rows'。
  • 在只读游标上使用 FOR FETCH ONLY。
  • 所有 Case 逻辑的代码中都应该有一个 'else'。如果所有 Case 条件都无法得到满足,则会在默认情况下消除 DB2 并返回空值。
  • 尽可能在谓词中使用硬编码值。如果值的分布不均匀,则需要对这些硬编码值进行特殊的统计。
  • 确保所有表中的所有列都存在基数统计。
  • 代码的顺序为从限制最多的谓词到限制最少的谓词。
  • 尽可能地不要使用 'Not' 逻辑。
  • 在代码中使用 'Not Exists' 而不是 'Not In'。两者都是第 2 阶段的谓词,但 'Not Exists' 通常优于 Not In,特别是在列表很长的情况下。
  • 尽量减少打开和关闭游标的次数。

 

本文作者:记者 来源:CIOZJ
CIO之家 www.ciozj.com 微信公众号:imciow
    >>频道首页  >>网站首页   纠错  >>投诉
版权声明:CIO之家尊重行业规范,每篇文章都注明有明确的作者和来源;CIO之家的原创文章,请转载时务必注明文章作者和来源;
延伸阅读
也许感兴趣的
我们推荐的
主题最新
看看其它的