一、隔离性与隔离级别

隔离性:一个事务正在操作的数据应该锁起来,阻塞其他事务修改。
隔离级别:描述事务隔离性的程度。隔离级别越高,隔离性就越好,性能就越差。

二、并发事务的类型

并发事务即多个事务同时执行,而在事务间执行操作的方面可以分为三种

  • 读-读(一个事务在执行select,另一个事务也在执行select)
  • 读-写(一个事务在执行select,另一个事务执行增删改操作)
  • 写-写(一个事务在执行增删改,另一个事务也在执行增删改)

当然在多个事务执行的过程中,既可能出现读-写又可能出现写-写。
在上面读-读类型不会产生问题,其他两个会产生问题。

1.读-写事务、隔离级别、MVCC


读-写事务可能产生的问题

如果同时操作共享数据,可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。

4个隔离级别的描述及问题

读未提交:读取另一个事务未提交的数据,出现脏读、不可重复读、幻读
读已提交:读取另一个事务已提交的数据,出现不可重复读、幻读
可重复读:保证在同一个事务中多次读取统一数据结果是一样的,出现幻读
可串行化:强制事务串行执行,多个事务互不打扰,不会出现并发一致性问题

前面说过隔离级别越高,隔离性就越好,并发性能越差。比如可串行化,多个事务间串行执行,已经不存在并发了,但是多个事务间可能不存在同时操作共享资源的情况,造成性能的浪费。因此从实际应用角度考虑,MySQL官方推荐的默认隔离级别是可重复读。

如何实现可重复读呢?——MVCC

MVCC即多版本控制协议,InnoDB实现了MVCC作版本控制,也就是实现了读已提交和可重复读的隔离级别。

实现的核心思想是防止不该被事务看到的数据(例如还没提交的事务修改的数据)被看到。在InnoDB中,主要是通过使用readview的技术来实现判断。查询出来的每一行记录,都会用readview来判断一下当前这行是否可以被当前事务看到,如果可以,则输出,否则就利用undolog来构建历史版本,再进行判断,知道记录构建到最老的版本或者可见性条件满足。

上面这段话是对MVCC最本质的理解,在下文会描述具体从代码级别的实现。


2.写-写事务与锁

写写事务出现的问题举例如下,一个事务修改了另一个事务修改的数据,并且另一个事务还没有提交,这就导致了数据更新丢失的问题。

怎么去解决呢?就是给共享数据加锁,即在一个事务修改数据前要先获得锁,进而操作数据,等事务提交后释放锁下一个事务才能操作。关于锁的内容后面也会介绍。

三、总结

对于数据库的并发事务类型下一共三种情况,分别是读-读、读-写、写-写。把上面阐述的总结成下表。

并发事务类型

场景释义

出现问题

问题详述

解决办法

解决办法释义

读-读

两个事务都在读相同行数据

不出现问题

读-写

一个事务在读,一个事务在写相同行数据

事务隔离性问题

脏读、不可重复读、幻读

MVCC

判断事务对一行数据是否可见

写-写

两个事务都在写相同行数据

更新丢失数据问题

第一类更新丢失

第二类更新丢失

读写锁

对共享资源数据上锁,InnoDB采用的是悲观锁

总结上表中描述的,InnoDB通过用MVCC解决了读的问题,实现了可重复读的隔离级别。读写锁解决了写的问题。MVCC+读写锁的方式实现了并发事务的隔离性。