数据库中的隔离和锁机制

在任何一个设计两个或更多的客户端和数据库交互的应用中,都要考虑并发(concurrency)问题,数据库一般通过隔离(Isolation)和锁机制(Locking)来解决这个问题.

***隔离
ANSI/SQL92标准说明了事务之间有三种不同类型的交互,下面从最低等级到最高等级分别列出:
*脏读(Dirty reads):不管事务是否提交,该事务中的变化都可被其他事务看到。所以一个事务可能读取另一个并发事务未提交的数据。

*不可重复读(Nonrepeatable reads):一个事务中的更新,删除和插入只有提交后,其他事务才可以看到,因此一个事务中多个相同的查询可能会读到不一致的数据。

*幻影读(Phantom reads):一个事务在表中插入的数据只有被提交,其他事务才可以看到。因此当一个事务重新执行一个查询时,返回的记录中可能包含其他事务最近提交的新记录。

SQL规范定义了四种隔离等级,下面分别说明个别允许或禁止哪些交互:
*Read uncommitted:最低的事务隔离,允许所有的交互

*Read committed:避免脏读

*Repeatable read:避免不可重复读和脏读

*Serializable:最高的事务隔离,可以避免所有的交互

一般数据库的默认隔离等级都是Read committed.当然,也可以调用Connection中的setTransactionIsolation()方法来选择一个更高的等级。


***锁机制
我们先回顾一下经典的订飞机票问题,假如两个乘客都在查询空余座位,他们选择了同一个座位号并准备预定,此时的情况是a先按了预定按钮,而b慢一拍,结果a的预定被b覆盖了,反而是b订到了该座位,该过程的代码如下(为了说明他们选择了同一个座位号,我们用仅剩最后一张座位来模拟):
Statement stmt=connection.creatStatement();
ResultSet rset=stmt.executeQuary("select seatid from seat where available=true");
if(rset.next)
{
 stmt.executeUpdate("update seat set passenger='"+user+"' available=false where seatid="+rset.getInt(1));//user="a"或"b"
}
从代码执行的角度看,在a执行了查询但尚未执行更新前,b执行了查询,此时在客户端显示给a和b的空闲座位其实是同一张座位,而接下来的情况是a先更新b随后,但最后该数据库中该座位的passenger应该是b.这种现象称为“丢失修改”

下面分别用乐观锁机制和悲观锁机制来解决这个问题
*悲观锁机制
Statement stmt=connection.creatStatement();
ResultSet rset=stmt.executeQuary("select seatid from seat where available=true for update");
if(rset.next)
{
 stmt.executeUpdate("update seat set passenger='"+user+"' available=false where seatid="+rset.getInt(1));//user="a"或"b"
}
可以看到,这段代码与上述唯一不同是在select语句的最后加了"for update".这样就告诉数据库这个事务将要更新表,而数据库则将该表锁定,此时如果b要执行同样的操作,他的查询将会被阻塞,知道a提交事务,b才能执行查询,而这时该座位的available已被设为false,所以b将查讯不到任何座位。所以悲观锁解决了修改丢失。
然而悲观所锁的代价是很大的,试想如果有很多座位可供选择,a打开了查询后慢慢的思考该选哪个座位,那么其他所有的用户都会被阻塞查询,这就太糟糕了。为此,请看乐观锁机制。

*乐观锁机制
Statement stmt=connection.creatStatement();
ResultSet rset=stmt.executeQuary("select seatid from seat where available=true");
if(rset.next)
{
 int a=stmt.executeUpdate("update seat set passenger='"+user+"' available=false where  available=true and seatid="+rset.getInt(1));//user="a"或"b"
 if(a==1)
  //succeed!
 else
  //fail!
}
可以看到,这段代码与上述唯一不同是在update语句中又重新验证了available=true,此时如果该座位已被a预定,那么b的预定操作就会失败,所以我们需要根据update的返回值来检查预定的结果,而不项悲观锁那样肯定会成功。
所以乐观锁在解决了并行性的同时也多了一步检查更新结果成功与否的操作。 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值