乐观锁和悲观锁的区别

来源:互联网 发布:给二维数组赋初值 编辑:程序博客网 时间:2024/05/22 04:39

       最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁的粒度上。比如,放在一个表上的锁限制对整个表的并发访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问。可见行锁粒度最小,并发访问最好,页锁粒度最大,表锁介于2者之间。锁有两种:悲观锁和乐观锁。

悲观锁:并发访问性不好。

        顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。   

乐观锁:

        顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

      如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。(读锁是共享的,写锁是排他的)。

乐观锁方案:
由于表Order的Status本身就可以起到跟timestamp列一样的效果,修改如下:
update Order
set Status="Y",
where OrderID=@OrderID and Status="wait"
set @rowcount=@@rowcount

程式的修改
var status  = GetOrderStatus(orderid); //获取用户充值状态
if(status= "wait")//如果状态为未处理
   int rowcount = UpdateOrderStatus(orderid);
if(rowcount= 1) //状态未更新
  充值
else //第二次过来的时候,返回行数为0
return   "已经充过值"
endif

悲观锁的方式,这次修改的SQL语句不是Update 而是Select
如下:
Select Status   from order    with (UPDLOCK) 

       where OrderID= @OrderID

程式完全不用修改:
//获取用户充值状态,第二次过来的时候,如果第一次还未更新,则该订单行还处于锁定状态,因此会等待第一次更新完以将锁释放
var status  = GetOrderStatus(orderid); 
if(status= "wait")//如果状态为未处理
   UpdateOrderStatus(orderid);//则更新状态为已处理
   //后台给用户充值的代码

这种方式最简单,程式完全不用修改,只需要在存储过程中加上with (UPDLOCK)即可。
缺点是对大量的并发性能会很差,而且会引起死锁。

当然对于充值这种交易而言,还是可以比较适合的





0 0
原创粉丝点击