MySQL 中的锁
按照 MySQL 官方的说法,InnoDB 中锁可以分为:
可见,InnoDB 中锁非常多,总的来说,可以如下分类:
这些锁都是做什么的?具体含义是什么?我们现在来一一学习。
8.1. 解决并发事务问题
我们已经知道事务并发执行时可能带来的各种问题,最大的一个难点是:一
方面要最大程度地利用数据库的并发访问,另外一方面还要确保每个用户能以一
致的方式读取和修改数据,尤其是一个事务进行读取操作,另一个同时进行改动
操作的情况下。
8.1.1. 复习并发事务问题
一个事务进行读取操作,另一个进行改动操作,我们前边说过,这种情况下
可能发生脏读、不可重复读、幻读的问题。
SQL 标准规定不同隔离级别下可能发生的问题不一样:
在 READ UNCOMMITTED 隔离级别下,脏读、不可重复读、幻读都可能发生。
在 READ COMMITTED 隔离级别下,不可重复读、幻读可能发生,脏读不可以发生。
在 REPEATABLE READ 隔离级别下,幻读可能发生,脏读和不可重复读不可以发生。
在 SERIALIZABLE 隔离级别下,上述问题都不可以发生。
不过各个数据库厂商对 SQL 标准的支持都可能不一样,与 SQL 标准不同的一
点就是,MySQL 在 REPEATABLE READ 隔离级别实际上就基本解决了幻读问题。
怎么解决脏读、不可重复读、幻读这些问题呢?其实有两种可选的解决方案:
8.1.2. 方案一:读操作 MVCC,写操作进行加锁
所谓的 MVCC 我们在前一章有过详细的描述,就是通过生成一个 ReadView,
然后通过 ReadView 找到符合条件的记录版本(历史版本是由 undo 日志构建的),
其实就像是在生成 ReadView 的那个时刻做了一个快照,查询语句只能读到在生
成 ReadView 之前已提交事务所做的更改,在生成 ReadView 之前未提交的事务或
者之后才开启的事务所做的更改是看不到的。而写操作肯定针对的是最新版本的
记录,读记录的历史版本和改动记录的最新版本本身并不冲突,也就是采用
MVCC 时,读-写操作并不冲突。
我们说过普通的 SELECT 语句在 READ COMMITTED 和 REPEATABLE READ 隔离
级别下会使用到 MVCC 读取记录。在 READ COMMITTED 隔离级别下,一个事务
在执行过程中每次执行 SELECT 操作时都会生成一个 ReadView,ReadView 的存在
本身就保证了事务不可以读取到未提交的事务所做的更改,也就是避免了脏读现
象;REPEATABLE READ 隔离级别下,一个事务在执行过程中只有第一次执行
SELECT 操作才会生成一个 ReadView,之后的 SELECT 操作都复用这个 ReadView,
这样也就避免了不可重复读和很大程度上避免了幻读的问题。
8.1.3. 一致性读(Consistent Reads)/快照读
事务利用 MVCC 进行的读取操作称之为一致性读,或者一致性无锁读,也称
之为快照读,但是往往读取的是历史版本数据。所有普通的 SELECT 语句(plain
SELECT)在 READ COMMITTED、REPEATABLE READ 隔离级别下都算是一致性读。
上面的这句话中,普通的 SELECT 语句是指不加锁的 select 语句在非串行化事务隔离级别下。