乐观锁和悲观锁

乐观并发控制(又名乐观锁)

乐观并发控制(OCC = Optimistic concurrency control)是一种并发控制方法,适用于像关系型数据库以及软件事务性内存这样的事务性系统。OCC假设多个事务可以频繁地完成,而这些事务互相不会干扰。在运行时,事务使用数据源而不需要获取这些资源上的锁。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果有其他事务更新的话,正在提交的事务将会回滚。

OCC常被使用在数据竞争程度低的场景中。当冲突极少发生的时候,事务可以忽略管理锁的开销,不需要等待其他事务锁的清除,可以获得比其他并发控制方法更高的吞吐量。然而,如果数据源的竞争非常频繁,重复重启事务的开销会显著地降低性能。通常来讲,其他的并发控制方法在这样的场景下会有更好的表现。

乐观并发控制的事务包括以下阶段:

● 读取:事务将数据读入缓存,这时系统会给事务分派一个时间戳。
● 校验:事务执行完毕后,进行提交。这时同步校验所有事务,如果事务所读取的数据在读取之后又被其他事务修改,则产生冲突,事务被中断回滚。
● 写入:通过校验阶段后,将更新的数据写入数据库。

优点与不足:

乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就会产生冲突。

实现:

使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段(也可以使用时间戳)来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

悲观并发控制(又名悲观锁)

在关系数据库管理系统里,悲观并发控制(PCC = Pessimistic Concurrency Control)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。
优点与不足:

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。

实现:

此实现转载于该文章:http://chenzhou123520.iteye.com/blog/1860954

这里讨论在Mysql中的实现。在上面的场景中,商品信息从查询出来到修改,中间有一个处理订单的过程,使用悲观锁的原理就是,当我们在查询出goods信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为goods被锁定了,就不会出现有第三者来对其进行修改了。

注:要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

我们可以使用命令设置MySQL为非autocommit模式:

set autocommit=0;

设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:

//0.开始事务 
begin;/begin work;/start transaction; (三者选一就可以)
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;/commit work;

注:上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交,在这里就不细表了。

上面的第一步我们执行了一次查询操作:select status from t_goods where id=1 for update;
与普通查询不一样的是,我们使用了select…for update的方式,这样就通过数据库实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

注:需要注意的是,在事务中,只有SELECT ... FOR UPDATELOCK IN SHARE MODE同一笔数据时会等待其它事务结束后才执行,一般SELECT … 则不受此影响。拿上面的实例来说,当我执行select status from t_goods where id=1 for update;后。我在另外的事务中如果再次执行select status from t_goods where id=1 for update;则第二个事务会一直等待第一个事务的提交,此时第二个查询处于阻塞的状态,但是如果我是在第二个事务中执行select status from t_goods where id=1;则能正常查询出数据,不会受第一个事务的影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值