参数 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 不会获取那些锁。
重写隔离(WITH 从句)
DB2 UDB 允许应用程序里的 SQL 语句指定所绑定计划或包的隔离级别。这将有效地重写用于计划或包的隔离级别,但仅仅是在它出现的语句中。WITH 从句可以用于下列类型的 SQL DML 语句:
- SELECT
- “搜索的”UPDATE 和 DELETE
- 子查询中的 INSERT
ISOLATION 建议
合适的 ISOLATION 选项需要通过应用程序的业务逻辑和数据完整性需求来决定。该决定需要对应用程序需求和相关的业务规则具有基本理解。
通常,您应该使用最少的限制隔离,它仍将维护应用程序的数据完整性需求。隔离的限制越少,数据的并发性就越高,因而访问 DB2 UDB 数据的性能就越好。通常,ISOLATION(CS) 与 CURRENTDATA(NO) 将是一个较好的起始点。ISOLATION(CS) 允许 DB2 UDB 尽快释所获取的锁,而 CURRENTDATA(NO) 允许 DB2 UDB 避免过于频繁地获取锁。
未提交读(Uncommitted Read,UR)极其高效,仅导致少量竞争或不导致竞争,但是,它的使用应极为谨慎,因为它使 DB2 UDB 能够读未提交的数据。一般不推荐使用 ISOLATION(UR),除非已经确定应用程序和终端用户可以接受 UR 所允许的逻辑数据不一致性。
锁避免是在 DB2 V3 中引入的并发性提高。它使 DB2 UDB 在确定某页中的数据已经提交之后,就能够读取该页面,而无需获得锁。是否可以使用锁避免取决于应用程序在其 SQL 调用中使用的隔离程度。
ISOLATION(RR) 和 ISOLATION(RS) 不允许任何锁避免。带有 ISOLATION(CS) 的游标对非限定的页面或行使用锁避免。带有 ISOLATION(CS) 和 CURRENTDATA(NO) 的歧义游标和只读游标对限定的和非限定的页面或行均使用锁避免。
LOCKMAX 是一个可以在 CREATE 或 ALTER 表空间时指定的选项。这指定在进行锁升级之前,单个应用程序可以持有的 DB2 UDB 表空间上的页面锁或行锁的最大数目。该选项可以同时对用户数据和 DB2 UDB 编目生效。
如果 n表示发生锁升级之前页面锁或行锁的最大数目,那么就指定 LOCKMAX n。通过指定 LOCKMAX 0 可禁止锁升级。指定 LOCKMAX SYSTEM 是将最大数目设置为系统默认值,而系统默认值是由安装面板的 DSNTIPJ 上的选项 LOCKS PER TABLE(SPACE) 设置的。
DB2 UDB 还允许设置单个应用程序持有的页面锁或行锁的最大数目。该选项 LOCKS PER USER 也位于安装面板 DSNTIPJ 上。如果一个应用程序进程已经持有所指定的最大数目,但又向 IRLM 请求一个页面锁或行锁,那么 DB2 UDB 就会向该应用程序的 SQLCA 发送一条错误消息。直到释放某些现有的锁之后,才可以获取所请求的锁。
默认的 LOCKS PER USER 为 10,000。当使用页面锁时,该数字对于大多数装置的多数工作负载来说通常已经足够了。对于极其大型的表上的行级锁或 LOB 来说,可能有必要指定一个更大的值。或者,您可能需要查看需要更大值的应用程序类型,并分析它们是否可以使用表空间锁来代替页面锁、行锁或 LOB 锁。
除了上述关于数据库和应用程序设计的建议之外,这里还有一些关于安装选项和系统环境的建议和考虑:
某些 SQL DDL(数据定义语言)、SQL DML(数据操作语言)和 SQL DCL(数据控制语言)语句获取 DB2 UDB 编目上的锁。如果多个应用程序进程都发出这些语句,就可能真正发生编目竞争。
我建议您尝试最低限度地并发使用更新同一 DB2 UDB 编目表空间的语句。通常,需要设置关于何时应提交这种 SQL 的标准,并且/或者限制可以更新该 DB2 UDB 编目的授权用户 ID 数目。关于可能导致编目竞争的 DDL、DML 和 DCL 的详细描述,请参阅 DB2 UDB Administration Guide。
应该给予 IRLM 极高的 MVS 调度优先权,以便它可以尽快为锁请求提供服务。IBM 通常建议 IRLM 的优先权仅次于 VTAM 的,但要高于其他 DB2 相关的地址空间。
面板 DSNTIPJ 上的 CROSS MEMORY 条目决定了 IRLM 是否在 ECSA(扩展的控制存储区)或它自己的私有地址空间里存储其锁控制块。
指定 NO(PC =NO,这意味着不使用跨地址空间的程序调用)会将锁结构置于 ECSA 中,而且只需要较少的 CPU 时间,但是,却可能减少使用 ECSA 的其他 MVS 地址空间可用的存储器数量。通过该选项,另一参数 MAXIMUM ECSA 也将起作用。这将决定 IRLM 可用于其锁结构的 ECSA 的最大数量。重要的是,要将该值设置得足够高,以防止 IRLM 到达极限。IRLM 仅仅按需获取存储器,因此,较好的是设置一个比估计需要值要大的值。
通过指定 YES,锁控制块结构将存储于 IRLM 私有地址空间中,而程序调用指令(PC=YES)则用于处理该结构。如果有 PC=YES,则会忽略 MAXIMUM ECSA 选项。
目前,DB2 UDB V7 的经验显示 PC=YES 为推荐设置,因为虚拟地址空间约束的影响被认为比通过选择该选项而消费的附加 CPU 周期产生的影响更大。从 DB2 UDB V8 开始,就不再使用 PC 和 MAXIMUM ECSA 键(V8 自动实现 PC=YES)。
如果您的 MVS 系统资源由于工作负载过重而较为紧张,那么 CPU 周期、I/O 处理和存储器上增加的竞争则可能导致任务陷入等待状态,直到可以获得所需的资源。
同样明智的是,在您的 MVS 系统中根据实际情况减少“交换”量。当 MVS 分页率变得过高时,某一地址空间就可能被“换出”,即将地址空间中的所有虚拟存储器移至磁盘中。每当一个任务被换出,或等待其他系统资源,而该工作单元还未提交,那么该任务就继续持有 DB2 UDB 对象上的锁,这将影响并发性。
参数 DEADLOCK TIME 指定 DB2 UDB 在系统中扫描死锁的应用程序进程的时间间隔长度。该参数是在安装面板 DSNTIPJ 上指定的,其默认值为 5 秒。
正如所期望的,死锁检测的非常好的值的确定取决于您 DB2 UDB 应用程序工作负载的特点和大小。因为死锁检测可以导致锁存器暂挂,所以如果您的系统未经历死锁,那么可以考虑使用默认值。死锁检测的检测不那么频繁则意味着 DB2 和 IRLM 所花的处理时间将会少一点。但是,如果您经历了一定程度的死锁,则应该将该值减小至 1 秒(您需要尽快发现死锁情况)。
参数 RESOURCE TIMEOUT 是在安装面板 DSNTIPI 上指定的,它规定了在发生超时之前 DB2 UDB 的最小秒数。默认值为 60 秒,这可能是新的 DB2 UDB 安装的较好起始点。因为您应用程序的性能特点会随时间发展并改变,所以您需要评估该参数值是否有效地维护较好的并发性。较小的值可能导致更多的超时。通过设置一个较大的值,暂挂的应用程序通常会更为频繁地重新开始,但是在更长的期间内,它们仍然是非活动的。
此外,这里有一个虚拟场景,演示了如何可以减小该值来真正减少超时,即使该思想与开始的直觉相反。假设 RESOURCE TIMEOUT 为 60 秒。
- 事务 A 得到页面 1 上的锁,但是随后陷入等待状态,因为它请求页面 2,而页面 2 当前正被事务 X 锁定。
- 事务 B 在 1 秒钟之后启动,得到页面 3 上的锁,但是随后陷入等待状态,因为它请求页面 1。
- 事务 C 在 1 秒钟之后启动,得到页面 4 上的锁,但是随后陷入等待状态,因为它也请求页面 1。
- 1 秒钟之后,事务 D 和 E 分别请求页面 3 和 4,并且陷入等待状态。
- 事务 A 等待了 59 秒,然后最终获得了对页面 2 的访问,但是它在结束处理之前,还要继续持有页面 1 上的锁 5 秒钟。
- 因此,其他所有 4 个事务 B-E 都超时了。
如果 RESOURCE TIMEOUT 为 30 秒,那么事务 A 在获得页面 2 之前就会超时,但这样可以释放页面 1,以致事务 B 到 E 都可以执行。因此,超时的事务将不是 4 个,而是只有 1 个。
安装面板 DSNTIPR 上有一个 IDLE THREAD TIMEOUT 选项。该选项指定不进行任何处理时,活动 DB2 UDB 分布式线程可以持有锁的时间期。在该时期之后,DB2 UDB 扫描进程就检测出该线程已经空闲了一段指定的时间,因此,DB2 UDB 就取消该线程。
参数 RELEASE LOCKS 位于安装面板 DSNTIP4 上,控制在进行提交时,是否释放被定义为 WITH HOLD 的游标使用的页面锁或行锁。默认值为 YES,这意味着 DB2 UDB 在发出 COMMIT 之后,释放页面锁或行锁。您应尽可能多地使用该默认值,因为这将提高并发性。
如果用户指定 NO,那么 DB2 UDB 将在发出 COMMIT 之后,为 WITH HOLD 游标保留该页面锁或行锁。该选项允许当前依靠该锁的应用程序可以像之前一样继续工作。
除了上面提及的关于数据库和应用程序设计考虑的众多事项之外,还有一些与 DB2 UDB 日常操作有关的事情,这些操作可以影响数据并发性水平,特别是在 DB2 UDB 实用程序区域中。
许多 DB2 UDB 实用程序都由于 NPI(非分区索引),受益于分区之间所提高的数据并发性。为了实现这一提高,您需要通过分区并行地运行实用程序,由于并发地执行实用程序,这可能需要新的操作和组织考虑。
LOAD 和 REORG 实用程序都可以极大地从使用该技术中受益。但是,分区依赖性并不有益于 COPY 和 MODIFY 实用程序,因为它们均工作于数据级。
在 DB2 UDB Version 5 之前,由 REORG 实用程序处理的数据访问要遭受多得多的限制。在 REORG 的卸载阶段,其他应用程序受到限制,只能对该表空间进行只读访问,而在再装入阶段,根本不能对该表空间进行访问。在 DB2 UDB V5 中获得 REORG 实用程序增强之后,通过指定 SHRLEVEL 选项,极大地提高了数据的可用性。这称作“联机”REORG。
通过在 REORG 实用程序作业中指定 SHRLEVEL REFERENCE,其他应用程序几乎在整个作业时期中都可以只读访问该数据。在 REORG 中指定 SHRLEVEL CHANGE 将允许其他应用程序几乎在整个重组过程中读/写访问该数据。
就在该实用程序完成之前,有一段极短的时期将拒绝数据访问。多数客户经常利用联机 REORG 来使得其他应用程序可并发地使用关键的业务数据,并且同时执行必要的数据维护。通过联机 REORG 实用程序,DB2 UDB 为用户提供了虚拟连续可用性方面的极大进展。
在调用联机 REORG 时,您可能会经历死锁。如果真的发生了,那么您应该运行带有 DRAIN ALL 选项的 REORG 实用程序。DRAIN WRITERS 是默认的选项,在日志记录阶段执行。DRAIN ALL 选项指示 DB2 UDB 一到达 MAXRO 阈值,就同时放弃(drain)写入者和读取者。在日志记录阶段经历过较大更新活动的环境中,应特别考虑该方法。若指定了 DRAIN ALL,那么在联机 REORG 的切换阶段,就无需要进行放弃(drain)。
您应知道您的 DB2 UDB 系统所具有的数据并发性水平。应用程序或数据库设计方面的不足、受约束的硬件资源、渐增的事务量、数据库增长和其他因素都可以导致性能和并发性的降低。
首先,要奠定“基线”- 换言之,就是要奠定在您的环境中用于应用程序和数据库的标准。监控量(volume)以及异常或错误条件成为识别和解决并发性问题的关键。
通过在发出 DISPLAY DATABASE 命令时包含 LOCKS 选项,DB2 UDB 将在特定时刻及时地显示大量关于持有或等待哪些锁的信息。对于显示(DISPLAY)的 DB2 UDB 数据库(或是其中的一部分)来说,该信息包括:
- 用于所有表、表空间、分区和索引空间(indexspace)的事务锁,以及这些锁的模式和持续时间。
- 当前持有、等待或保留锁的应用程序和 DB2 子系统。
- 用于分配给数据库的所有应用程序的授权 ID、相关 ID 和连接 ID 的信息。
- 关于逻辑分区和当前执行的实用程序作业所持有的其他资源的 drain 锁信息。
如果发出 EXPLAIN 语句,或者如果在 BIND 和 REBIND 命令中包含 EXPLAIN 选项,就可以决定表和表空间锁的模式(Share、eXclusive 等),而这些锁最初是 DB2 UDB 为特定的 SQL 语句指派的。EXPLAIN 的输出存储在 PLAN_TABLE 中,用户然后可以进行查询。表空间的初始锁模式包含在 PLAN_TABLE 的 TSLOCKMODE 列中。
为了最大化环境中的数据并发性,回答下列问题将有助于识别模式、趋势和问题:
- 哪些应用程序竞争资源?
- 对于哪些表和/或表空间来说,存在竞争?
- 竞争的类型和频率是什么?
- 那些竞争有何影响?
您应运行统计(Statistics)、记帐(Accounting)和跟踪(trace)功能来记录关于 DB2 UDB 应用程序性能特点以及整个 DB2 UDB 系统的信息。这些跟踪应该连续运行,这样您就可以定期创建报表来查看和分析数据。这项报表编制功能可以用 IBM 的 DB2 Performance Expert(DB2/PE)或类似的程序来完成。
统计(Statistics)跟踪记录报表时间间隔中发生的锁定和解锁请求、声明和放弃请求、锁暂挂、死锁、超时和锁升级的数目。记帐(Accounting)跟踪记录在应用程序(而非³»统)级别上跟踪相同类型的信息。此外,这些跟踪显示了整个时间间隔期间,以及持有和获取的并发页面锁的最大数目。
从并发性观点来看,您应该查看这些报表来帮助您识别下列状况:
- 锁升级通常视为是不理想的,一般是由持有大量页面锁、行锁和 LOB 锁的应用程序导致的。对于这些情况,通过一开始就使用表锁或表空间锁可以提高并发性和整个系统性能,因此,要避免升级发生。
- 超时可能是由一个对于您的环境来说并非非常好的的 RESOURCE TIMEOUT 值导致的。如果存在大量超时,则可能是该值过低。较高的 RESOURCE TIMEOUT 值可以为更多应用程序提供完成其工作的机会。但是,要密切监控您所做的修改,因为通过减小该值(请参阅子标题 RESOURCE TIMEOUT 下面的讨论),可以改善某些状况。
- 死锁可能是性能和并发性的主要障碍。通常应进一步调查这些东西,以便同时确定导致障碍的原因以及可能的解决方案。
- 某些应用程序一贯地持有大量锁。请查看这些应用程序(以及底层的数据库设计),以判断是否可以降低该数字。
理想情况是,装置争取获得死锁和超时的“零”计数,最多具有少量的锁升级。如果在经过有限的少量事件之后,发生了这三种征兆之一,那么可能应该调优您的锁定了。
关于锁升级,一条经验法则主张如果有超过四分之一的锁升级最终导致死锁或超时,那么升级就不适用于您的环境(请参阅上面关于锁升级的讨论)。要考虑增加 LOCKMAX 或 LOCKS PER TABLE(SPACE)。另一种可能的解决方案就是使受影响的应用程序在初始化时发出 LOCK TABLE。
另外,监控锁定/解锁请求、暂挂和声明/放弃请求的总数目。监视那些数量趋势(预期的或其他的)是在 DB2 UDB 环境中维护高水平的数据并发性的关键。
特定页面或行被锁定的概率可能有较大变化,这取决于数据库设计、应用程序代码、事务量、可用的系统资源(例如 CPU、内存、I/O 等)和许多其他的因素。作为一般的经验法则,如果一页或一行被锁定的时间超过 10%,就认为它是“热的(hot)”。因此,10% 可视作合理的报警极限。少于 10% 通常视为是可以接受的,而大于 10% 则可能暗示竞争过多,并且可能应进行调查。
在多数环境中,锁的平均持续时间少于 1 秒钟。如果您装置的锁平均持续时间大于 1 秒,可能就必须进一步进行分析了。
以下几段强调了运行 DB2 UDB 数据共享(Data Sharing)环境时,应该查看的信息。某些讨论仅仅与数据共享有关,而某些材料则可能适用于通常状况下的 DB2 UDB(甚至在前面可能已经提及),但对于数据共享环境中的考虑特别重要。
任何按顺序执行 INSERT 的批处理应用程序都可能导致表空间的空间映射页面上的严重竞争。对于这样的应用程序,在创建用于它们的表空间时要考虑使用参数 MEMBER CLUSTER。通过该选项,DB2 UDB 在为 SQL INSERT 语句指派空间时,就不考虑群集索引。因此,DB2 UDB 将基于可用空间,将插入置于数据库中,这意味着不只是一次或几次访问不同的空间映射页面。这将极大地减少对于空间映射页面的竞争。
DB2 UDB V5 引入了选择性的分区锁定,这意味着仅仅锁定要访问的分区。这是通过在 CREATE 或 ALTER TABLESPACE 语句中指定 LOCKPART YES 选项来完成的。
该功能主要使用在数据共享的环境中,允许 DB2 在分区级别上支持显式的层次结构锁定。选择性的分区锁定为 L 锁提供了真正的分区独立性,因而当数据共享组中的每个成员处理不同的分区时,避免了 IDRWI(DB2 内部读/写兴趣)。在 DB2 UDB V5 之前,表空间的父 L 锁定是在表空间级别上的,甚至对于分区的表空间也是如此。
在 DB2 数据共享组中,建议进行下列实践,用以最大化锁避免,并因此提高并发性:
- 在运行于分组里任何成员中的所有应用程序进程中进行频繁提交。
- 停止(Quiesce)分组中非活动的成员。
- 快速地重新启动失败的成员,以便解决处于质疑中的事务。
- 在可能时对计划和包使用 CURRENTDATA(NO)。
对于数据共享系统,我们极力建议使用 LOCKSIZE PAGE。在使用 LOCKSIZE ROW 之前,用户应仔细分析数据库设计和将访问该数据的应用程序进程的本质。数据共享的 DB2 UDB 系统由于页面物理锁(P-lock)竞争和协商,招致了行锁定的附加开销。通过较高的事务速率,应用程序在页面 P 锁之后,趋向于串行化。因此,要按照设计默认值使用页面锁定。
DB2 数据共享环境中有 3 种不同类型的竞争:
-
假(False)竞争- 单个 DB2 UDB 成员计算一个哈希(hash)值,用于确定将资源指派给哪个锁表条目。锁表存储在耦合设备中。此哈希值是通过使用资源名称,以及惟一地定义将要锁定资源的相关信息来计算的。虽然特定的资源总是产生一个(且只有一个)值来确定它在锁表中的位置,但极其可能有不止一个资源将散列到相同的锁表条目,该状况称作假(false)竞争。
-
XES 竞争- 耦合设备中的锁管理器和 XES 只承认两类锁模式 - 共享(S)模式和排他(X)模式。但是,IRLM 支持许多其他的模式,例如意图共享(intent-share,IS)、意图排他(intent-exclusive,IX)和意图排他的共享(share with intent-exclusive,SIX)。该情况导致从 ERLM 传递到 XES 时,将缩减锁模式。如果 XES 接着试图注册一个与现有锁不兼容的锁,XES 就必须检查 IRLM,以便更精确地确定所需的锁类型,以及这一锁请求是否真的兼容。如果该调查确定那些锁实际上是兼容的,那么就仅仅为 XES 竞争。如果那些锁实际不兼容,那么该竞争就是真的。
-
真(Real)竞争- 这是一种“传统的”竞争,通常由两个应用程序之间 IRLM 不兼容性导致,这在非数据共享的环境也会发生。
通常建议的经验法则显示所有锁请求会导致竞争(真的或假的)应低于 2%。应对导致多于 2% 竞争的应用程序进程进行进一步的调查,用以确定这些竞争是真的还是假的。
锁结构的大小可以影响数据共享环境中遭受的 false 竞争的数量。如果有更多锁表条目,那么两个不同资源“散列”到同一条目的机会就更少。因此,通常可以通过增加耦合设备中锁结构的大小来减少假竞争。如果所有竞争中超过一半为假竞争,我们就建议您扩展锁结构。
如果大多数竞争是“真的”,那么应评估所熟知的数据库和应用程序设计原则,看是否适用于该情况。下列动作通常有助于减少实际竞争:
- 对表访问和更新使用一致次序。
- 优化事务以减少整个经过时间。
- 频繁地增加 PCTFREE 和运行 REORG 实用程序。
- 将跟新推迟到就正提交之前进行。
- 评估是否使用 LOCKSIZE ROW。