初识“乐观锁”和“悲观锁”

一、前言

还记得前段时间手头项目初识阶段,团队一大神code review时提及“乐观锁”,我心里一懵,哇,这是什么东西,感觉好叼的样子!是的,我根本就母鸡这是什么意思,有何用途,如何正确使用。

最近浏览别人的面经时看到这个话题,在此做个简单的入门,也好在以后的面试中不是一再重复面试官的问题。(〃'▽'〃)

二、概念

个人理解,这里的锁是针对数据库而言的。像synchronized关键字是对于Java程序而言的。就“乐观锁”和“悲观锁”字面上讲,可以简单作出如下解释:

乐观锁:及其乐观,认为我从数据库取数据时没有其他操作修改该数据,在更新时会把将要更新的数据与之前取出的数据作出对比,如果有过修改,则更新不成功。比对的方法通常采用字段添加一个版本号,更新数据行时顺带更新这个版本号;

悲观锁:非常悲观,获取数据时害怕其他人修改本条数据,将其锁住(这里分为“row lock”和“table lock”,后续会简单提及),然后在自己操作完成后将其锁释放。在此期间,对本记录的操作都将阻塞;

三、简单实现

假如我们有张数据表student作为基础数据:

id name version
1 张三 0
2 李四 0
3 王五 0

1、乐观锁

查询sql语句为:

select id, name, version from student;

更新语句为:

update  
  name = #{name}, 
  version = version + 1
from student 
where id = #{id} and version = #{version};

很清楚查询就是普通的sql,但是更新时会检查在拿到查询结果到执行更新操作期间内version是否有过变动;如果是有过变动where条件不满足那么执行完sql后受影响行数为0,则达到了乐观锁的效果。

2、悲观锁

真正的给数据表加锁,需要手动控制事务的提交。MySQL的事务默认是自动提交,我们需要设置为手动提交:

set autocommit = 0;

查询时需要运用到数据库sql的特性for update

select * from student where id = 1 for update;

注意:这里查询条件若是主键的话,则是row lock(行锁),不影响其他数据的操作,若是其他条件或是没有条件的话,则是table(表锁),整个表都会被锁住,知道该事务提交。而且这里的操作sql都是针对for update而言的,普通sql不会造成影响。

为了验证悲观锁的特性,我们开启两个mysql的console,方便区分,取名为left,right;

left:

set autocommit = 0;
begin;
select * from student where id = 1 for update;

输出:

id name version
1 张三 0

但是注意我们的事务还没有提交,切换到right控制台:

select * from student where id = 1 for update;

发现控制台一直等待,一段时间后就会抛出错误信息:

1205 - Lock wait timeout exceeded; try restarting transaction

提示很清楚,锁超时了。然后我们再次执行right控制台同样的语句,同样阻塞在等待获取锁,切换到left的控制台,执行commit;命令提交事务,再次切换到right控制台,发现已经输出:

id name version
1 张三 0

如果去掉where的查询条件会发生表锁,其他数据也不能获取到,有兴趣可自行实践。

四、后话

只是一个简单的入门,目前工作很少应用,先尝尝鲜。并不是所有你不知道的,都是那些高大上,及其难的知识点,在亲自实践过后再怀疑人生吧。
( ̄▽ ̄)/

你可能感兴趣的:(初识“乐观锁”和“悲观锁”)