Mysql 事务隔离级别

当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性。


隔离级别

在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,在事务内和事务间的可见性。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。

READ UNCOMMITTED (未提交读/脏读)

在READ UNCOMMITTED级别中,事务所做的写操作,即使没有提交,对其他事务也是可见的,A事务可以读取B事务未提交的数据,这就叫做未提交读/脏读。 这个级别会导致很多问题,但从性能上来说,并不比其他级别好太多

READ COMMITTED (提交读/不可重复读)

大部分数据库系统的默认隔离级别就是 READ COMMITTED,它解决了脏读的问题,当一个事务开始时,只能“看见”已提交的事务所做的修改。但这也会引入一个不可重复读的问题:事务A在执行过程中有两次对变量a的读取,而在这两次读取之间,事务B对a做了修改,就会导致事务A的两次读取出的值不同,这就叫“不可重复读”。

REPEATABLE READ (可重复读)

可重复读是Mysql的默认事务隔离级别

REPEATABLE READ 解决了不可重复读的问题,它保证了在同一个事务中多次读取同一条记录的结果是一致的,但理论上,它无法解决另外一个幻读的问题,即:在事务A中两次读取某个范围的记录,在两次读取之间,另一个事务B在该范围插入了新的记录,当事务A再次读取时,会产生幻行。InnoDB存储引擎通过多版本并发控制(MVCC) 解决了幻读的问题。

SERIALIZABLE (可串行化)

SERIALIZABLE是最高的隔离级别,它强制事务串行执行(或者它本身就是个单线程的模型,例如Redis)。但在普通的数据库中,串行化会导致大量的加锁和锁竞争问题,从而耗费大量的资源,除非是在非常需要确保数据一致性而可以接受没有并发的情况下,才考虑采用该级别。


Mysql事务的使用方式

Mysql 默认采用自动提交的模式,也就是说,如果不是显式地开启一个事务,则每个查询都会被当作一个事务来执行提交操作,在当前连接中,可以通过设置AUTOCOMMIT变量来启用或禁用自动提交模式。

当AUTOCOMMIT=0时,所有的查询都在一个事务中,直到显式地执行了COMMIT 或 ROLLBACK,该事务才结束。


InnoDB 存储引擎是如何实现可重复读隔离级别的?

InnoDB 通过在每行记录后面保存两个隐藏的列,来实现MVCC,这两个列一个保存了行的创建版本号,一个保存了行的过期版本号,每开始一个新的事务,系统版本号都会自动递增。

事务开始时刻的版本号会作为本次事务的版本号,用来和每行记录的版本号进行比较,具体比较方式如下:

SELECT:

只查找创建版本小于等于当前版本,并且删除版本不存在或大于当前版本的记录

这样能保证本次事务SELECT的行,只读取到在本次事务执行前就存在,或是被本次事务新增的数据。

INSERT:

为新插入的每一行保存当前事务版本号,作为该行的创建版本号

DELETE:

为删除的每一行保存当前事务版本号,作为该行的删除版本号

UPDATE:

插入一行新记录,保存当前事务版本号作为该行创建版本号,并同时保存当前版本号到原来的行作为删除版本号

有这两个版本号的存在,使得大多数读操作不用加锁,这样设计使得读操作很简单,性能良好,并且也保证了只会读取到符合标准的行。但不足之处是需要额外的存储空间,并且需要更多的行检查工作和维护工作。