一、前言
还记得前段时间手头项目初识阶段,团队一大神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的查询条件会发生表锁,其他数据也不能获取到,有兴趣可自行实践。
四、后话
只是一个简单的入门,目前工作很少应用,先尝尝鲜。并不是所有你不知道的,都是那些高大上,及其难的知识点,在亲自实践过后再怀疑人生吧。
( ̄▽ ̄)/
网友评论