悲观锁、乐观锁
大约 4 分钟
1、悲观锁
假设会发生并发冲突,屏蔽一切可能违反数据完整性的操作(具有强烈的独占和排他性)
依赖数据库的锁机制实现,以保证操作最大程度的独占性。
百度百科:正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
2、缺点
数据库性能的大量开销,特别是对长事务而言,这样的开销无法承受
3、实现方法
**Mysql中 :**
在sql后面加上 for update或者for update nowait
for update和for update nowait区别:
1. for update 锁定当前操作数据,其他事务等待
2. for update nowait 锁定当前数据,其他事务发现数据被锁定,立即返回"ORA-00054错误,内容是资源正忙, 但指定以 NOWAIT 方式获取资源"
例如:select * from account where name="123" for update
优点:无论是在单机还是分布式中,只要使用的是同一个数据库,那么悲观锁就能起到作用。
缺点:锁定数据后,必将影响其他操作,在大流量的情况下,操作速度变慢
**JAVA中 :**
独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
4、使用场景举例
以mysql InnoDB为例
Demo:
begin;
select amount from item where item_id = 1 for update;
// 通过amount来做出一些行为,例如告诉用户库存不足,购买失败,然后只有amount > 1才进入更新库存操作
update item set amount = amount - 1 where item_id = 1;
commit;
由于是串行执行,其他事务的for update必须等该当前事务的 for update 语句执行,所以我们不必担心我们获得的amount被修改过,因为它永远是最新的
0、乐观锁:
不是真正的锁,而是一种实现 : 是一种实现的
1、乐观锁:
假设不会发生并发冲突,只有在提交操作时检查是否违反数据完整性,乐观锁不能解决脏读问题
乐观锁大多都基于数据版本(version)记录机制实现,何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据表增加一个“version”字段来实现。读取出数据时,将此版本一同读出,之后更新时,对此版本后 +1。此时,将提交的版本数据与数据库表对应记录的当前版本信息对比时,如果提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据。
2、优缺点:
优点 :可以多个事务同时进行,然后根据返回的不同结果做相应的操作,避免了长事务中的数据库加锁开销。
缺点 :乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局限性,如在上例中,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户余额更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。
在系统设计阶段,我们应该充分考虑到这些情况出现的可能性,并进行相应调整(如将乐观锁策略在数据库存储过程中实过程中实现,对外只开放基于此存储过程的数据更新途径,而不是将数据库表直接对外公开)。
3、步骤 :
// 1.查询出商品信息
select (status,status,version) from t_goods where id=#{id}
// 2.根据商品信息生成订单
// 3.修改商品
update t_goods
set status=2,version=version+1 where id=#{id} and versio{139}};