DB2锁机制

锁的概念

锁分类

基本的锁有两类:

  • 排他锁(X锁):写锁
  • 共享锁(S锁):读锁

事务隔离级别

  • 幻读:在一个事务中多次执行同一SQL,后续执行会返回附加行
    比如:机票代理第一次查询所有空闲座位时只有一个座位,后续有人取消了座位,再去执行查询所有空闲座位时就会有两个座位了。
  • 脏读(读取了未提交的数据):如果这些未提交的数据修改被回滚,那么就会导致数据处理中使用无效数据
    比如:飞机上仅剩的一个座位的名字被设置为Pname,但是没有提交,另一个使用UR隔离级别的应用执行SELECT语句查询这个座位对应的人员,因为会忽略行上的锁,所以将返回Pname,但是事务在提交之前被回滚,就会导致返回的这个Pname是错误的。
  • 不可重复读:相同查询在同一工作单元中返回不同的工作集,这样会导致原先做出的决定由于条件的更改而产生偏差
    例如:

幻读和不可重复读的区别:

分类描述原因可能出现此情况的隔离级别怎么办可以防止出现此种情况
幻读事务内第一次没有读取到的数据,第二次读取到了因为别的事务的操作对数据操作,使得更改后的数据符合第一次读取的筛选条件了读未提交,读已提交允许的
不可重复读事务内第一次读取到的数据,第二次读取不到了第一次读取到的数据被别的事务修改了读未提交,读已提交对第一次读取到的数据加锁,不允许其他事务进行更改(S锁)

原始连接:DB2锁机制

锁的属性、策略及模式

   相比较Oracle来说,DB2的锁机制麻烦了很多,而且这个麻烦带来的不是性能的上升而是下降,不过如果细致了解的话,只能感慨不愧是数据库理论诞生的公司,在实现数据库理论上比Oracle全面得多。 Oracle没有实现一般数据库理论里的锁机制,带来的并发性与性能上的提升以及相关的问题上文已经介绍了,现在来说说几乎完全实现一般数据库理论锁机制的DB2数据库这方面的实现。
下面的资料来源于IBM资料库DB2和 Oracle的并发控制(锁)比较。
首先是锁是属性,有如下几个基本属性:

  • 锁定对象object,
    锁定对象表示锁定的数据资源,DB2支持对表空间,表,行,索引(大型机里支持对数据页)的锁定。通常考虑表锁与行锁。
  • 锁定大小size,
    锁定大小表示锁定的数据大小。
  • 锁定时间duration,
    锁定时间通常由事务的隔离级别控制。
  • 锁定状态mode,
    锁定状态就是锁的类型,下面将会介绍。

加锁策略

  • 表锁:表中所有的行都受到同等程度的影响
  • 行锁:如果加锁的范围针对的是表及下属的行,在在对表加锁后还要在相应的数据行上加锁

对表加锁 or 同时加表锁和行锁

存在行锁的情况下,一定存在表锁。对吗?

锁的模式

DB2的表级锁定

表 1:DB2支持的表级锁定

名称缩写全名描述
IN无意图锁(Intent Node),不需要行锁拥有者可以读取包括其他事务未提交数据在内的所有数据,但不能对表中的数据作出修改
IS意图共享锁(Intent Share),需要行锁配合拥有者可以在拥有相应行上的S锁时可以读取该行的数据,但不能修改数据
IX意图排他锁(Intent eXclusive),需要行锁配合拥有者可以在拥有相应行上的X锁时可以修改该行的数据
SIX共享并且意图排他锁(Share with Intent eXclusive),需要行锁配合拥有者可以读取表中的任何数据,如果在相应的行上可以获得X锁,可以修改该行。SIX的获取比较特殊,当程序拥有IX锁时请求S锁,或者在已经拥有S锁的时候请求IX锁时产生
S共享锁(Share),不需要行锁配合可以读取表上的任何数据,如果表上被加了S锁,表上的数据只能被读取而不能做出任何修改
U更新锁(Update),不需要行锁配合拥有者可以读取表中的任何数据,如果升级为X锁,则可以更改表中的任何数据,该锁是等待对数据进行修改的一种中间状态
X排他锁(eXclusive),不需要行锁配合拥有者可以读取或者修改表中的任意数据,如果加上了X锁,除了未提交读事务外,其他程序都不能对表进行任何读取或者修改
Z超级排他锁(Super eXclusive),不需要行锁配合该锁一般不是由DML产生,而是由Drop,Alter或者创建删除索引时产生的,加上Z锁后,所有程序(包括未提交读程序)都不能对表进行读取或者修改

  具体来说,

  • IS,IX,SIX用于表一级并且需要行锁配合,用于阻止其他程序对表加上排他锁。区别如下:

    • 如果一个程序获得表的IS锁,程序可以获得某一行上的S锁用于只读操作,其他程序也可以读取该行,或者对表中其他行作出修改。
    • 如果一个程序获得表的IX锁,程序可以获得某一行的X锁用于更改操作,其他程序可以更改或者读取表中其他的行。
    • 如果一个程序获得表的SIX锁,程序可以获得某一行的X锁用于更改操作,其他程序只能对表中的其他行进行只读操作。
  • S,U,X,Z用于表一级,不需要行锁的配合。区别如下:

    • 如果程序得到表的S锁,则程序可以读表中的任意数据,同时允许其他程序获得表上的只读锁请求,如果有程序需要更改表上的数据,必须等到S锁释放。
    • 如果程序得到U锁,程序可以读取表中任意数据,最终可以通过获得X锁得到对表中任意数据的修改权,其他程序只能读取表中的数据,U锁与S锁的区别在于修改意图,U锁的设计主要是为了避免两个程序在拥有S锁的情况下同时申请X锁导致死锁。
    • 如果程序得到表上的X锁,程序可以读或者修改表上任意数据,其他程序无法读或者修改表上的数据。
    • 如果程序获得Z锁,程序可以读或者修改表中任意数据,其他程序包括未提交读程序在内不能对表执行读或者修改操作。
  • IN锁用于表上以允许未提交读这一概念。

对意图锁的理解

牛老师的注释:对于IN、IX、IS和SIX这些意图锁,我们可以这样理解:严格来说他们并不是一种锁,而是存放表中行锁的信息。举个通俗的例子,我们去住一个酒店。可以把整个酒店比喻成一张表,每个房间时一个行。当我们预定一个房间时,就对该行(房间)添加X锁,但是同时会在酒店的前台对该行(房间)做一个信息登记(旅客姓名、住多长时间等)。大家可以把意图锁当成这个酒店前台,它并不是真正意义上的锁,它维护表中每行的加锁信息,是共用的。后续的旅客通过酒店前台来看哪个房间是可的,那么,如果没有意图锁,会出现什么情况呢,假设我要住房间,那么我每次都要到每一个房间看看这个房间有没有住人,显然这样做的效率是很低下的。其实,最早的DB2版本是没有意图锁的,但这对并发影响很大,后来就增加了意图锁。所有的数据库(Oracle、Infomix和Sybase)都有意图锁的实现机制。

DB2支持的行锁如下所示:

名称缩写全名需要表锁最低级别描述
S共享锁(Share)IS该行正在被读取,其他程序只能执行读操作
U更改锁(Update)IX某个程序正在读取并有可能修改该行,其他程序只能读取该行
X排他锁(eXclusive)IX该行正在被某个程序修改,其他程序不能访问该行
W弱排他锁(Weak eXclusive)IX一行被插入表后,该行会加上W锁,只有锁的拥有者可以修改该行,与X锁的不同在于该锁与NW锁兼容
NS下一键共享锁(Next Share)IS拥有者与其他程序都可以读取该行,但不能进行修改,当程序处于RS或者CS隔离级别下时,该锁可以代替S锁
NX下一键排他锁(Next eXclusive)IX一行的数据被插入到索引或者从索引被删除时,该行的下一行会被加上NX锁,锁的拥有者可以读该行的数据但不能修改。该锁与X锁类似,但与NS锁兼容
NW下一键弱排他锁(Next Weak eXclusive)IX一行的数据被插入到索引时,该行的下一行会被加上NW锁,锁的拥有者可以读但不能修改该行的数据,与X锁及NX锁类似,但与W锁以及NS锁兼容

默认情况下,DB2总是尝试获取行锁,但可以使用ALTER TABLE语句修改为总是获取表锁,也可以使用LOCK TABLE语句获取表锁。

  以上就是DB2复杂的锁机制,相比较Oracle,DB2没有做任何乐观假设,写必须阻塞读,读必须阻塞写,不然会影响读一致性,这是最正统的数据库理论。
  不过对于开发人员来说,这种锁机制就是一个噩梦,在写任何并发性程序之前,首先就是要分析锁,我前后用了两天才理顺DB2里各种锁之间的关系,话说,如果可以理解上面两张锁阻塞表,估计就可以对DB2锁机制有一个清醒的认识了。

如何获取锁

大多数情况下,

DB2 的事务隔离级别

  下面是DB2的事务隔离级别,话说我一直以为只有事务级与语句级隔离级别,直到我看了DB2的事务隔离级别。

  • 可重复读(RR-RepeatableRead)
    首先是可重复读(RR-RepeatableRead)级别,这一级别怎么说呢,概括一点,这个级别会锁定所有使用到的表,直到事务结束,保证事务开始一直到结束所可能需求的数据没有任何变化。即事务运行时,其他程序无法修改其所用到的表,这一级别类似oracle的事务级隔离级别,但Oracle没有锁定表,只是假定没有人去修改所需求的数据。
  • 读稳定性(RS-ReadStability)
    下一个是读稳定性(RS-ReadStability)级别,这一级别只锁定真正使用到的行(包括修改的行以及检索到的行),而且可以读到其他已提交事务的数据。
  • 游标稳定性(CS-CursorStability)
    在往下是游标稳定性(CS-CursorStability)级别,这一级别类似Oracle的语句级隔离级别,锁定的只有修改了的行,可以读取已提交事务的数据。
  • 未提交读(UR-UncommittedRead)
    最后是未提交读(UR-UncommittedRead)级 别,这一级别如题,可以读到未提交的数据,不过如果修改了数据,则其表现与CS相同,即锁定修改了的行防止别的会话修改。

   四个事务级别,
确保所有可能需要的数据不要被人修改
->确保已经读取的数据不要被人修改
->确保修改的数据不要被别人修改
->可以读取别人未提交的数据(修改的数据同样不允许被别的程序修改)。

可重复读隔离级别可以防止所有现象,但是会大大降低并发性(可以同时访问同一资源的事务数量)。默认的隔离级别是CS(游标稳定性)

可重复读(Repeatable Read)

可重复读隔离级别是最严格的隔离级别。

读稳定性(Read Stability)

游标稳定性(Cursor Stability)

未提交读(Uncommitted Read)

   下一个讨论的是锁转换。

锁转换

   当程序向数据库请求它已经加锁的对象上面的锁的时候,数据库会比较对象上现在的锁与所请求的锁的模式,如果所请求的锁级别更高,则把现在的锁升级为请求的锁。
锁级别比较:
- 表锁:IN < IS < S< IX< U< X< Z
- 行锁:S< U< X
   有一个特殊例子是,如果持有S锁请求IX锁,或者持有IX锁请求S锁,锁转换结果为SIX锁。

锁升级

在学Oracle的时候,我从来不知道锁升级这个概念,这是由于Oracle中,锁并不是稀缺资源,没必要把多个锁合并为同一个锁来减少资源占用。

   DB2里有两个参数,LOCKLIST与MAXLOCKS:

  • LOCKLIST表示数据库分配的用来储存锁列表的空间大小
  • MAXLOCKS表示程序最大允许占用锁列表大小的百分比,
    当超过这个百分比的时候,就会进行锁升级,这里我就不发那长长的一大串DB2锁列表计算公式了,只需要知道,DB2会在一个程序锁定过多行的时候,会把锁定多行变更为锁定整个表。这是一个Oracle里没有的概念,如果从Oracle转为DB2,需要注意。

锁等待

   最后说说锁等待,这里类似Oracle里的nowait选项,DB2里面有一个参数LOCKTIMEOUT,可以设置这个参数的值来设定遇到锁阻塞后的等待时间,如果超时的话就会回滚语句。

   在DB2的开发建议里,为了实现并行化与数据完整性,有如下几个建议,比较Oracle的类似建议的话,相当有意思。

  • 频繁使用commit来使多个用户可以并发地访问数据(Oracle中的建议是尽可能维护业务事务的完整性,而不是切割成小事务)
  • 发出commit语句前,关闭CURSORWITH HOLD来确保所有锁都得到释放(Oracle中事务提交或者回滚后自动解锁,不需要其他操作)
  • 指定适当的隔离级别。DB2会借助一切机会锁定行,需要不同的隔离级别来协调(Oracle在必要的时候使用事务级隔离,一般只需要语句级隔离级别)
  • 适当使用LOCKTABLE语句(尽可能不要使用也很少有必要使用LOCL TABLE语句)

   DB2的开发建议还有很多,感兴趣的可以去看看相关资料,与Oracle的开发建议比较相当有意思的。关于两个数据库锁与并行的介绍就到这里,这篇文章中没有介绍DB2并发性的问题,但其实也没必要介绍,在锁机制部分已经到达并发性的瓶颈,并发性在DB2的锁机制下受到的限制太大了。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值