34. MySQL 事务隔离级别会产生的并发问题?
事务定义了四种事务隔离级别,不同数据库在实现时,产生的并发问题是不同的。
不同的隔离级别有不同的现象,并有不同的锁定/并发机制,隔离级别越高,数据库的并发性就越差。
READ UNCOMMITTED(未提交读):事务中的修改,即使没有提交,对其他事务也都是可见的。
会导致脏读。
READ COMMITTED(提交读):事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。
会导致不可重复读。
这个隔离级别,也可以叫做“不可重复读”。
REPEATABLE READ(可重复读):一个事务按相同的查询条件读取以前检索过的数据,其他事务插入了满足其查询条件的新数据。产生幻行。
会导致幻读。
SERIALIZABLE(可串行化):强制事务串行执行。
MySQL InnoDB 采用 MVCC 来支持高并发,实现结果如下表所示:
关于 Oracle 和 PostgreSQL ,需要胖友自己去搜索资料。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是(x) |
串行化(serializable) | 否 | 否 | 否 |
MySQL 默认的事务隔离级别为可重复读(repeatable-read) 。
上图的
<X>
处,**MySQL 通过 MVCC + 事务第一次调用SELECT
语句才生成快照,实现其在可重复读(repeatable-read)的隔离级别下,不存在幻读问题。**也就是说,上图<X>
处,需要改成“否”!!!!想要进一步了解的,可以看看 《MySQL InnoDB 事务 —— 一致性读(快照读)》、《MYSQL 当前读和快照读》、《【MySQL】当前读、快照读、MVCC》 文章。? 记住这个表的方式,我们会发现它是自左上向右下是一个对角线。当然,最好是去理解。
具体的实验,胖友可以看看 《MySQL 的四种事务隔离级别》 。
有些资料说可重复读解决了幻读,实际是存在的,可以通过
SELECT xxx FROM t WHERE id = ? FOR UPDATE
的方式,获得到悲观锁,禁止其它事务操作对应的数据,从而解决幻读问题。感兴趣的胖友,可以看看如下文章:必读 《MySQL 幻读的详解、实例及解决办法》 案例性更强,易懂。
其实 RR 也是可以避免幻读的,通过对 select 操作手动加 行X锁(SELECT … FOR UPDATE 这也正是 SERIALIZABLE 隔离级别下会隐式为你做的事情),同时还需要知道,即便当前记录不存在,比如 id = 1 是不存在的,当前事务也会获得一把记录锁(因为InnoDB的行锁锁定的是索引,故记录实体存在与否没关系,存在就加 行X锁,不存在就加 next-key lock间隙X锁),其他事务则无法插入此索引的记录,故杜绝了幻读。
选读 《MySQL 的 InnoDB 的幻读问题》 原理性更强,读懂会很爽。
随意 《Innodb 中 RR 隔离级别能否防止幻读?》 一个简单的讨论。