Mysql可重复读隔离级别下如何解决幻读
一些概念
幻读
:在一次事务中,同一条select语句在先后两次查询中得到了不同的记录。
MySql可重复读隔离级别下解决了幻读。
快照读的幻读用MVCC解决,当前读的幻读用间隙锁解决
MVCC,快照读和当前读的概念:
MVCC及实现原理
下面只分析当前读的幻读。
一些锁的概念:
共享锁(Shared)
:又称S锁,读锁,共享锁,加了S锁其他事务可以读,不可以写。
排它锁(Exclusive)
:又称X锁,写锁,排它锁,加了X锁其他事务不可以读也不可以写。
record lock
:索引加锁。单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身,即使表上没有任何索引,innodb会在每条记录上生成默认的聚簇索引。当一条sql语句没有任何索引时,会在每一条聚簇索引后面加X锁,这个表类似于表锁,但原理是不同的。
GAP lock
:间隙锁。在记录索引的间隙中加锁。
跟间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作。间隙锁和间隙锁之间不存在冲突关系。
next-key lock
:由record lock+GAP lock组成,每次锁住的不光是需要使用的数据,还会锁住这些数据附近的数据。
在可重复读隔离级别下,加锁的基本单位是next-key lock,是前开后闭的。
具体加锁说明
有如下一张person表,其中id是主键索引。name是二级索引。
id | name | age |
---|---|---|
1 | a张三 | 20 |
3 | b李四 | 23 |
6 | c王五 | 20 |
12 | d赵六 | 25 |
使用主键索引进行等值查询
- 如果记录存在,因为主键具有唯一性,所以只需要添加一个行锁(record lock)。
SELECT * FROM person WHERE id=3 LOCK IN SHARE MOOE;
深色表示加上了行锁,具体是S锁。
- 如果记录不存在。为了防止幻读会使用间隙锁。
SELECT * FROM person WHERE id=5 LOCK IN SHARE MOOE;
会在3和6之间加上间隙锁。
插入:
SELECT …LOCK IN SHARE MOOE和SELECT …FOR UPDATE的区别
SELECT …LOCK IN SHARE MOOE走的是IS锁(意向共享锁),即在符合条件的rows上都加了共享锁。
SELECT …FOR UPDATE走的是IX锁(意向排它锁),即在符合条件的rows上都加了排它锁。
如果在上面的语句中换成了SELECT …FOR UPDATE,大致的流程是一样的,只不过行锁中的S锁换成了X锁。
使用主键索引进行范围查询
- 查询语句
SELECT * FROM person WHERE id>3 LOCK IN SHARE MOOE;
-
update语句
– 如果update语句没有更新二级索引,和前面是一样的。
– 如果update更新了二级索引。
UPDATE SET name='春花' WHERE id>3;
会对二级索引对应的行加上X锁。
使用二级索引进行等值查询
SELECT * FROM person WHERE name='张三'LOCK IN SHARE MODE;
因为二级索引不是唯一的,二级索引会加上行锁和间隙锁,主键索引会加上行锁。
使用二级索引进行范围查询
SELECT * FROM person WHERE name>='李四' LOCK IN SHARE MOOE;
首先会查找>=’李四’的第一条记录,在二级索引中加上行锁和间隙锁,在对应的主键索引中加上行锁,判断下一个记录是否符合要求,继续重复。