数据库两大神器【索引和锁】 (5)

但Read committed出现的现象--->不可重复读:一个事务读取到另外一个事务已经提交的数据,也就是说一个事务可以看到其他事务所做的修改

注:A查询数据库得到数据,B去修改数据库的数据,导致A多次查询数据库的结果都不一样【危害:A每次查询的结果都是受B的影响的,那么A查询出来的信息就没有意思了】

上面也说了,Read committed是语句级别的快照!每次读取的都是当前最新的版本

Repeatable read避免不可重复读是事务级别的快照!每次读取的都是当前事务的版本,即使被修改了,也只会读取当前事务版本的数据。

呃...如果还是不太清楚,我们来看看InnoDB的MVCC是怎么样的吧(摘抄《高性能MySQL》)

数据库两大神器【索引和锁】

数据库两大神器【索引和锁】

至于虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

注:和不可重复读类似,但虚读(幻读)会读到其他事务的插入的数据,导致前后读取不一致

MySQL的Repeatable read隔离级别加上GAP间隙锁已经处理了幻读了

参考资料:

https://www.jianshu.com/p/cb97f76a92fd

https://www.zhihu.com/question/263820564

扩展阅读:

https://www.zhihu.com/question/67739617

2.3乐观锁和悲观锁

无论是Read committed还是Repeatable read隔离级别,都是为了解决读写冲突的问题。

单纯在Repeatable read隔离级别下我们来考虑一个问题:

数据库两大神器【索引和锁】

此时,用户李四的操作就丢失掉了:

丢失更新:一个事务的更新覆盖了其它事务的更新结果

(ps:暂时没有想到比较好的例子来说明更新丢失的问题,虽然上面的例子也是更新丢失,但一定程度上是可接受的..不知道有没有人能想到不可接受的更新丢失例子呢...)

解决的方法:

使用Serializable隔离级别,事务是串行执行的!

乐观锁

悲观锁

乐观锁是一种思想,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样。如果一样更新,反之拒绝。之所以叫乐观,因为这个模式没有从数据库加锁,等到更新的时候再判断是否可以更新。

悲观锁是数据库层面加锁,都会阻塞去等待锁。

2.3.1悲观锁

所以,按照上面的例子。我们使用悲观锁的话其实很简单(手动加行锁就行了):

select * from xxxx for update

在select 语句后边加了 for update相当于加了排它锁(写锁),加了写锁以后,其他的事务就不能对它修改了!需要等待当前事务修改完之后才可以修改.

也就是说,如果张三使用select ... for update,李四就无法对该条记录修改了~

2.3.2乐观锁

乐观锁不是数据库层面上的锁,是需要自己手动去加的锁。一般我们添加一个版本字段来实现:

具体过程是这样的:

张三select * from table --->会查询出记录出来,同时会有一个version字段

数据库两大神器【索引和锁】

李四select * from table --->会查询出记录出来,同时会有一个version字段

数据库两大神器【索引和锁】

李四对这条记录做修改:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},判断之前查询到的version与现在的数据的version进行比较,同时会更新version字段

此时数据库记录如下:

数据库两大神器【索引和锁】

张三也对这条记录修改:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},但失败了!因为当前数据库中的版本跟查询出来的版本不一致

数据库两大神器【索引和锁】

参考资料:

https://zhuanlan.zhihu.com/p/31537871---什么是悲观锁和乐观锁

https://www.zhihu.com/question/27876575---乐观锁和 MVCC 的区别?

2.4间隙锁GAP

当我们用范围条件检索数据而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”。InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。

值得注意的是:间隙锁只会在Repeatable read隔离级别下使用~

例子:假如emp表中只有101条记录,其empid的值分别是1,2,...,100,101

Select * from emp where empid > 100 for update;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywygj.html