AS400数据库事务处理机制
- 记录锁等待与程序处理机制
在多作业并发处理中为了保证事务的完整性,数据库通过记录锁、表锁来确保数据的正确性。一个作业在获得一条记录锁时,如果此记录已经处于锁状态,那么当前作业会如何处理呢?一般来说如果一个作业获得锁时未成功(已经被锁),那么此作业不是立即做异常处理,而是要等待一段时间,即锁等待。锁等待的时间长短主要是根据数据库的参数来确定,在AS400中锁等待的时间是可以指定到表,一般的开放平台数据库(DB2/ORCLA)是指定到数据库的。在AS400平台上锁等待时间是在编译文件(PF,LF)时指定。
如下:编译物理文件和逻辑文件时选择等待时间。
一般程序中获得记录锁时需要判断记录是否被锁。
例如:
这些都是比较常用的,但这里再强调一下,出现锁不是立即进入程序的异常处理分支,而是需要等待一个锁时间后才会进行异常处理分支。如果一个程序想在出现锁时立即就进入异常处理分支,那么首先应该修改所读取表的编译参数-锁等待时间(选择合适的等待时间),并在程序中增加指示灯或(E)方式才能捕获锁锁异常,如果程序中读取数据时没有进行异常处理,那么作业将处于MSGW状态。
- AS400数据库事务隔离级别机制
在多事务的并发处理中不同的作业(进程、线程)可能对同一条记录做增、删、改、查。在这些并发操作中常出现脏读、不可重复读、幻读这几类问题,为了解决上述的问题,数据库提出事务隔离级别,以此保证数据的正确性。在SQL关于事务隔离级别的标准中有未提交(Read Uncommitted)、读提交(Read Committed)、可重复读取(Repeatable Read),序列化(Serializable)四个级别。这四个隔离级别设置依次解决脏读、不可重复读、幻读问题。详细知识请参考数据库原理中关于事务隔离的讲解。
AS400中关于数据库的隔离级别的控制与SQL标准略有不同,就是AS400中只设置了三个隔离级别,分别是*CHG、*CS、*ALL。本文主要介绍一下*CHG和*CS使用中需要注意的地方,对于*ALL级别由于使用场景很少,所以不做介绍。
- *CHG
详细解释说明请参照AS400帮助说明,这里不做概念解释。下面从以下几方面说明事务级别在*CHG模式下的一些注意点。
- 产生锁的时机
- 在*CHG模式下只要F表是以“U”方式定义,那么在做读取操作时都会产生锁。例如:
(程序1)
锁是在“0009”(CHAIN)行产生锁。
注:只要以“U”方式定义的表在读取(READ/CHAIN)时产生锁。SETLL/SETGT不会产生锁。
- 应用中需要注意的地方
数据表(GACC)中存在4条记录,其中GLLTYP值分别是1,2,3,5。如果另一个作业已经将表中GLLTYP值为5的记录锁住,那程序3在做READE时将出现记录锁等待(0029行)。
(程序3)
注意:定位后做READ/READE时,即使没有读取到相同键值的记录同样会产生锁等待。由此说明操作记录时根据表的定义首先要获取锁,然后再判断键值是否相同。
- 释放锁的时机
以“U”方式打开的表,在做读取时会对记录加锁。表在定义时有COMMIT控制和没有COMMIT控制主要区别在于锁的释放点不一样。例如一下程序:
(程序2)
注:记录锁是在第“0013”行产生锁,直到作业做COMMIT/
ROLLBACK时记录锁才释放。
上例中F表定义时如下(未加COMMIT)
注:如果F表以这种方式定义,那么上述例子中锁是在“0013”(CHAIN)行产生,在“0019”(UPDATE)之后释放。作业退出/回收作业资源(RCLRSC)/SETON/CLOSE同样会释放锁。
- 锁的产生与操作符的关系
操作符与锁的关系:
操作符 | 打开方式 | 事务定义 | 是否产生锁 |
SETLL/SETGT | I |
| × |
U |
| × | |
I | COMMIT | × | |
U | COMMIT | × | |
READE/READ | I |
| × |
U |
| √ | |
I | COMMIT | × | |
U | COMMIT | √ | |
CHAIN | I |
| × |
U |
| √ | |
I | COMMIT | × | |
U | COMMIT | √ |
- *CS
下面介绍一下在*CS模式下的一些注意点。
- 产生锁的时机
- *CS模式下与*CHG模式一样只要以“U”方式定义的表都会在数据读取时产生锁(获取锁)。产生锁的时机与*CHG模式下类似,但注意在*CS模式下SETLL/SETGT也会产生锁,这点与*CHG模式不一样。
- *CS模式下以“I”方式打开的表,定义时如果有COMMIT控制,那么表读取时同样会产生锁。注意SETLL/SETGT/READ/READE/CHAIN都会产生锁。
- 释放锁的时机
以“U”方式或以“I”方式打开,并且定义时增加COMMIT控制,在做读取或定位时都会对记录加锁。其中,以“U”方式打开并且有COMMIT控制的,在执行COMMIT/ROLLBACK时释放锁;以“U”方式打开,但没有COMMIT控制,即使执行COMMIT/
ROLLBACK,锁依然存在,直到作业退出/执行UPDATE/回收作业资源(RCLRSC)/SETON/CLOSE。
程序参照程序1和程序2。
- 锁的产生与操作符的关系
操作符与锁的关系:
操作符 | 打开方式 | 事务定义 | 是否产生锁 |
SETLL/SETGT | I |
| × |
U |
| √ | |
I | COMMIT | √ | |
U | COMMIT | √ | |
READE/READ | I |
| × |
U |
| √ | |
I | COMMIT | √ | |
U | COMMIT | √ | |
CHAIN | I |
| × |
U |
| √ | |
I | COMMIT | √ | |
U | COMMIT | √ |
- 事务处理机制
事务的提交与回滚比较常用,这里主要介绍一下做ROLLBACK操作时需要格外注意的地方。
- 游标回滚与什么有关系
只要F表定义时增加COMMIT控制,那么做ROLLBACK时游标一定会回滚。注意不管表是以“I”方式打开或以“U”方式打开做ROLLBACK,游标一定会回滚,这点尤其需要注意,后面通过例子说明。
- 回滚后游标的位置
事务ROLLBACK后,相关的游标回滚的位置在哪里呢?简单说就是回滚到上条记录位置。比如做READ操作多次后,中间如果做了ROLLBACK操作,那么回滚后的游标应该定位到当前记录的上一条记录的位置。
- 应用中需要注意的地方
- 回滚游标错位
数据表中GLLTYP值为“4”的有5条。以上程序如果在I=2时,或UPDATE时出现ERROR,将会出现记录被重复更新的情况。主要原因是做了ROLLBACK后GRCC1游标虽然是“I”方式打开,但同样会回滚到上一条记录。
注意:不管是以“U”方式或“I”方式打开表,只要有COMMIT控制回滚后游标将定位到上一条记录。