一、事务隔离级别
MySQL :: MySQL 8.0 Reference Manual :: 15.7.2.1 Transaction Isolation Levels
二、InnoDB中不同SQL语句设置的锁
MySQL :: MySQL 8.0 Reference Manual :: 15.7.3 Locks Set by Different SQL Statements in InnoDB
总结:
1、 SELECT ... FROM是一致读取,读取数据库的快照并且不设置锁,除非事务隔离级别设置为 SERIALIZABLE. 对于 SERIALIZABLE级别,搜索在它遇到的索引记录上设置共享的下一个键锁。但是,对于使用唯一索引锁定行以搜索唯一行的语句,只需要一个索引记录锁。
2、 对于锁定读取 (SELECTwithFOR UPDATE或FOR SHARE)、 UPDATE和 DELETE语句,所采用的锁取决于语句是使用具有唯一搜索条件的唯一索引还是范围类型的搜索条件。
2.1 对于具有唯一搜索条件的唯一索引, InnoDB只锁定找到的索引记录,而不锁定它之前的间隙。也就是行锁。
2.2 对于其他搜索条件和非唯一索引, InnoDB锁定扫描的索引范围,使用间隙锁或下一个键锁
For locking reads (SELECT with FOR UPDATE or FOR SHARE), UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition.
For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it.
For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key locks to block insertions by other sessions into the gaps covered by the range. For information about gap locks and next-key locks, see Section 15.7.1, “InnoDB Locking”.
什么是间隙锁?
间隙锁是一个在索引记录之间的间隙上的锁。
间隙锁的作用
保证某个间隙内的数据在锁定情况下不会发生任何变化。比如我mysql默认隔离级别下的可重复读(RR)。
当使用唯一索引来搜索唯一行的语句时,不需要间隙锁定。如下面语句的id列有唯一索引,此时只会对id值为10的行使用记录锁。
select * from t where id = 10 for update;// 注意:如果是普通查询则是快照读,不需要加锁,加了for update就不是普通查询
如果,上面语句中id列没有建立索引或者是非唯一索引时,则语句会产生间隙锁。
如果,搜索条件里有多个查询条件(即使每个列都有唯一索引),也是会有间隙锁的。
需要注意的是,当id列上没有索引时,SQL会走聚簇索引的全表扫描进行过滤,由于过滤是在MySQL Server层面进行的。因此每条记录(无论是否满足条件)都会被加上X锁。但是,为了效率考量,MySQL做了优化,对于不满足条件的记录,会在判断后放锁,最终持有的,是满足条件的记录上的锁。但是不满足条件的记录上的加锁/放锁动作是不会省略的。所以在没有索引时,不满足条件的数据行会有加锁又放锁的耗时过程。