https://www.jianshu.com/p/51035d3f6953
本文主要为了总结事务隔离级别的整体知识,包含模拟脏读、不可重复读和幻读的场景
要了解什么是事务隔离级别,需要先了解什么事务,可参考 百度百科-数据库事务 的概念解释,简单来说:
数据库事务,描述的是对数据库进行读写操作的一个操作序列,要么全部执行,要么全都不执行的现象,这个操作序列是原子性的、不可分割的工作单元
并且数据库事务包含如下 4 个经典特性,也就是常说的 ACID 特性:
原子性(Atomicity)
事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行
一致性(Consistency)
几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致
也就是说,当执行数据库事务操作后(事务提交或者事务回滚),数据库的完整性约束不能被破坏
隔离性(Isolation)
事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的
持久性(Durability)
对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障
数据库的事务经常需要并发执行,也即经常会有多个客户端连接到数据库上来进行读写操作,并发事务下会发生如下几种问题:
丢失更新,包含回滚丢失和覆盖丢失两种
可参考:并发事务导致的丢失更新及处理方式详解 博文进行了解
脏读
某个事务读到了其他事务还没有提交的数据
不可重复读
着重指针对某一行数据,在同一个事务内多次读取的内容不同,同一条记录的内容被修改了(或删除了)
产生原因:读取过程中,有其他事务对该数据进行了修改或删除
幻读, 参见 mysql-官网-幻读说明
着重指在同一个事务里的操作发现了未被操作的数据
比如,事务 A 根据条件查询得到了 N 条数据,准备进行更新操作;但此时事务 B 新增或删除了 M 条符合事务 A 查询条件的的数据;当事务 A 执行更新操作,重新查询后发现了 M 条自己不应该进行更新的记录,就像发生了幻觉一样
注意,上述示例的事务 A 的查询得到 N 条数据,准备更新这 N 条数据,并重新根据条件进行查询的操作均在同一个事务内
其中,丢失更新的问题是由于临界资源使用不当造成(同一条数据被并发修改,该条数据应被视为共享临界资源,需要使用者控制好加锁方式),可通过加锁方式来解决
而脏读、不可重复读和幻读,则需要通过事务隔离级别的控制来加以解决,通过不同级别的事务隔离级别,来解决相应的脏读、不可重复读和幻读问题,而事务隔离级别越高,在并发事务场景下会产生的问题就越少,但数据库付出的性能消耗也就越大,并发能力也就越低,因此需要根据不同场景设置不同的事务隔离级别,在事务并发性和数据库性能之间做一个平衡
了解了什么是事务,以及为什么要有事务隔离级别之后,我们再来回答什么是事务隔离级别,就比较轻松了:
在 SQL-92 标准中定义了并发事务场景下脏读、不可重复读和幻读这三种异常情况,而为了解决这些异常情况,SQL-92 标准还定义了 4 种隔离级别来解决这些异常,具体有哪 4 种隔离级别可参照下文
所以简单说,事务隔离级别就是为了解决并发事务场景下脏读、不可重复读和幻读异常而提出的一种应对方式
数据库的事务隔离级别主要有以下 4 种:
读未提交(READ UNCOMMITTED)
能够读取到其他事务没有提交的数据,不能解决脏读、不可重复读、幻读
读已提交(READ COMMITTED)
能够读取到其他事务已经提交的数据,可以防止脏读,但不能防止不可重复读和幻读
可重复读(REPEATABLE READ)【MySQL 默认事务隔离级别】
保证一个事务在相同查询条件下两次查询得到的数据结果是一致的,可以避免不可重复读和脏读,但无法避免幻读
串行化(SERIALIZABLE)
最高级别的隔离等级,将事务进行串行化,也就是在一个队列中按照顺序执行,可以解决脏读、不可重复读和幻读,但是牺牲了系统的并发性
不同事务隔离级别能解决的异常情况,表格展示如下:
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(READ UNCOMMITTED) | 允许 | 允许 | 允许 |
读已提交(READ COMMITTED) | 不允许 | 允许 | 允许 |
可重复读(REPEATABLE READ) | 不允许 | 不允许 | 允许 |
串行化(SERIALIZABLE) | 不允许 | 不允许 | 不允许 |
以 MySQL 引擎为例进行说明
通过如下 sql 可查看 MySQL 引擎的事务隔离级别:
-- 查询 MySQL 引擎事务隔离级别变量设置,适用于所有版本的 MySQL 引擎查询
SHOW VARIABLES LIKE '%isolation%';
-- 直接查询 MySQL 事务隔离级别变量设置,需明确 MySQL 引擎版本,不同版本的变量设置不一样,可参考下文表格(示例为 5.6 版本)
SELECT @@tx_isolation;
不同版本的 MySQL 引擎(可通过 select version()
查询 MySQL 版本),事务隔离级别变量的设置不一致,以表格形式进行展示:
MySQL 引擎版本 | 事务隔离级别变量 |
---|---|
5.6 | tx_isolation |
5.7 | tx_isolation, transaction_isolation |
8.0.19 | transaction_isolation |
事务隔离级别的参数包含如下 4 种:
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
按照修改生效的作用于区分,可分为如下两种修改:
修改当前会话事务隔离级别(当前会话即为当前连接)
-- 将当前会话的事务隔离级别设置为可重复读,`REPEATABLE READ` 参数可替换成上述 4 种参数的任意一种
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
修改全局事务隔离级别(即对之后的所有连接均生效)
-- 将全局事务隔离级别设置为可重复读,`REPEATABLE READ` 参数可替换成上述 4 种参数的任意一种
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
可参考 廖雪峰-事务-脏读示例演示,示例简单,清晰明了,包含简短视频演示
可参考 廖雪峰-事务-不可重复读示例演示,示例简单,清晰明了,包含简短视频演示
可参考 廖雪峰-事务-幻读示例演示,示例简单,清晰明了,包含简短视频演示