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 隔离级别能否防止幻读?》 一个简单的讨论。