技术开发 频道

DB2通用数据库的并发性

    使用何种大小的锁

    在 DB2 V4 中才开始可以使用行级锁。之前,数据页是最小的锁定单元。I/T 行业中的许多人都假定行锁是并发性问题的灵丹妙药,但实际上,它并不能解决所有的并发性问题。在经历许多锁等待、死锁和超时的环境中,它也许提供了较大的改进。但在其他情形下,DB2 UDB 可能在获取更多锁上消耗资源,同时无法成比例地提高并发性。

    因为 IRLM 获取、维护和释放行锁所需的处理与页面锁需要的大致相同,所以关于指定锁大小的决定其实就是在较高的锁定开销与并发性的潜在提高之间进行权衡。

    因此,至于是使用页面锁还是使用行锁,这取决于您的数据和应用程序的特点。如果您觉察到使用页面锁定级别的表空间的数据页上存在大量竞争,那么就请考虑使用行锁。通过在行级别而非页面级别上进行锁定,可以极大地减少与其他应用程序进程的竞争,特别在访问是在随机的情况下。

    但是,如果多个应用程序正以不同的顺序更新某一页上的相同行,那么行锁导致的竞争甚至可能比页面的还要多。这是因为,通过页面锁,第二个以及随后的应用程序在访问该页面之前,都必须等待第一个应用程序完成,而它们就可能超时。通过行锁,多个应用程序可以同时访问同一页上的行,但如果它们试图访问相同的行集,就可能死锁。

    使用 LOCKSIZE TABLESPACE 或 LOCKSIZE TABLE 之前,用户必须相当确定没有其他重要的应用程序进程需要并发访问该对象。在任何表上指定 LOCKSIZE ROW 之前,极为明智的做法就是先对增加锁开销来提高并发性是否值得进行估计。

    小型表

    如果一个 DB2 UDB 表兼具尺寸小和高使用率的特点,那么就考虑使用 LOCKSIZE ROW,特别是每个页面中有许多行的时候。此外,因为表十分小,锁粒度将不会给锁开销带来较大的性能影响。

    DB2 UDB 对象和授权的分组

    通常总是将与同一应用程序逻辑相关的表分组到一个数据库中。理想情况是,每一个应用程序进程都引用尽可能少的 DB2 UDB 数据库。而且,要设法给用户不同的授权 ID 来使用不同的 DB2 UDB 数据库。实际上,这将增加可能应用程序进程的数目,同时可能减少每个应用程序进程可以访问的数据库数目。因此一般说来,“组合相似的事物”和“分开不同的事物”。

    应用程序设计考虑

    正如初始数据库设计一样,早在开发新的 DB2 UDB 应用程序之时就开始考虑并发性问题也很重要。更好的做法是,在实现之前,就将并发性原则包含到生产中去。然而,有时候并发性的提高可能取决于较新的 DB2 UDB 版本中的新功能以及与之接合的其他系统。在这些情况下,可能需要修改现有的应用程序,以便提高并发性水平。而在其他情况下,可能要等并发性问题发展和增长到一定的时候,才有必要对应用程序进行大量修改。以下是一些用以评估在某一时刻是开发新的 DB2 UDB 应用程序,还是修改现有的 DB2 UDB 应用程序的选项。

    尽可能晚地访问关键资源

    例如,设计和编写应用程序,以便 SQL 语句获取和更新 DB2 UDB 数据的时间尽可能地接近提交点。注意该问题将减少锁定数据的时间总量,从而减少其他应用程序进程无法使用该数据的时间。

    尽可能快地提交工作

    通常,在达到数据一致性临界点之后,尽快发出 COMMIT 语句是一种较好的 SQL 编程技术。该实践将有助于避免不必要的锁竞争,甚至在只读应用程序中也应该采用该实践。类似地,我们建议应用程序中一检测出 SQL 故障,就发出 ROLLBACK 语句。这将阻止未成功的 SQL 语句不必要地、过长时间地锁定 DB2 UDB 资源。

    尽可能快地关闭游标

    尽可能快地关闭游标是另一个可以帮助提高并发性的编程实践。通过尽快发出 CLOSE CURSOR 语句,应用程序将释放那些锁以及它们所持有的 DB2 UDB 资源。

    应用程序重试逻辑

    我建议应用程序中包含重试逻辑,即当程序接收死锁或超时状况的指示时,该应用程序代码应重试该操作,甚至允许重试多次。这就使得应用程序有可能从该状况中恢复,而无需操作人员从外部干涉或提供帮助。SQLCA 中的字段 SQLERRD(3) 返回一个理由码,指示是否发生了死锁或超时。

    以一致次序访问数据

    在多数环境中,时常会发生不同的应用程序需要访问同一数据的情况。要在您的环境中规定应用程序以相同的次序访问数据。例如,要求所有应用程序按升序访问表 XYZ 中各行。如果那样,第一个访问表 XYZ 的应用程序就可能会延迟其他应用程序,但不会导致死锁的状况。以类似的方式,所有访问表 A、B 和 C 的应用程序都应按字母顺序来进行访问。

    增加联机并发性

    减少死锁和超时的一种方法就是按照上面所描述的,在整个系统中按照一致次序来访问 DB2 UDB 数据。而第二种方法则是在 UPDATE 和 DELETE 上使用 WHERE CURRENT OF 游标选项,而不是使用独立的 UPDATE 和 DELETE 语句。在争取获得联机 DB2 UDB 数据的最大并发性时,考虑的第三种选择就是在非活动或非高峰 OLTP 时期,尝试为联机表计划批处理活动。

    参照约束

    在 DB2 UDB 数据库中使用参照约束可以带来性能影响。父表中某行上的 DELETE 语句需要获取子表(或者,至少是其索引)上的锁。那么,那些子表的并发使用的可用性会由于那些锁而减小。该影响对于级联删除可能相当大,因为访问单行的单个删除操作可能需要扫描多个表中的多行数据。

    您经常可以通过在外键上创建索引来减小性能影响。虽然并不强制外键上有索引,但是若不存在该索引,那么当从父表中删除一行时,DB2 UDB 就必须扫描整个子表。

    可以潜在地改进该状况的另一个动作必须与组合相关表有关。如果频繁删除父表中的行,则可以考虑将所有的子表(以及它们的子表)置于同一表空间中。

    BIND 选项

    将“源” SQL(实际上就是准备好的 DBRM)转换成可执行代码的过程称作绑定,在某些方面类似于 COBOL 等过程语言的编译/链接-编辑过程。在绑定 SQL 代码时,可以指定许多选项,而这些选项可以极大地影响应用程序将经历的并发性程度。下面讨论了一些影响并发性的重要选项。

    ACQUIRE

    参数 ACQUIRE 决定 DB2 UDB 用以获得应用程序所请求数据上的锁的时间。ACQUIRE(ALLOCATE) 指示 DB2 UDB 在分配应用程序计划时获取所有所需的表空间锁,但 ACQUIRE(USE) 将使 DB2 UDB 等到应用程序首次向该表发出 SQL 语句时,才获取那些锁。

    您通常应该用 ACQUIRE(USE) 选项(这是包绑定的默认选项)来绑定计划。那通常也是并发性的非常好的选择。ACQUIRE(ALLOCATE) 选项可以更好地保护优先级极高的作业免于死锁。为了偶尔适当地使用该选项,您可能需要考虑将所有 DBRM 直接绑定到该计划。

    RELEASE

    DB2 UDB 释放它所持有的锁的时间是由 RELEASE 选项决定的。通过 RELEASE(COMMIT),DB2 UDB 指示 IRLM 在到达 COMMIT 点时释放锁,而通过 RELEASE(DEALLOCATE),DB2 UDB 将保留所有锁,直到终止程序并释放计划。

    虽然不一定适用于所有状况和环境,但通常的建议是将 RELEASE(COMMIT) 用于 OLTP 应用程序,以及其他请求相对少量锁和应用程序进程的经过时间相当短的应用程序。对于批处理应用程序进程以及其他获取大量锁且/或执行时间长的应用程序进程来说,RELEASE(DEALLOCATE) 通常为更好的性能选项。

    ACQUIRE/RELEASE 组合

    对于 BIND 的 ACQUIRE/RELEASE 选项的通用建议是基于应用程序的特点选择组合值。有一种组合,即 ACQUIRE(ALLOCATE)/RELEASE(COMMIT) 是不允许的。DB2 BIND 进程会将该种说明标记为错误。其他三种可能的组合都有其合适的用法,下面将简要地进行描述。

    ACQUIRE(ALLOCATE)/RELEASE(DEALLOCATE)
    ACQUIRE(ALLOCATE)/RELEASE(DEALLOCATE) 可能对于运行几小时并访问不同表的批应用程序十分有用,因为该组合将有效地阻止死锁状况损耗一切处理。一开始运行程序,应用程序就会锁定所有所需资源。

    该组合的不足就是它可能严重地减小并发性。 潜在地具有较高需求的资源的锁定时间可能比实际所需要长。同时,请注意 ACQUIRE(ALLOCATE) 禁用选择性的分区锁定,以至于如果您访问用 LOCKPART YES 定义的表空间,将会锁定所有分区。

    ACQUIRE(USE)/RELEASE(DEALLOCATE)
    ACQUIRE(USE)/RELEASE(DEALLOCATE) 组合通常导致最为高效地使用处理时间。DB2 UDB 资源仅在应用程序需要它们时才被锁定。这样就减小了锁开销,因为在那之后,会将所获取的锁保留到终止应用程序之时。

    该组合的不足就是相较于 ACQUIRE(ALLOCATE) 时,它可能导致更频繁地发生死锁状况。只有在实际的应用程序运行时,才按可预测的次序这样来作用所获取的锁。因此,可能发生更多死锁。

    ACQUIRE(USE)/RELEASE(COMMIT)
    ACQUIRE(USE)/RELEASE(COMMIT) 碰巧就是 BIND 的默认组合,提供了非常好的的并发性。DB2 UDB 资源仅在应用程序需要它时才被锁定,如果该应用程序包含许多不常使用的 SQL 语句,这就可能极其重要。

    然而,其权衡就是,如果应用程序相当频繁地提交,就可能增加处理时间,那么该应用程序就必须再次重新获取锁。与上面的 ACQUIRE(USE)/RELEASE(DEALLOCATE) 组合一样,也可能增加死锁,因为只有在执行实际的程序时,才按可预测的次序获取所有的锁。

    ISOLATION

    参数 ISOLATION 决定 DB2 UDB 为保护一个应用程序所用数据,防备该 DB2 UDB 系统里的其他应用程序,而维护的保护或隔离程度。下面完全从并发性角度出发,按照递增的优先级列出了 4 个选项:

    ISOLATION(RR)
    通过可重复读(Repeatable Read,RR),要用行锁或页面锁锁定应用程序所访问的所有行(无论是否为限定的),并至少保持到下一提交点。DB2 UDB 确保:

    您的应用程序不读取另一应用程序进程修改的那一行,直到该进程释放该行,并且
    其他应用程序进程不修改您的应用程序所读取的那一行,直到应用程序进行了提交或终止。

    ISOLATION(RS)
    通过读稳定性(Read Stability,RS),要用行锁或页面锁锁定所有返回给应用程序的限定行,并至少保持下一提交点。DB2 UDB 确保:

    您的应用程序不读取另一应用程序进程修改的那一行,直到该进程释放该行,并且
    其他应用程序进程不修改满足您应用程序的搜索规则(由 WHERE 从句决定)的那一行,直到应用程序进行了提交或终止。它允许其他应用程序进程 INSERT 一行或 UPDATE 现有的行,只要该行原来不满足其搜索规则。

    ISOLATION(CS)
    通过游标稳定性(Cursor Stability,CS),仅仅将行锁或页面锁保留到游标移动到另一行或另一页。因此,DB2 UDB:

    确保您的应用程序不读取另一应用程序进程修改的那一行,直到该进程释放该行,但是
    允许其他应用程序进程在您的应用程序进行了提交或终止之前,修改您的应用程序所读取的那一行。

    ISOLATION(UR)
    通过未提交读(Uncommitted Read,UR),DB2 UDB 不获取任何行锁或页面锁(除了对于 LOB 数据),且可以与大多数其他操作并发运行。在 I/T 行业中,这常常称作“脏读”。通过该隔离级别,DB2 UDB 允许:

    其他应用程序进程修改您的应用程序在工作单元期间可能读取的行。
    您的应用程序读取另一应用程序进程所修改的那一行,即使该修改还未被提交。
    CURRENTDATA

    该选项仅适用于 ISOLATION(CS)。对于与游标稳定性(Cursor Stability)绑定的应用程序中的只读和歧义游标,该选项决定是否需要数据并发性。换言之,它告诉 DB2 UDB 该游标所定位数据是否必须与基表中的数据保持一样(或“相容”)。进一步需要阐明的是,对于歧义游标,DB2 UDB 无法确定是将它用于更新还是将它用于只读目的。

    CURRENTDATA(YES) 指定需要数据并发性,以致 DB2 UDB 将获取页面锁或行锁来确保这一点。CURRENTDATA(NO) 指定不需要数据并发性,因此,DB2 UDB 不会获取那些锁。

0
相关文章