外文资料译文及原文
院(系):计算机学院
专业:计算机科学与技术
班级:2401102
学号:20023011059
姓名:
指导教师:
2005年6月
简介
有关如何调优数据库系统和应用程序的好的建议的来源有很多。比如OLTP 应用程序的DB2调优技巧(以前在IBM® DB2® 开发者园地上发表)之类的文章通过使用事务和数据并行性以及分析查询方案,给出了从表空间和索引设计到缓冲池的内存分配等方面的建议。这些方面的内容是性能调优的基础知识。
但是,有关如何组织存储过程自身中的逻辑并着眼于其性能的专门建议却并不多见。本文就提供了这样一种建议。尽管本文着重于介绍 SQL 过程,但是这里所提供的大多数信息同样适用于用其它语言编写的在应用程序中或存储过程中嵌入的 SQL 逻辑。
背景知识和术语
在深入研究详细问题之前,让我们先想想DB2 中有关过程化 SQL 的一些基本术语和概念。过程化 SQL 构造(例如标量变量、IF 语句和 WHILE 循环)是在DB2 Universal Database™ (UDB) V7 发行版中引入 DB2 的。以前的 DB2 发行版支持 C 和Java™ 作为存储过程的语言。V7 引入了 SQL 存储过程,以及其它许多可以促进 OLTP 应用程序开发的特性(例如临时表、应用程序保存点和标识列)。
当创建 SQL 过程时,DB2 将过程主体中的 SQL 查询与过程逻辑区分开来。为了使性能最优,SQL 查询被静态地编译成包中的节。(对于静态编译的查询而言,节主要是由 DB2 优化器为该查询选择的存取方案构成的。包是节的集合。
在过程的执行期间,每当控制从过程逻辑流向 SQL 语句时,在 DLL 和 DB2 引擎之间就存在“上下文切换”。(在 DB2 V8 中,SQL 过程是在“不受保护的方式”下运行的,即与 DB2 引擎在相同的寻址空间中。因此我们这里谈及的上下文切换并不是操作系统级别上的完全的上下文切换,而是指 DB2 中层的更换。)减少频繁调用的过程(例如 OLTP 应用程序中的过程)或者处理大量行的过程(例如执行数据
清理的过程)中的上下文切换次数,对它们的性能有显著的影响。本文中的几个技巧恰好旨在减少这些上下文切换。
刚开始的时候(DB2 通用数据库 V7 GA),只允许在 SQL 过程中使用 SQL 过程语言(通常称为 SQL PL)。后来(在 DB2 UDB V7.2 中),在 SQL 函数和触发器主体中开始支持该语言的子集。SQL PL 的这个子集即所谓的内联(inline)
SQL PL。“内联”一词突出显示了它与完整语言的重要区别。SQL PL 过程是通过将其单独的 SQL 查询静态地编译成包中的节实现的,而内联 SQL PL 函数就象其名称所展示的,是通过将函数主体内联到使用它的查询中实现的。稍后我们将再看一下内联 SQL PL 及其用法的一些示例。
从多个 SQL 语句到一个 SQL 表达式跟其它编程语言一样,SQL 语言提供了两类条件构造:过程型(IF 和 CASE 语句)和函数型(CASE 表达式)。在大多数环境中,可使用任何一种构造来表达计算,到底使用哪一种只是喜好问题。但是,使用 CASE 表达式编写的逻辑不但比使用 CASE 或 IF 语句编写的逻辑更紧凑,而且更有效。
使用 SQL 的一次处理一个集合语义,诸如循环、赋值和游标之类的过程化构造允许我们表达那些只使用 SQL DML 语句是不可能表达的计算。但是,当我们拥有一些可以随意使用的过程语句时,即使我们手头的计算实际上仅使用 SQL DML 语句就可表达,但转换成过程语句还是有风险的。正如我们以前提
到的,过程计算的性能与使用 DML 语句表达的同一个计算的性能相比会慢几个数量级。
在研究改进现有过程逻辑的性能时,为消除游标循环而花费的任何时间都可能是值得的。
改进游标性能
如果存储过程中的逻辑确实需要游标,那么要使性能最优,请牢记下面这些内容。
首先,请确保不使用高于您所需的隔离级别。隔离级别决定了 DB2 对过程读取或更新的行应用的锁定的数量。隔离级别越高,DB2 将执行的锁定越多,因此为同一资源而竞争的应用程序之间的并发就越少。例如,使用可重复读(Repeatable Read,RR)隔离级别的过程将形成对其读取的任何行的共享锁,而使用游标稳定性(Cursor Stability,CS)的过程只会锁定任何可更新游标的当前行。可以使用 DB2_SQLROUTINE_PREPOPTS 注册表变量来指定 SQL 过程的隔离级别。
DB2 中缺省的隔离级别是游标稳定性。但是,当然了,为了保持应用程序的正确性,有时需要使用可重复读。还需记住一件重要的事情,一旦创建了需要可重复读的过程,必须将 DB2_SQLROUTINE_PREPOPTS 重新设置回较低的隔离级别。
在尝试改进游标性能时需要牢记的一个相关问题是游标的可更新能力。如果游标涉及的行是可以使用 INSERT 或 DELETE 语句中的 WHERE CURRENT OF 子句进行更新或删除,那么它就是可删除的。当
游标可删除时,DB2 必须获取行上的互斥锁(与共享锁相对),并且不能执行行分块。行上的互斥锁甚至可以防止其它应用程序读取该行(在互斥锁被释放之前,这些应用程序必须等待,除非它们的隔离级别是 UR),而行分块通过在一个操作中检索行块,从而减少了用于游标的数据库管理器开销。
只有不可删除的游标才可以进行行分块。这就是为什么让 DB2 了解将如何使用游标是很重要的原因。通过在 SELECT 语句中指定 FOR READ ONLY 子句,可以将游标显式地声明为不可删除,或者通过在 SELECT 语句中使用 FOR UPDATE 子句将其声明为可删除。根据该信息(并且还根据下面描述的 BLOCKING 选项),DB2 将确定是否将行分块用于给定的游标。
缺省情况下,对于那些使用 FOR READ ONLY 子句定义的游标,DB2 将始终使用行分块,除非指定了 BLOCKING NO 绑定选项。另一方面,如果使用了BLOCKING ALL 绑定选项,那么对于含混游标(既不是定义成 FOR READ ONLY 也不是定义成 FOR UPDATE 的游标),DB2 将使用行分块。
简而言之:如果可能,则在游标定义中使用 FOR READ ONLY 子句;如果您的过程包含含混游标,那么请使用 BLOCKING ALL 绑定选项。要设置 BLOCKING 绑定选项的值,我们还可以使用 DB2_SQLROUTINE_PREPOPTS 注册表变量。
db2数据库sql语句在无副作用的情况下,请使用 SQL 函数。正如我们在简介中提及的,SQL 过程和 SQL 函数是使用不同技术实现的。SQL 过程中的查询是单独编译的,每个查询都成为包中的一个节。编译是在过程创建时进
行的,直到重新创建过程或者直到重新绑定其相关的包时才重新编译这些查询。
另一方面,SQL 函数中的查询是一起编译的,就好像函数体是一个查询一样。每当编译一条使用 SQL 函数的语句时,也会对 SQL 函数进行编译。
与 SQL 过程中所发生的情况不同,SQL 函数中的过程语句与数据流语句是在同一个层中执行的。因此,每当控制从过程语句流向数据流语句或相反时,并不发生上下文切换。
因为存在这些区别,所以当给定的过程代码段作为函数实现时的执行速度通常比作为过程实现时要快。但是,当然了,有一个小问题。函数只能包含那些不会改变数据库状态的语句(例如 INSERT、UPDATE 或 DELETE 语句是不允许的)。并且只允许完整 SQL PL 语言的子集出现在 SQL 函数中(不能是 CALL 语句、游标和条件处理)。
尽管有这些限制,但大多数 SQL 过程都可以在无副作用的情况下转换成SQL 函数。
因此,正如本节标题所展示的,当您只是从数据库抽取数据而不执行任何更改时,请考虑使用 SQL 函数而不是使用 SQL 过程。
使用用于临时数据的临时表
在 V7 中,DB2 引入了临时表。对临时表的操作通常比对常规表的操作快。让我们看一些原因:
∙首先,临时表的创建不会涉及向目录中插入项,并且临时表的使用也不会涉及对目录的访问;因此,不会有目录争用问题。
∙因为临时表只能由创建它们的应用程序访问,因此在其操作中不会涉及锁定问题。
∙如果指定了 NOT LOGGED 选项,则不对临时表上的操作记录日志(当然,这样就不可能回滚更改)。因此,如果您的存储过程生成了大量临时数据,并只打算在数据库的一个会话中使用它们,那么请将这些数据
存储进临时表,这样可以显著地改进性能。
在对 SQL 过程中的临时表进行任何应用之前,表定义在编译环境中必须是可用的。
在执行了 CONNECT RESET 命令后,临时表将不复存在。在运行时,应用程序必须确保在执行使用临时表的首个查询之前该表是存在的。最后的这个观察引出了一个我们从未提及的要点:引用临时表的任何查询都将被动态地编译,即使该查询被写成静态的 SQL。跟其它任何动态查询一样,在编译该查询之后,它将以已编译的形式保留在包高速缓存中。在下一次执行相同的查询时,仅当无法在高速缓存发现它时,DB2 才重新编译它。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。