一致性读(MVCC,无锁读)
InnoDB使用数据库某一个时间点的快照提供给查询,查询只能看到该时间点之前已提交的数据 或者 当前查询事务自己做的修改。
在可重读和读已提交的普通select语句都是一致性读,一致性读不会在表上设置任何锁。
快照不适用于DML语句
快照只应用于select语句,而不应用在DML语句(insert、update、delete),如果你在一个事务中插入或修改某些行,并提交事务,另一个同时运行的可重复读事务,如果执行delete或update语句,会影响第一个事务提交的数据,即使第二个事务的查询并看不到第一个事务的修改,并且第二个事务做出修改之后,它就可以查询到这些第一个事务做的修改。如以下栗子:特殊select语句
对于特殊的select语句,INSERT INTO ... SELECT, UPDATE ... (SELECT),和CREATE TABLE ... SELECT,如果是非序列化的其他隔离级别,则以读已提交的隔离级别来执行一致性读。
两种锁定读
如果使用普通的select语句(无锁),然后再执行更新操作(CAS),可能会出现问题,因为别的事务也可以同时修改你查出来的数据。InnoDB提供了两种锁定读,两种锁都在事务结束之后释放:
SELECT ... FOR SHARE:对读取的数据加共享锁,之后其他事务不能对其修改,如果读取之前,别的事务已经对数据做了修改,查询会等待其他事务锁的释放
SELECT ... FOR UPDATE:跟update语句一样,其他事务无法再对数据加锁。一致性读会忽略这些锁,照样读取数据而不需要等待锁释放,因为老版本数据并不能加锁,它们是通过undo logs重建出来的存放在内存的记录拷贝。
用在for update和for share上两种拒绝等待锁的方式:
- NOWAIT:如果数据被加锁了,就返回error
-
SKIP LOCKED:如果数据被加锁,就跳过,只返回没加锁的数据
可重复读
- 一致性非锁定读(普通select)
- 在一个事务里的所有一致性读都会返回在第一次读取时的快照(MVCC同一份快照)
- 锁定读(for update或for share)、update、delete
- 对于唯一索引搜索条件:inndb锁定index record
- 其他搜索条件,使用间隙锁或next-key锁,防止其他事务往间隙中插入数据
读已提交
- 一致性非锁定读(普通select)
- 在一个事务里的所有一致性读都返回最新的已提交快照,如果第一次读取了最新快照之后,自己做了修改,再次读取,会读到自己的修改,如果此时别的事务又提交了新版本,再次读取,会读到新提交的快照(MVCC的最新已提交快照)
- 锁定读(for update或for share)、update、delete
- InnoDB只锁定index record,而不锁定间隙,允许其他事务插入间隙,可能出现幻读
- 对于update和delete语句,InnoDB只锁定涉及到的行
- 对于update语句,如果行已经被锁定,InnoDB执行“半一致读”并返回最新提交的数据,然后再判断返回的行是否符合update的where谓语,如果符合where谓语,InnoDB会锁住它或者等待它上面的锁。
举个栗子