悲观锁和乐观锁
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了悲观锁和乐观锁相关的知识,希望对你有一定的参考价值。
锁有两种机制:悲观锁和乐观锁。
悲观锁
悲观锁,锁如其名,他对世界是悲观的,他认为别人访问正在改变的数据的概率是很高的,所以从数据开始更改时就将数据锁住,直到更改完成才释放。
一个典型的倚赖数据库的悲观锁调用:
select * from account where name=”Erica” for update
这条 sql 语句锁定了 account 表中所有符合检索条件( name=”Erica” )的记录。 本次事务提交前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。该语句用来锁定特定的行(如果有where 子句,就是满足 where条件的那些行)。当这些行被锁定后,其他会话可以选择这些行,但不能更改或删除这些行,直到该语句的事务被 commit 语句或 rollback 语句结束为止。需要注意的是,select ....for update 要放到 mysql 的事务中,即 begin 和commit 中,否则不起作用。
悲观锁可能会造成加锁的时间很长,并发性不好,特别是长事务,影响系统的整体性能。
悲观锁的实现方式:
悲观锁,也是基于数据库的锁机制实现。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁
乐观锁,他对世界比较乐观,认为别人访问正在改变的数据的概率是很低的,所以直到修改完成准备提交所做的修改到数据库的时候才会将数据锁住,当你读取以及改变该对象时并不加锁,完成更改后释放。乐观锁不能解决脏读的问题。
乐观锁加锁的时间要比悲观锁短,大大提升了大并发量下的系统整体性能表现。
乐观锁的实现方式:
1.大多是基于数据版本(Version)记录机制实现,需要为每一行数据增加一个版本标识(也就是每一行数据多一个字段 version),每次更新数据都要更新对应的版本号+1。
工作原理:读出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本信息与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据,不得不重新读取该对象并作出更改。
假设数据库中帐户信息表中有一个version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。
1 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $5( $100-$50 )。
2 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
3 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额(balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
4 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2,不满足 “ 提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。
这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。
从上面的例子可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员 A 和操作员 B 操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。
2.使用时间戳来实现
同样是在需要乐观锁控制的 table 中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的 version 类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则 OK,否则就是版本冲突。
悲观锁和乐观锁的适用场景:
如果并发量不大,可以使用悲观锁解决并发问题;但如果系统的并发量非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.现在大部分应用都应该是乐观锁的。
本文出自 “秦斌的博客” 博客,谢绝转载!
以上是关于悲观锁和乐观锁的主要内容,如果未能解决你的问题,请参考以下文章