笔记大纲
1.快照读和锁定读
1.1 一致性读 / 快照读
事务的读取操作如果使用MVCC的方式读取就是快照读,它是一种无锁的读。所有的SELECT查询语句在可重复读、读已提交的级别下都是快照读。由于一致性读不会对记录进行加锁的操作,所以在并发环境下其它事务可以对表中的记录进行写操作。
1.2 锁定读
1.2.1 共享锁和独占锁
- `共享锁`:简称`S锁`,事务要读取一条记录时需要获取该记录的S锁。读读允许。
- `独占锁/排它锁`:简称`X锁`,事务要改动一条记录需要获得该记录的X锁。读写、写读、写写都会阻塞。类似于Java中的ReentrantReadWriteLock。
1.2.2 锁定读的语句
锁定读
:读取记录信息前对记录进行加锁(S/X锁都行)的行为就叫锁定读
。
1.2.2.1 Lock In Share Mode 对记录加S共享锁
SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE
,这段语句就会对该记录进行加S锁,允许其它事务的读取,其它事务也可以继续加S锁,但是不允许其它事务加X锁,否则会阻塞,直到当前事务提交后释放了S锁,其它线程才能加X锁。
1.2.2.2 For Update 对记录加X独占锁
SELECT * FROM user WHERE id = 1 FOR UPDATE
,对该记录进行加X独占锁,其它事务要加S锁或者X锁都会被阻塞。生产环境严谨使用For Update。
2.表锁和行锁、X锁/S锁、IX锁、IS锁
MyISAM、MEMORY这些存储引擎他们只支持表锁且不支持事务。InnoDB支持表锁和行锁。
2.1 意向锁 Intention Lock
- `意向共享锁 IS锁`:当事务准备在某条记录上加S锁时,需要先在表上加IS锁。
- `意向独占锁 IX锁`:当事务准备在某条记录上加X锁时,需要现在表上加IX锁。
2.2 InnoDB的表锁
一般情况InnoDB引擎在做CRUD时,不会对表进行加表级别的S锁或X锁的。但是对某个表执行了DDL语句就会阻塞其他的事务对表记录的修改。同样,一个正在对表记录进行修改的事务会将DDL操作阻塞。这个过程是通过在Service层的元数据锁 Metadata Lock
来完成的。
Auto-Inc锁
:主要用来对表进行插入记录,系统为记录赋予自增ID的作用,Insert语句执行时需要对表进行加Auto-Inc锁
来保证这个递增值的分配安全,和其它锁不一样的是,这个锁在Insert结束后就会立即释放。
InnoDB对自增ID的线程安全只采用Auto-Inc锁
的方式,有一个参数innodb_autoinc_lock_mode
来控制是否采用Auto-Inc锁方式,如果不采用这种方式就会采用一种轻量级锁的方式来保证自增ID的准确:简单来说就是为Insert语句修饰了自增属性的列获取一把锁然后分配ID,分配完成后立即释放,无需等到Insert语句结束才释放。
一般,对于确定Insert多少条记录的场景使用轻量级锁,对于不确定数量的Insert来说采用Auto-Inc锁的方式。
3.InnoDB的行锁
InnoDB的锁都是基于索引加锁的,如果修改语句没有使用索引,那么将升级为表锁。
3.1 Record Lock 记录锁
最普通的记录锁,普通记录锁是区分S锁和X锁的。
3.2 Gap Lock 间隙锁
间隙锁的出现就是为了解决可重复读隔离级别下的幻读问题(快照读遇到当前读会有幻读问题)。间隙锁就是在目标记录行的上一条集合和当前目标记录之间的缝隙加一个锁。
因为InnoDB的锁时基于索引的,索引是根据索引字段进行排序的,所以通过间隙锁可以来阻止其它事务向该间隙插入记录。
3.3 Next-Key Lock 临键锁
临键锁是记录锁+间隙锁的组合形式,用来保护当前记录不被修改和这个间隙不予许插入。
3.4 Insert Intention Lock 插入意向锁
当A事务要插入一条记录时,要判断索引在这个位置区间是否被加了间隙锁或者临键锁,如果有的话,A事务就会阻塞,直到其它事务提交释放了锁为止。但是A事务处在等待过程中也需要在内存中生成一个锁结构,来表名A事务处于锁的等待过程中,这个锁就是插入意向锁。
4.锁的内存结构
行锁的n_bits:一个页面中存在多条记录,用不同的比特位来区分到底是哪一条记录加了锁。