单纯的select操作,不包括上述 select ... lock in share mode, select ... for update。 快照读的实现方式:undolog和多版本并发控制MVCC 当前读,读取的是最新版本,并且对读取的记录加锁,阻塞其他事务同时改动相同记录,避免出现安全问题。以下形式的SQL属于当前读: 例如,假设要update一条记录,但是另一个事务已经delete这条数据并且commit了,如果不加锁就会产生冲突。所以update的时候肯定要是当前读,得到最新的信息并且锁定相应的记录。 对于读操作,mysql默认的隔离级别是 可重复读(Read Repeatable) 。普通的select查询是可重复读的,底层基于多版本并发控制MVCC的快照读 ,其他事务的修改操作对当前事务不可见,当前事务读的是历史版本。但是如果加上 其他隔离级别如下: 基于多版本并发控制MVCC实现的默认隔离REPEATABLE_READ并不能解决幻读(PHANTOM READ)问题,mysql解决幻读并不是在隔离级别层面上处理,而是通过锁层面处理,这是两个层面的问题。 这里,我需要对“幻读”做一个说明,来源自mysql45讲,第20讲: 我自己的理解是,普通的行锁是锁住已经存在的行,而在可重复读的前提下插入的是第二次读的时候读到第一次读不存在的数据,所以这才是幻读(PHANTOM READ)。PHANTOM 在英文中就有幽灵的意思,意味幻觉,不存在的。 对于“不存在的数据”和“不符合过滤条件的数据” 要分开理解,在mysql中,如果对应字段没有索引,会遍历所有行,然后对所有遍历过的行进行加行锁,即使没有符合条件的行结果。 所以,幻读就是针对插入(insert)这种情况。 而mysql在可重复读隔离级别下,为了解决幻读,在行锁(record lock)的基础上,加入了间隙锁(gap lock)。行锁和间隙锁组合起来一起,就成为了临键锁(next-key lock)。 对于索引中包含了查询语句所有结果的情况,查询就不需要通过主键进行回表,对应索引就是覆盖索引。因为对于锁而已,它锁住的就是其搜索过的对象。如果没有索引而走全表扫描,锁住的就是全部行,走索引锁住的就是索引对象。 加锁规则如下,来源自mysql45讲,第21讲 加锁规则里面,包含了两个“原则”、两个“优化”和一个“bug”1 前置知识
1.1 快照读
Read Committed隔离级别:每次select都生成一个快照读
Read Repeatable隔离级别:开启事务后第一个select语句才是快照读的地方,而不是一开启事务就快照读1.2 当前读
此处参考 https://www.jianshu.com/p/eb3f56565b422. 事务隔离级别和锁
for update 或者 in share mode 就会切换到当前读,能读到其他事务已经提交commit的数据。
ISOLATION LEVEL
DIRTY READ
NON-REPEATABLE READ
PHANTOM READ
READ_UNCOMMITTED
allowed
allowed
allowed
READ_COMMITTED
prevented
allowed
allowed
REPEATABLE_READ
prevented
prevented
allowed
SERIALIZABLE
prevented
prevented
prevented
3. 覆盖索引
参考