南京二氧化锆烤瓷牙.doc
第 6 部分: 数据并发性
简介: 本
是共包含 9 篇教程的第 6 篇,这个 系列教程 旨在帮助您熟悉 IBM? Informix? Dynamic Server(IDS)的所有方面,以及帮助您准备 IDS 基础认证考试(555)。本教程与考试的第 6 部分对应,您将从中理解到 IDS 中的数据并发性机制。
关于本系列
这个共包含 9 个部分的免费 系列 将帮助您准备 IBM Informix Dynamic
Server 11.50 基础认证考试 555。这个认证将考察关于 IDS 11.50 管理的入门级知识,包括基础 SQL、如何安装 IDS 11.50、如何创建数据库和数据库对象、安全性、事务隔离、备份和恢复
,以及数据复制技术。这些教程为考试的每部分打下了坚实的基础。不过,您不能仅使用这些教程作为唯一的考试准备
。 理解锁
什么是锁,
锁 是一种软件机制,用于控制对数据库中的数据的访问。在出现同时读取和更新数据的多用户环境中,锁能够确保每个事务的原子性、隔离、一致性和持续性(ACID)不受到威胁,并且维护数据的完整性。
锁的粒度
Informix 提供各种粒度的锁。它们是:
, 数据库锁:针对整个数据库的锁
, 表锁:针对整个表的锁
, 页锁:针对整页数据的锁
, 行锁:针对一个数据行的锁
, 字节锁:在包含 VARCHAR 的行上的锁
, 键锁:在索引中的一个键值上的锁
锁的粒度越粗,它就能锁住越多的数据库对象。例如,对于能够在一个磁盘页上包含 4 行的表,在该页放置一个锁将锁住其中包含的所有 4 个行。相反,如果使用行锁,那么将仅锁住一个行。因此,锁的粒度越粗,并发性就越低,从而影响到性能,尤其是应用程序试图访问相同的行集时。不过,粗粒度也意味着在某些情况下锁住相同数量的行需要的锁数量更少。例如,锁住整个表仅需要一个表锁。
回页首
表的锁模式
创建一个表时,它的默认锁粒度是页锁。可以在创建表的过程中使用 LOCK MODE 子句覆盖默认值。例如:
CREATE TABLE t1(c1 int) LOCK MODE ROW;
这个上下文中的锁模式表明访问表时需要使用的锁的粒度。如果从 CREATE SQL 语句省略掉这个 LOCK MODE 子句,那么就使用默认的锁模式。 如果表已经创建,那么可以使用 ALTER TABLE 语句更改锁模式。对于以上提到的表 t1 例子,您可以使用下面的 ALTER 语句将锁模式从行锁更改页锁: dbaccess db1 -
ALTER TABLE t1 LOCK MODE(PAGE);
可以使用配置参数 DEF_TABLE_LOCKMODE 更改已创建的表的默认锁模式。该参数的值可以是 ROW 或 PAGE。例如,如果 DEF_TABLE_LOCKMODE 设置为 ROW,那么数据库服务器重启之后创建的表的默认锁模式将为 ROW。
类似地,可以使用环境变量 IFX_DEF_TABLE_LOCKMODE 实现相同的效果。注意:如果从运行 oninit 以启动服务器的窗口设置环境变量,那么通过环境变量指定的默认锁模式将对所有会话有效。然而,如果仅在特定客户端会话环境中设置锁模式,那么它仅对特定会话有效。
清单 1 - 4 显示了检查表的锁模式的不同方法(表名为 t1;数据库名为 db1):
清单 1. 使用 dbschema
dbschema -d db1 -t t1 -ss
清单 2. 输出
create table "informix".t1
(
c1 integer
) extent size 16 next size 16 lock mode row;
清单 3. 使用 oncheck
oncheck -pt db1:t1
Output:
TBLspace Report for db1:informix.t1
Physical Address 1:64070
Creation date 07/14/2009 04:51:27
TBLspace Flags 802 Row Locking
TBLspace use 4 bit
bit-maps
Maximum row size 4
Number of special columns 0
Number of keys 0
Number of extents 1
Current serial value 1
Current SERIAL8 value 1
Current BIGSERIAL value 1
Current REFID value 1
Pagesize (k) 2
First extent size 8
Next extent size 8
Number of pages allocated 8
Number of pages used 1
Number of data pages 0
Number of rows 0
Partition partnum 1049067
Partition lockid 1049067
Extents
Logical Page Physical Page Size Physical Pages
0 1:64999 8 8
清单 4. 使用 dbaccess 从数据库的系统目录表查询系统表
dbaccess db1 -
> select * from systables where tabname='t1';
tabname t1
owner informix
partnum 1049067
tabid 100
rowsize 4
ncols 1
nindexes 0
nrows 0.00
created 07/14/2009 version 6553601
tabtype T
locklevel R
npused 0.00
fextsize 16
nextsize 16
flags 0
site
dbname
type_xid 0
am_id 0
pagesize 2048
ustlowts
secpolicyid 0
protgranularity
字段 “locklevel” 为 R,表明这是一个行锁。
另外,使用 onstat -k 来监控锁模式。这将在 “使用 onstat 实用程序监控锁”
小节进行介绍。
回页首
锁的类型
数据库、表、页和行(在这个小节中统称为对象)通过不同类型的锁来控制对自
身的访问。IDS 使用以下类型的锁: 共享锁
当需要读取数据并且不允许更改行时,那么就可以在该对象(比如表、页或行)
上设置一个共享锁。一个对象可以由不同的会话设置多个共享锁。因此,可以在
需要时在一个对象上设置多个共享锁。 更新/提升锁
可以在需要更新的对象上设置更新锁。如果一个对象已经设置了共享锁,另一个会话还可以在其上设置一个更新锁。如果需要更改对象,那么更新锁必须提升为独占锁。不过,为了实现独占锁,在提升过程中该对象不能包含共享锁或更新锁。 独占锁
当一个会话请求单独使用某个对象时,可以在该对象上放置一个独占锁。如果一个对象上已经放置了独占锁,那么其他会话就不能再在该对象上放置其他类型的锁。
INSERT、UPDATE 和 DELETE 语句在它们所更改的行上使用这个锁。在事务完成之后将释放该锁。
onstat 实用程序和 syslocks 表使用以下符号表示锁的类型:
, 共享锁 - S
, 更新锁 - U
, 独占锁 - X
在某些情况下,可以通过放置一个意向锁(intent lock)来表明要放置特定锁的意向。例如,意向共享锁的表示符号为 IS。在使用 SELECT 语句期间通常可以在表上看到 IS 锁。在使用 INSERT、UPDATE 或 DELETE 语句期间通常可以在表上看到意向独占锁。
表 1 使用这些符号列出了一个兼容性矩阵,显示当一个会话已经在某个对象上放置了特定的锁时,另一个会话可以在该对象上请求的锁。
表 1. 锁兼容性矩阵
持有 X 持有 U 持有 S 持有 IS 持有 持有 IX 锁 锁 锁 锁 SIX 锁 锁
请求 X No No No No No No 锁
请求 U No No Yes Yes No No 锁
请求 S No Yes Yes Yes No No 锁
请求 IS No Yes Yes Yes Yes Yes 锁
请求 No No No Yes No No SIX 锁
请求 IX No No No Yes No Yes 锁
回页首
锁请求失败
当会话请求锁失败时,数据库服务器的默认行为是返回一个错误。要让该会话等
待拥有锁的会话释放锁,请在运行预定语句之前运行以下 SQL 语句:
SET LOCK MODE TO WAIT;
这将让会话一直等到锁释放。如果要设置固定的等待期限(例如,5 秒),则使
用以下语句:
SET LOCK MODE TO WAIT 5;
回到默认行为:
SET LOCK MODE TO NOT WAIT;
SET LOCK MODE 语句仅在会话级别有效。 我们通过以下例子查看锁模式的行为:
清单 5. 开始会话
dbaccess - -
DROP DATABASE db1;
CREATE DATABASE db1 WITH BUFFERED LOG; CREATE TABLE t1 (c1 int);
INSERT INTO t1 VALUES (1);
清单 6. 会话 A
dbaccess db1 -
BEGIN WORK;
UPDATE t1 SET c1=5;
清单 7. 会话 B
dbaccess db1 -
SELECT* FROM t1;
清单 8. 收到的错误
244: Could not do a physical-order read to fetch next row.
107: ISAM error: record is locked.
清单 9. onstat -g sql 输出
$ onstat -g sql
IBM Informix Dynamic Server Version 11.50.FC4 -- On-Line -- Up 1 days 06:16:34
-- 149504 Kbytes
Sess SQL Current Iso Lock SQL ISAM
F.E.
Id Stmt type Database Lvl Mode ERR ERR
Vers Explain
30 - db1 CR Not Wait 0 0
9.24 Off
输出结果显示 db1 的隔离级别为 “Committed Read”(后面将讨论隔离级别)。因为表 t1 上有一个未提交的更新,错误提示需要使用锁定的记录。 让我们在会话 B 中尝试使用 SET LOCK MODE WAIT 语句:
SET LOCK MODE TO WAIT;
SELECT* FROM t1;
这个会话并没有立即返回错误,而是等待锁释放。
现在,在会话 A 中,完成事务:
COMMIT WORK;
在会话 B 中,现在是等待提交结束之后才返回查询结果。
死锁
当两个会话都持有它们需要使用的锁时就会导致死锁。结果是两个会话都等待对方释放锁。不过,数据库服务器可以检测到这种情况并阻止它发生。例如: T1:读取行 A 并在其上放置一个锁。
T2:读取行 B 并在其上放置一个锁。
T1:在行 B 上请求一个锁。
T2:在行 A 上请求一个锁。
IDS 维护一个内部锁表。为了防止出现死锁,IDS 将检查内部锁表,查看在会话等待锁期间是否会导致死锁。发生的情况如下所示:
T1:将锁模式设置为等待。
T2:将锁模式设置为等待。
T1:读取行 A 并在其上放置一个锁。
T2:读取行 B 并在其上放置一个锁。
T1:在行 B 上请求一个锁;必须等待 T2 在行 B 上释放该锁。 T2:在行 A 上请求一个锁,但被拒绝了,因为等待该锁的释放会导致死锁。 IDS 从内部锁表检测到:
, T1 持有行 A 上的锁
, T2 持有行 B 上的锁
, T1 等待 T2 持有的行 B 上的锁
如果允许 T2 等待 T1 在行 A 上持有的锁,那么将出现死锁。因此,锁请求被拒绝,并返回错误 (-143 ISAM error: deadlock detected)。 检查 onstat -p 的输出看看是否有死锁:
清单 10. 检查 onstat -p 输出中的死锁
$ onstat -p
IBM Informix Dynamic Server Version 11.50.FC4 -- On-Line -- Up 2 days 22:34:37
-- 157696 Kbytes
Profile
dskreads pagreads bufreads %cached dskwrits pagwrits
bufwrits %cached
13851 44301 1894394 99.27 22604 41159 429311
94.73
isamtot open start read write rewrite delete
commit rollbk
1178884 64495 88313 413795 46723 58450 5657
17833 16
gp_read gp_write gp_rewrt gp_del gp_alloc gp_free
gp_curs
0 0 0 0 0 0 0
ovlock ovuserthread ovbuff usercpu syscpu numckpts
flushes
0 0 0 89.23 13.32 366 70849
bufwaits lokwaits lockreqs deadlks dltouts ckpwaits
compress seqscans
191 0 750183 5 0 6
4010 6770
ixda-RA idx-RA da-RA RA-pgsused lchwaits
231 2 3080 3313 254
查看 “deadlks” 字段,您可以看到在数据库服务器上线至今发生了 5 个死锁
事件。如果频繁发生死锁,请检查应用程序并交错使用频繁更改的行。通过减少
锁等待时间可以降低出现死锁的几率。
对于分布式事务,会话在返回错误之前使用 DEADLOCK_TIMEOUT 配置参数等待来
自远程数据库的响应。在 onstat -p 输出中的 “dlouts” 字段显示分布式死
锁超时的统计数据。
日志和非日志数据库
在日志数据库中,数据库服务器记录其事务以及在事务期间持有的锁。一般都能
够识别到事务持有的锁,除非事务没有提交或被回滚。非日志数据库中没有事务,
但仍然使用锁。尽管很容易错过,但是服务器在执行 Data Manipulation
Language 时使用锁,并且在该语句完成之后快速释放锁。
监控锁
onstat 实用程序监控锁
数据库服务器使用的锁保持在一个内部锁表中。使用 onstat -k 命令可以监控
数据库服务器持有的锁的类型和粒度。
onstat -k 命令的输出包含以下字段:
表 2. onstat -k 命令输出包含的字段
字段 说明
锁表中的锁的位置。如果用户线程正在等待这address 个锁,那么该锁的地址就会出现在 onstat -u
(用户)输出的 wait 字段中。
等待锁的用户线程列表的第一项,如果存在的wtlist 话。
持有锁的线程的共享内存地址。这个地址与
onstat -u(用户)输出中的 address 字段中
的地址对应。当持有者值显示在圆括号中时,owner 它表示事务结构的共享内存地址。仅当为全局
事务分配锁时才会发生这种情况。这个地址与
onstat -G 输出的 address 字段对应。
刚才列出的持有者持有的链接锁列表中的下lklist 一个锁。
使用以下符号表示锁的类型:
, HDR - 头部
, B - 字节
, S - 共享
, X - 独占 type , I - 意向
, U - 更新
, IX - 意向独占
, IS - 意向共享
, SIX - 共享意向独占
行标识号码。rowid 提供以下锁信息:
, 如果 rowid 为 0,那么这个锁是一个
表锁。
, 如果 rowid 以两个 0 结尾,那么这个
锁是一个页锁。 tblsnum , 如果 rowid 小于等于 6 位数并且不
以 0 结尾,那么这个锁可能是一个行
锁。
, 如果 rowid 大于 6 位数,那么该锁可
能是一个索引键-值锁。
索引键值号,或为 VARCHAR 锁锁定的字节数。
如果这个字段包含 “K-” 并且其后接一个key#/bsiz值,那么这是一个键锁。该值标识要锁住的索
引。例如,K-1 表示为表定义的第一个索引包
含一个锁。
回页首
onstat -k 命令的示例输出
这个例子使用不遵从 ANSI 的日志数据库和两个表: CREATE DATABASE db1 WITH LOG;
CREATE TABLE t1 (c1 int);
CREATE TABLE t2 (c1 int) LOCK MODE ROW;
以下值被插入到这两个表中:
INSERT INTO t1 VALUES (1);
INSERT INTO t2 VALUES (1);
INSERT INTO t2 VALUES (2);
执行以下语句:
BEGIN WORK;
UPDATE t1 SET c1=5;
UPDATE t2 SET c1=5;
onstat -k 的输出:
清单 11. onstat -k 的输出
$ onstat -k
IBM Informix Dynamic Server Version 11.50.FC4 -- On-Line -- Up 2 days
21:39:44
-- 157696 Kbytes
Locks
address wtlist owner lklist type tblsnum
rowid key#/bsiz
10a3134d8 0 111535cd8 0 HDR+S 100002 204 0
10a44bcd8 0 111535488 0 S 100002 204 0
10a44bd58 0 111536528 0 S 100002 204 0
10a44c4d8 0 111537e18 0 HDR+S 100002 202 0
11348b028 0 111537e18 10a44c4d8 HDR+IX 1001ef 0 0
11348b0a8 0 111537e18 11348b028 HDR+X 1001ef 100 0
11348b128 0 111537e18 11348b0a8 HDR+IX 1001f0 0 0
11348b2a8 0 111537e18 11348b128 HDR+X 1001f0 101 0 U
11348b328 0 111537e18 11348b2a8 HDR+X 1001f0 102 0 U
9 active, 80000 total, 16384 hash buckets, 2 lock table overflows
该输出包含以下信息:
, 表锁:
o 查看地址为 11348b028 的行,其 rowid 为 0,表明这个锁是表锁。
IX 表明其类型为意向独占。
, 页锁:
o 查看地址为 11348b0a8 的行,其 rowid 的最后两位为 00,表明
这个锁是页锁。类型 X 表明它是独占锁。这是针对 t1 的,它使
用页级锁模式。
, 行锁:
o 查看地址为 11348b328 的行,其 rowid 为 0x102,表明这个锁是
行锁,在这里为独占锁。这是针对 t2 的,它使用行级锁模式。
, 数据库锁:
o 查看地址为 10a3134d8 的行,表上有一个行锁。不过,tblsnum
0x100002 是一个数据库 tblspace,它是在服务器中保持数据库列
表的特殊表。因此,它行上的锁相当于锁定数据库。类型 S 表明
该锁是一个共享锁。
要在 onstat -k, 中找出 tblsnum 的表名,需要对 systables 表使用以下语
句:
清单 12. 识别 tbslnum 的表名
SELECT * FROM systables WHERE partnum=1049072;
Output:
tabname t2
owner informix partnum 1049072 tabid 101 rowsize 4
ncols 1
nindexes 0
nrows 0.00 created 07/20/2009 version 6619137 tabtype T
locklevel R
npused 0.00 fextsize 16
nextsize 16
flags 0
site
dbname
type_xid 0
am_id 0
pagesize 2048 ustlowts
secpolicyid 0
protgranularity
回页首
在 sysmaster 数据库中使用 syslocks 表监控锁
您还可以通过在 sysmaster 数据库中使用 syslocks 表来监控锁。表 3 列出了
该表中的列并附带上说明:
表 3. syslock 表的字段
列 描述 dbsname 持有锁的数据库
tabname 持有锁的表的名称
rowidlk 持有锁的行的 ID(0 表明该锁是一个表锁)
keynum 行的键号
Type 锁的类型
owner 锁持有者的会话 ID
waiter 锁的第一个等待者的会话 ID 在 db1 中运行以下查询:
清单 13. db1 上的查询
CREATE DATABASE db1 WITH LOG;
CREATE TABLE t3 (c1 int);
CREATE INDEX idx1 ON t3(c1);
INSERT INTO t3 VALUES(10); INSERT INTO t3 VALUES(11); INSERT INTO t3 VALUES(12); INSERT INTO t3 VALUES(13);
BEGIN WORK;
UPDATE t3 SET c1=99 WHERE c1=10;
从 syslocks 选择关于 db1 的信息:
清单 14. 从 syslocks 选择 db1 信息
SELECT * FROM syslocks WHERE dbsname='db1';
Output:
dbsname db1
tabname t3
rowidlk 0
keynum 0
type IX
owner 109
waiter
dbsname db1
tabname idx1
rowidlk 0
keynum 1
type X
owner 109
waiter
dbsname db1
tabname t3
rowidlk 256
keynum 0
type X
owner 109
waiter
输出结果明确显示这些锁是 db1 中的对象持有的。tabname 列列出了索引名。注意,rowidlk 的解释方式与 onstat -k 输出的 rowid 列一样。如果将 256 转换成十六进制得到 0x100,则表明该锁是一个页锁。
隔离级别
什么是隔离级别,
“隔离级别是指您的程序与其他程序的并发操作的隔离程度” (“Informix
Guide to SQL:教程”,IBM 2007 年)。每个隔离级别都提供不同的并发性级别和不同的数据一致性程度。
ANSI SQL-92 标准中提到了数据并发性的 3 个潜在问题(现象)。它们是:
, 脏读:读取永远不可能存在的数据
下面是一个可能的脏读场景,其中 T1 和 T2 作为不同的事务:
T1:开始一个事务
T1:修改一些数据
T2:读取修改的数据
T1:回滚事务
, 非重复读:读取修改后的数据或不能找到删除项
下面是一个可能的场景,其中 T1 和 T2 作为不同的事务:
T1:读取一个数据项
T2:修改或删除该数据项
T1:再次读取该数据项
, 伪读:从第一次搜索中得到不同的结果
下面是一个可能的场景,其中 T1 和 T2 作为不同的事务:
T1:基于某些条件搜索一组结果
T2:插入一组满足 T1 的条件的数据
T1:再次根据相同的条件搜索结果
IDS 提供 4 种隔离级别,它们为处理数据并发性问题提供一些机制:
, 脏读隔离(读未提交)
, 提交读隔离(读提交)
, 游标稳定性隔离
, 可重复读隔离(可重复读和可系列化)
脏读隔离级别(读未提交)
脏读隔离不需要在读取的行上使用锁,因此不检查所需的行上是否有锁。使用该隔离级别的会话可能会得到脏数据,即该数据被另一个会话更新但尚未提交。 对于非日志数据库,这是唯一可以使用的隔离级别。
以上列出的所有 3 种现象都可以在这个隔离级别上出现。
提交读隔离级别(读提交)
提交读隔离仅允许会话读取已提交的行。它不在正在读取的行上放置锁,但检查该行是否放置了锁。因此这个隔离级别不会发生脏读。它是这样阻止脏读的:
清单 15. 阻止脏读
T1: CREATE TABLE t1 (c1 int) LOCK MODE ROW;
T1: INSERT INTO t1 VALUES(1);
T1: INSERT INTO t1 VALUES(2);
T1: INSERT INTO t1 VALUES(3);
T1: BEGIN WORK
T1: UPDATE t1 SET c1=20 WHERE c1=2 (this places an exclusive lock on the
row)
T2: SET ISOLATION TO Committed Read
T2: SELECT * FROM t1 WHERE c1=2
将拒绝 T2 读取该行,因为数据库服务器检测到该行已经放置了一个独占锁,表明它是一个未提交的行。
因为正在读取的行上没有放置任何锁,所以可能发生非重复读现象。例如:
清单 16. 非重复读现象
T1: BEGIN WORK
T1: SET ISOLATION TO Committed Read
T1: SELECT * FROM t1 WHERE c1=2
T2: BEGIN WORK
T2: UPDATE t1 SET c1=20 WHERE c1=2 (places an Exclusive Lock on the row
and updates it)
T2: COMMIT; this releases the lock
T1: SELECT * FROM t1 WHERE c1=2 (read the same row but this time it has
different data)
另外,由于表没有锁定,所以并不能阻止发生伪读,因为另一个会话能够轻易插入其他行。
IDS 支持一种称为最新提交读 的提交读,它读取最新提交的行。因此,即使更改某个行并且未提交它,数据库服务器也能返回最新提交的行。最新提交读隔离阻止读取器在以上场景中收到锁错误,但仍然不能阻止类似于一般提交读隔离中出现的两种现象。
游标稳定性隔离级别
游标稳定性隔离在读取的行上放置一个共享锁,并且在锁定下一个行时释放该锁。
这个隔离级别能够阻止脏读和非重复读现象。下面显示了如何阻止脏读:
清单 17. 阻止脏读
T1: BEGIN WORK
T1: UPDATE t1 SET c1=20 WHERE c1=2 (an exclusive lock is placed this row) T2: SET ISOLATION TO Cursor Stability
T2: SELECT * FROM t1 WHERE c1=2
服务器拒绝 T2 读取该行,该行不能再放置一个共享锁,因为其上已经放置了一个独占锁(参见 表 1)。
清单 18 演示了游标稳定性如何阻止非重复读现象:
清单 18. 游标稳定性阻止非重复读现象
T1: SET ISOLATION TO Cursor Stability
T1: declare a cursor for SELECT * FROM t1 WHERE c1=2
T1: open cursor and fetch the row
T2: UPDATE t1 SET c1=20 WHERE c1=2 (Fail to put an exclusive lock on
the row because a shared lock is
already on it; see Table 1.) T1: SELECT * FROM t1 WHERE c1=2
注意,为了在读取的记录上放置一个锁,会话将需要使用一个游标来获取该记录。否则,将不能放置任何锁,就像在提交读隔离级别中一样。
使用这个隔离级别的会话可能出现伪读现象,就像在提交读隔离级别中一样。 可重复读隔离级别
在 IDS 中实现的可重复读隔离级别相当于 ANSI SQL 92 中的可重复读和可系列化。
这个隔离级别除了能够阻止脏读和非重复读现象之外,它还能阻止伪读现象。 如果数据库服务器需要为使用可重复读隔离的会话对表执行序列读,它将在该表上放置一个共享锁。不过,如果使用了索引扫描的话,共享锁仅能锁定与受影响的行相关的索引键。
查看以下例子。创建一个表并插入下面的值:
清单 19. 创建表并插入值
CREATE TABLE t1 (C1 INT) LOCK MODE ROW;
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(3);
INSERT INTO t1 VALUES(4);
INSERT INTO t1 VALUES(5);
INSERT INTO t1 VALUES(6);
INSERT INTO t1 VALUES(7);
INSERT INTO t1 VALUES(8);
INSERT INTO t1 VALUES(9);
INSERT INTO t1 VALUES(10);
然后,事务 T1 对表执行以下命令:
清单 20. 执行事务 T1
T1: BEGIN WORK;
T1: SET ISOLATION TO REPEATABLE READ;
T1: SELECT * FROM t1 WHERE c1=5;
onstat -k 输出显示在表 t1 上放置了一个共享锁,尽管请求的行仅为 c1=5:
清单 21. onstat -k 的输出
Locks
address wtlist owner lklist type tblsnum rowid key#/bsiz
10a3134d8 0 111535cd8 0 HDR+S 100002
204 0
10a313558 0 111536d78 0 HDR+S 100002
202 0
10a313758 0 111536d78 10a313558 HDR+S 100243 0 0
10a44bcd8 0 111535488 0 S 100002
204 0
10a44bf58 0 111536528 0 S 100002
204 0
0x100243 的十进制值为 1049155。
运行以下 SQL 语句:
select tabname from systables where partnum=1049155
返回的结果为:
tabname t1
在这里,第二个事务 T2 继续执行以下 SQL 语句: T2: INSERT INTO t1 VALUES(20);
T2 将收到错误 271(不能插入行)和 113(表被锁定)。 如果改为在 T2 上运行以下语句:
T2: UPDATE t1 SET c1=20 WHERE c1=10
将返回一个表示不能获取需要更新的行的错误。如您所见,在表上放置共享锁对
并发性有巨大的影响。
如果您使用索引修改该例子:
CREATE INDEX idx1 ON t1(c1);
重新开始执行:
清单 22. 重新开始执行
T1: BEGIN WORK;
T1: SET ISOLATION TO REPEATABLE READ
T1: SELECT * FROM t1 WHERE c1=5;
onstat -k 的输出将类似于:
清单 23. onstat -k 的输出
Locks
address wtlist owner lklist type
tblsnum rowid key#/bsiz
10a3134d8 0 111535cd8 0 HDR+S
100002 204 0
10a313558 0 111536d78 0 HDR+S 100002 202 0
10a313658 0 111536d78 10a313d58 HDR+SR 100245 106 K- 1
10a313758 0 111536d78 10a313558 HDR+IS 1001ca 0 0
10a313b58 0 111536d78 10a313758 HDR+IS 100243 0 0
10a313d58 0 111536d78 10a313b58 HDR+SR 100245 105 K- 1
10a44bcd8 0 111535488 0 S 100002 204 0
10a44bf58 0 111536528 0 S 100002 204 0
这次,数据库服务器并没有在表 t1 上放置一个共享锁,而是在表上放置一个意
向共享锁。相反,共享锁被放置在相关的索引键上(查看 清单 23 的最后一列
的 “K -1” 值)。
如果 T2 尝试执行更新,如以上例子所示:
T2: BEGIN WORK;
T2: UPDATE t2 SET c1=5 WHERE c1=1
这次更新将成功。
从这个例子可以看到,索引能够改善并发性。
不过,如果 T2 尝试执行以下任意 SQL 语句时,会话将收到一个 “键值被锁定”
错误:
INSERT INTO t1 VALUES(5);
UPDATE t1 SET c1=20 WHERE c1=5;
DELETE t1 SET c1=20 WHERE c1=5;
因为不能插入或更改 c1 等于 5 的行,这不仅避免了伪读现象,还增加了并发
性。
回页首
默认隔离和更改默认隔离
遵从 ANSI 的数据库的默认隔离级别是可重复读。不遵从 ANSI 的非日志数据库的默认隔离级别是脏读,不遵从 ANSI 的日志数据库的默认隔离级别是提交读。 要更改隔离级别,需要使用以下 SQL 语句:
SET ISOLATION TO
其中 可以是以下之一:
, DIRTY READ
, COMMITTED READ
, CURSOR STABILITY
, REPEATABLE READ
在 SET ISOLATION 运行之后运行的 SQL 语句将拥有新的隔离级别。
回页首
监控隔离级别
可以使用 onstat -g sql 或 onstat -g ses 命令监控隔离级别。 例如,会话 ID 为 26 的会话(onstat -u 输出的第三列)显示以下信息:
清单 24. onstat -g ses 26
$ onstat -g ses 26
IBM Informix Dynamic Server Version 11.50.FC3 -- On-Line -- Up 00:36:15
-- 522240 Kbytes
session effective #RSAM total
used dynamic
id user user tty pid hostname threads
memory memory explain
26 informix - 128 16699 acme 1
196608 114440 off
tid name rstcb flags curstk status
40 sqlexec 111972cf0 Y-BP--- 7839 cond wait
netnorm -
Memory pools count 2
name class addr totalsize freesize #allocfrag
#freefrag
26 V 112bdc040 192512 81360 259 62
26*O0 V 112dcf040 4096 808 1 1
name free used name free used
overhead 0 6576 scb 0 144
opentable 0 4360 filetable 0 1480
ru 0 600 log 0 16536
temprec 0 21664 keys 0
856
gentcb 0 1632 ostcb 0 2872
sqscb 0 38648 sql 0 72
rdahead 0 1120 hashfiletab 0 552
osenv 0 2680 sqtcb 0 3808
fragman 0 352 udr 0 10488
sqscb info
scb sqscb optofc pdqpriority sqlstats optcompind directives
1125dc318 112c61028 0 0 0 2 1
Sess SQL Current Iso Lock SQL ISAM F.E.
Id Stmt type Database Lvl Mode ERR ERR
Vers Explain
26 - db1 CR Not Wait 0 0
9.24 Off
Last parsed SQL statement :
update t2 set c1=5
清单 25. onstat -g sql 26
$ onstat -g sql 26
IBM Informix Dynamic Server Version 11.50.FC3 -- On-Line -- Up 00:36:40
-- 522240 Kbytes
Sess SQL Current Iso Lock SQL ISAM
F.E.
Id Stmt type Database Lvl Mode ERR ERR
Vers Explain
26 - db1 CR Not Wait 0 0
9.24 Off
Last parsed SQL statement :
update t2 set c1=5
“CR” 表示提交读。脏读、游标稳定性、可重复读分别用 DR、CS 和 RR 表示。如果您使用最新提交读隔离级别,那么其表示符号为 LC。
结束语
现在,您已经很好地理解 IDS 使用哪些类型的锁来在多用户环境中维护数据的完整性。另外,您还学习了不同隔离级别如何影响并发性,以及如何避开这些隔离级别的潜参考资料
学习
, 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
, “IBM Informix Guide to SQL: Tutorial“(IBM,2007 年):了解 SQL
的 Informix 实现,包括 SQL 语句、数据类型和提供数据库结构信息的
系统目录表。
, developerWorks Informix 专区:获取扩展您的 Informix 技能所需的资
源。
, “IDS Detective Game”(developerWorks,2008 年 4 月):通过一个
称为 “IDS Detective Game” 的互动游戏学习或教授 Informix Dynamic
Server(IDS)和关系数据库的基础知识。
, IDS roadmap for administrators, developers, and end users:查找
关于 IDS 的各个方面的资源,包括、安装、配置、管理、调优和监
控等。
, Informix Education Training Path:查看要掌握特定技能或通过特定认
证所需的课程。
, Informix 库:从在线手册或 IDS Information Center 更多地了解关于
IDS 的详细信息。
, IBM Informix Dynamic Server 11.50 Information Center:查找使用 IDS
系列产品和特性所需的信息。
, developerWorks Information Management 专区:在这里可以学到更多关
于 Information Management 的知识。还可以找到技术文档、how-to 文
章、、下载、产品信息等。
, 随时关注 developerWorks 技术活动和网络广播。
获得产品和技术
, 试用版:Informix Dynamic Server Express Edition V11.50:下载
Informix Dynamic Server Express Edition 试用版本开始使用 IDS。 , Informix Dynamic Server Enterprise and Developer Edition:下载
Informix Dynamic Server Enterprise or Developer Edition 免费试用
版本。
, 下载 IBM 产品评估试用版软件 或 IBM SOA Sandbox for Reuse,并开始
使用来自 DB2?、Lotus?、Rational?、Tivoli? 和 WebSphere? 的应用程
序开发工具和中间件产品。
讨论
, 参与论坛讨论。
, IDS 专家博客:阅读世界各地的开发和技术支持工程师关于 Informix
Dynamic Server 的技术说明。
, 参与 developerWorks blogs 并加入 My developerWorks 社区;您可以
通过个人信息和定制主页获得符合自己的兴趣的 developerWorks 文章,
并与其他 developerWorks 用户进行交流。
在问题。