读锁与写锁X锁S锁X锁冲突冲突S锁冲突不冲突读锁:当前事务给某些数据加了读锁,允许其他事务加读锁但不允许加写锁。也可以称之为共享锁、Shared Locks、S锁示例select * from tab
读锁与写锁
X锁 | S锁 | |
---|---|---|
X锁 | 冲突 | 冲突 |
S锁 | 冲突 | 不冲突 |
读锁:当前事务给某些数据加了读锁,允许其他事务加读锁但不允许加写锁。也可以称之为共享锁、Shared Locks、S锁
示例
select * from table_a lock in share mode
使用场景:读出数据后其他事务不能修改,但自己也不一定能修改,因为其他事务也可以加读锁。主要用来保证数据在本事务内值不会变。但实际开发中用得比较少。
写锁:当前事务如果加了写锁,其他事务若加锁会阻塞。也可以称之为排他锁、Exclusice Locks、X锁
示例
select * from table_a for update 以及 DELETE/UPDATE/INSERT(插入后未提交的数据也会加写锁)
使用场景:只有本事务才可以修改这些数据,其他事务加锁会阻塞。一般开发过程中大部分接触到的锁都是写锁。
行锁与表锁
行锁:只有InnoDB里才会有。
行锁又分为:
- LOCK_REC_NOT_GAP:单个行记录上的锁。
- LOCK_GAP:间隙锁,锁定一个范围,但不包括记录本身。什么意思呢?比如查询一条ID为7的数据,间隙锁是给ID=7这条数据上下记录间的间隙给锁定,但记录本身不上锁。就如MVCC是解决可重复读的问题一样,间隙锁是解决幻读的问题,即同一事务内,数据总数不变。
- LOCK_ORDINARY:同上,也是间隙锁,只是锁定范围包括记录本身。我们一般所说的间隙锁就是这个。
示例:
select * from table_a where a = 1 for update
事务隔离级别为读提交时会对查询出的所有每条数据加行锁(单个行记录上锁),但此时其他事务可以针对这个数据范围insert造成幻读。
当事务隔离级别为可重复读时(mysql默认的事务隔离级别)如果走索引,mysql通过索引知道数据的范围,会对查询出的数据范围间隙及数据本身加锁,其他事务针对这个数据范围写操作都会阻塞,包括insert在这个间隙范围内的操作,以避免幻读。
但如果sql语句不走索引全表扫描,此时mysql并不知道数据范围,mysql只能对所有的数据和间隙上锁以避免幻读。
表锁
表锁在InnoDB中应用很少,写操作是不会加表级别的锁。
而DDL语句也会发生阻塞,这个过程是通过使用元数据锁(Metadata Locks,MDL)来实现的,并不是表级别的锁。但是可以用过以下方式加表锁
- LOCK TABLES t1 READ
- LOCK TABLES t1 WRITE
但是不建议使用,因为InnoDB的优点就是行锁,表锁性能差。
总结
- mysql默认事务隔离级别是可重复读,所以需要重点关注的概念:写锁、行锁、间隙锁。
- 读锁与写锁是对锁功能的分类,而行锁与表锁是对锁范围的一个分类。

- 0