文章目录
前言
数据库作为一个服务(进程),不可避免会有被多个用户同时使用的场景。也就是所谓的并发场景,熟悉多线程编程的朋友们也应该都知道高并发下多线程修改共享数据需要做并发控制才能避免我们的共享数据出现错误。这里类比我们的关系型数据库其实也不例外,隔离等级就是关系型数据库提供给我们的并发控制工具。本文将带你了解MySQL提供给我们的事务隔离等级以及不同等级下会面对的不同数据错误问题。
事务隔离级别
事务隔离级别,英:Transaction Isolation Level,有时候会被省略为隔离级别。MySQL的官方文档对其解释如下:
大致的意思是:事务隔离是数据库处理的基础之一。隔离(Isolation)在数据库特性缩写的ACID中代表着 I;隔离等级则是在多个事务同时进行时,用于平衡性能和事务结果的可靠性、一致性和再现性(reproducibility)的一种设定(Setting)。
说白话就是,并发场景下,不同的隔离事务等级有着不同的性能和结果可靠性。严格的隔离等级就意味着性能的损失,但也意味着事务结果更加可靠,反之亦然。
三种数据不一致问题
在了解具体不同事务隔离等级之前,我们需要先了解几种数据不一致问题的概念,我们的数据库,通常会面临几种数据不一致问题,分别是:
- 脏读
- 不可重复度
- 幻读
下面我们分别介绍一下这几个概念。
1. 脏读
脏读,英:dirty read。
脏读意味着读到了不可信的数据,比如数据被某个事务更改了,但是明明该事务还没提交,就已经可以被其他事务读到了,只有事务的隔离级别是read uncommitted时才会出现脏读。
2. 不可重复读
不可重复读,英:non-repeatable read。
不可重复读意味着,在同一个事务里,一个查询被前后两次执行去获取数据,根据数据库ACID的特性,我们认为 同一个事务里相同的查询操作去获取数据应该是能得到相同的数据。然而我们一前一后的两个查询却取到了不同的数据(有的数据被其他事务修改并提交因此被读到)。
3. 幻读
幻读,英:phantom read。
phantom,有幻觉之意。幻读意味着在同一个事务里,我们执行同一个查询语句两次,一前一后。后一个查询读到了前一个查询里没有的数据(由于其他事务插入了新数据或是更新了数据导致where语句条件通过)。数据的突然出现,就像幻觉一样。
不可重复读 vs 幻读
如何区分不可重复读和幻读一直是一个问题,因为按照定义幻读其实也是一种不可重复读。毕竟在幻读的场景下同一事务内同一查询操作前后两次请求到的数据确实不一样,也符合不可重复读的前后不一致的定义。
所以如何区分这两个概念呢?笔者这里提供一个思路:
- 不可重复读:侧重点是前后两次请求到的结果集内数据条数一致,但结果集内的数据并不完全一致的情况。
- 幻读:侧重点是前后两次请求到的结果集内数据条数不一致。
四种事务隔离级别
MySQL的数据存储引擎InnoDB根据SQL:1992 标准的规定,提供了四种事务隔离级别。
按隔离严格程度低到高排序 分别是:
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ(默认隔离级别)
- SERIALIZABLE
笔者会在接下来的章节里分别介绍这几种隔离级别。
1. READ UNCOMMITTED
READ UNCOMMITTED,中译:读未提交。
是最不严格的事务隔离级别,速度也最快。如其名,其他事务的未提交(Commit)的修改,也能感知(Read)到。也正是因为如此,使用这种隔离级别会导致前面我们提到的所有三种问题。
2. READ COMMITTED
READ COMMITTED,中译:读提交。
比READ UNCOMMITTED稍微严格一点的事务隔离级别,性能次于READ UNCOMMITTED,和READ UNCOMMITTED相比,一个事务的未提交的修改,不能被其他的事务感知,但一旦提交后,其他能立即感知到这个变化。使用这种隔离级别会导致幻读和不可重复读。
3. REPEATABLE READ
REPEATABLE READ,中译:可重复读。是InnoDB引擎的默认事务隔离级别。
比READ COMMITTED更严格一点的事务隔离级别,性能次于READ COMMITTED,和READ COMMITTED相比,其他事务提交的修改,不会影响到当前的事务的多次查询结果。但是对于新数据的插入则会出现幻读(数据条数增加)。
InnoDB为了实现这种隔离级别,用到了MVCC(Multi-Version Concurrency Control / 多版本并发控制)机制。这个机制本文不过多做讨论,有兴趣的读者可以自行搜索关键词MVCC。
4. SERIALIZABLE
SERIALIZABLE,中译:串行化、序列化。是InnoDB引擎最不常用的事务隔离级别。
作为最严格的事务隔离级别,因为额外的控制开销导致这种隔离级别的事务性能最差。但也正因为如此,这个隔离级别的事务不会遇到上述的三种问题中的任意一种。
不同事务隔离级别会面临的问题
不同事务隔离级别的事务会面临不同的问题,为了方便记忆和理解,笔者作了一个表格。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | √ | √ | √ |
READ COMMITTED | √ | √ | |
REPEATABLE READ | √ | ||
SERIALIZABLE |
不同隔离事务级别的使用率排名
在MySQL官方文档里提供了隔离级别的使用率排名。
从最常用的到最不常用的顺序分别是:
- 默认的REPEATABLE READ
- READ COMMITTED
- READ UNCOMMITTED
- SERIALIZABLE
可以看到通常情况下我们不太需要设置默认的隔离事务级别,基本都是够用的。
实战
简单列举几个实战的例子。
查看事务隔离级别
你可以通过在MySQL的命令行客户端执行
show variables like '%tx_isolation%';
或
select @@tx_isolation;
就可以查询到事务的隔离级别了
亦或是在你的数据库连接工具里执行也是可以的,如下:
select @@global.tx_isolation;
select @@session.tx_isolation;
select @@tx_isolation;
更改事务隔离级别
虽然我们大部分情况使用默认的隔离事务级别即可,但是如果你想使用其他的事务隔离级别。你可以使用SET TRANSACTION命令去实现。
你可以通过下面几种命令去改变SESSION级别的事务隔离级别,全局的GLOBAL通常不推荐修改,没有权限的用户也是会被MySQL拒绝修改的。
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
select @@session.tx_isolation;
结语
事务之于数据库是非常重要的,而事务隔离级别之于事务也是一样,是非常重要的。理解几种事务隔离级别以及它们面临如脏读、幻读、不可重复读等问题,是更好地理解关系型数据库如何工作的基础。另外文中的大部分知识都在MySQL的官方文档能找到出处,笔者强烈推荐有英语阅读能力的读者们去阅读MySQL官方文档(文中有链接)。
我是虎猫,希望本文对你有所帮助。(=・ω・=)