InnoDB 的锁类型
读锁(共享锁)、写锁(排他锁)、意向锁和MDL锁。
- 读锁
读锁简称 S 锁,一个事务获取了一个事务行的读锁,其他事务也能够获取到该行对应的读锁,但不能获得写锁,即一个事务在读取数据时,其他事务也可以读,但不能对该数据进行增删改的操作。
读锁有两种select 方式。第一种是自动提交模式下的 select 查询语句,不需要加任何锁,直接返回查询结果,这就是一致性非锁定读。第二种是通过 select … lock in share mode 在被读取的行记录或者行记录的范围上加一个读锁,让其他事务可以读,但是要想申请加锁就会被阻塞。 - 写锁
写锁简称 X 锁,一个事务获取了一个数据行的写锁,其他事务就不能获取该行的其他锁,写锁的优先级最高。一些 DML 语句的操作都会对行记录加写锁。select for update 也会对读取的行记录加上一个写锁,其他事务不能再对被锁定的行进行加任何锁,否则会发生堵塞。 - MDL 锁(meta data lock)
MDL锁是 MySQL5.5之后才引入的,目的是为了保证表中的元数据的信息。在会话 A 中开启了查询事务后,会自动获得一个 DML 锁,会话 B 不可用执行任何 DDL 语句操作。
- 意向锁
在 MySQL 存储引擎 InnoDB 中,意向锁是表级锁。意向锁有两种类型:意向共享锁、意向排他锁。
意向共享锁(IS)是指在一个数据行加共享锁前必须取得该表的 IS 锁。
意向排他锁(IX)是指在一个数据行加排他锁前必须取得该表的 IX 锁。
意向锁和MDL 的作用类似,都是防止在事务进行的过程中,执行 DDL 语句的操作而导致数据的不一致。
InnoDB 行锁种类
InnoDB 在默认的事务隔离级别为 RR ,并且参数 innodb_locks_unsafe_for_binlog = 0 的模式下,行锁的种类有三种。
-
单个行记录锁(record lock)
在InnoDB中,当查询条件字段上存在索引时,多个会话更新同一行记录事务时会出现锁等待的现象,更新不同的行记录不会出现锁等待。当查询条件字段上不存在索引或者包含不存在索引的字段时,多个会话更新同一行记录事务时会出现锁等待的现象,更新同一表中不同的行记录也会出现锁等待。即对表中某一行数据进行操作时,对表中所有的行记录加了锁。
InnoDB 的锁是加在索引项上面的。
查询条件不添加索引的执行效果:
- 间隙锁
在RR 这个隔离级别,为了避免幻读现象,引入了 Gap lock。但它只锁定行记录数据的范围,不包含记录本身,即不允许在此范围内插入任何数据。
开启一个会话A,在事务中查询 ttt表中 score > 124的记录,在上面加一个共享锁;
开启一个会话B,往表中插入一个 score 大于124的记录以及一个 score 小于 124的记录,大于score 大于124的记录插入一直处于等待状态,score 小于124 的记录很快就能插入成功。
间隙锁只是针对 RR 隔离级别才管用,它是用来避免幻读现象发生的。
- Next-key Locks (记录锁和间隙锁的组合)
Next-key Locks 是记录锁和间隙锁的组合,当 InnoDB 扫描索引记录时,会先对选中的索引记录加上记录锁 , 再对索引记录的两边的间隙加上间隙锁(Gap Lock)。
例如:
在查询 ttt 表score >128 的语句上加上一个写锁,即
select * fromttt where score > 128 for update;
在另一个事务中,往表中插入 score=128 的记录,无法成功插入,出现锁超时。Next-key Locks 锁不光锁住了 score >128 这个范围,还锁住了128 这个值的本身。