事务隔离级别分为:未提交读(Read Uncommitted
,RU),提交读(Read Committed
,RC),可重复读(Repeatable Read
,RR)和串行化(Serializable
),其中 MySQL InnoDB存储引擎默认使用 RR 隔离级别。
不同隔离级别
-
RU
:未提交读,两个不同事务 T1 和 T2 可以相互读取各自提交(Commit
)以前的数据,显然这一隔离级别不符合事务特征中的“原子性”和“隔离性”原则,因此在 SQL 标准中不被推荐使用,但这种隔离级别下的事务的并发性能最好,因为 RU 级别没有对事务影响到的数据进行额外的加锁操作,但会产生“脏读”(Dirty Read
)的问题; -
RC
:提交读,即单独每个事务在执行的过程中(commit 以前),只能读取其他事务提交以后产生的数据,这样造成的后果就是,如果 T1 事务执行中首先读取一部分数据,恰好后续T2事务对这部分数据做了修改(Update
或者Delete
)并提交,如果 T1 事务继续读取这部分数据,就会发现这部分数据已经和第一次读取的结果不一致,也就是产生了“不可重复读取”的问题,因此这一级别也被称为“不重复读”(Non Repeatable Read
); -
RR
:可重复读,这一级别解决了 RC 级别中出现的“不可重读”的问题,InnoDB 中通过对事务可能影响到的数据加锁(行锁),避免数据在提交之前被其他事务修改,但有一点需要注意的是,这里的“锁”只能锁定被读取过的行,但无法限制其他事务对数据库的插入操作(Insert
),也就是说,及时T1事务对其中SELECT
使用过的 N 行数据加了锁,在提交T1事务前,T2 事务还是可能对这 N 条数据中插入其他行,这样就造成了读取过的行虽然没有修改,但是出现的新的行,这就是“幻读”(Phantom Read
)问题。
按照 RR 的定义,这一级别通常是无法处理“幻读”的问题的,然而 MySQL InnoDB 引擎通过 MVCC
(多版本并发控制)和 Gap Lock
(间隙锁)有效的解决了幻读的问题,关于这一问题的细节内容十分复杂繁琐,后续文章再抽空整理总结。
Serializable
:串行化执行,即所有事务都按照串行的方式,顺序执行,显然这样就避免了并发中可能出现的各种问题,但明显这种级别和并发处理的原则的背道而驰的,因此除非是对数据一致性要求十分苛刻的环境,一般情况都不被推荐使用。
隔离级别中可能出现的问题
首先理解几个不同的概念:
-
Dirty Read
:脏读,指 T1 事务在执行中读取到 T2 事务没有提交的数据; -
Non Repeatable Read
:不可重读,指 T1 事务执行中,首先执行两次重复SELECT
操作,在间隙中。另一事务 T2 对 T1 SELECT 获取到的数据做了修改(Update
和Delete
)并提交(Commit
),由于已提交的数据在这一级别是可读取的,因此 T1 数据再次执行相同SELECT
操作,就会发现之前读取到的数据被莫名其妙的修改过,因此这一级别的数据不能被重复读取;
-Phantom Read
:幻读,幻读的概念和不重复读容易混淆,查阅了不少资料,似乎都讲得不是太明白,美团技术博客的文章《Innodb中的事务隔离级别和锁的关系》中提到,幻读强调插入操作(Insert
)对读取的影响,不重复读产生的原因是数据被其他的事务修改(Update
和 Delete
操作都被认为是修改操作)
最后总结一下各个级别可能产生的问题:
- RU:脏读,不可重读,幻读
- RC:不可重读,幻读
- RR:幻读
- Serializable:无