【MySQL】事务隔离级别,Java开发者必知必会

为了更好地说明事务隔离级别,请先移步【MySQL】快速理解脏读、不可重复读、幻读

你们在开发时是否遇到过这些诡异现象?

  • 刚扣完款却显示余额未更新?
  • 两次查询结果不一致导致逻辑错误?
  • 批量操作时莫名多出几条记录?

这些问题的根源都指向今天要深入探讨的——事务隔离级别!
数据库中有四种隔离级别,分别是读未提交(Read uncommitted),读已提交(Read committed),可重复读(Repeatable read),可串行化(Serializable),用来解决数据库操作中产生的各种问题。

一、ACID原则与并发问题三座大山

ACID原则是事务的基石:事务是一个不可分割的数据库操作序列。就是多条sql语句,要么全部成功,要么全部失败。

  • 原子性(Atomicity):组成任何一个事务的多个数据库操作是一个不可分割的原子单位,只有操作都成功,整个事务才会提交。任何一个操作失败,都会导致事务回滚,以及执行的操作撤销,数据库返回初始状态。
  • 一致性(Consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的。
  • 隔离性(Isolation):在并发操作数据库时,不同的事务拥有各自的数据空间,他们的操作不会对彼此产生干扰。
  • 持久性(Durability):一旦事务提交成功,数据库中的数据就是持久的,即使数据库发生障碍也会会有影响。

数据库三大并发问题:【MySQL】快速理解脏读、不可重复读、幻读

二、四大隔离级别全景解析

读未提交(Read uncommitted)

在这种隔离级别下,所有事务能够读取其他事务未提交的数据。读取其他事务未提交的数据,会造成脏读。因此在该种隔离级别下,不能解决脏读、不可重复读和幻读。

  • 适用场景:统计类不计精确值的操作
  • 案例:实时大屏显示订单总数(允许短暂误差)

读已提交(Read committed)

在这种隔离级别下,所有事务只能读取其他事务已经提交的内容。能够彻底解决脏读的现象。但在这种隔离级别下,会出现一个事务的前后多次的查询中却返回了不同内容的数据的现象,也就是出现了不可重复读。

这是**大多数数据库系统默认的隔离级别**,例如Oracle和SQL Server,但mysql不是。

  • 解决:脏读问题
  • 新问题:同一事务内多次查询结果可能不同
  • MVCC实现:每个语句使用最新已提交快照,MVCC原理可看这篇

可重复读(Repeatable read)

在这种隔离级别下,所有事务前后多次的读取到的数据内容是不变的。也就是某个事务在执行的过程中,不允许其他事务进行update操作,但允许其他事务进行add操作,造成某个事务前后多次读取到的数据总量不一致的现象,从而产生幻读。

可重复读是MySQL的默认隔离级别,但是是因为innodb存储引擎,如果是MyISAM存储引擎,由于该引擎是不支持事务的,它的默认隔离级别是读未提交

  • 解决:不可重复读
  • 机制:事务级快照+间隙锁(Gap Lock)
  • 注意:仍可能出现幻读(InnoDB通过Next-Key Lock解决)

可串行化(Serializable)

注:《高性能MySQL》中用的是可串行化,且英语直译也为"可串行化",但是在Java中这个词中文翻译为序列化。

在这种隔离级别下,所有的事务顺序执行,所以他们之间不存在冲突,从而能有效地解决脏读、不可重复读和幻读的现象。但是安全和效率不能兼得,这样事务隔离级别,会导致大量的操作超时和锁竞争,从而大大降低数据库的性能,一般不使用这样事务隔离级别。

  • 终极方案:事务串行执行
  • 代价:性能下降约30%-50%
  • 典型场景:银行核心转账系统

下面用一张表格来表示他们能够解决的问题

隔离级别 脏读 不可重复读 幻读
读未提交(Read uncommitted) ×(未解决) × ×
读已提交(Read committed) √(解决) × ×
可重复读(Repeatable read) ×
可串行化(Serializable)

主流数据库实现差异

特性\数据库 MySQL 8.0(innodb) Oracle 19c
默认级别 可重复读 读已提交
锁机制 行锁+间隙锁 多版本控制
幻读解决方案 Next-Key Lock 全表锁
快照生成时机 事务开始时 语句开始时

随着NewSQL和分布式数据库的发展,未来可能出现更细粒度的隔离控制(如Google Spanner的TrueTime机制),但事务隔离的基本原理仍然是我们需要牢固掌握的基石知识。

你可能感兴趣的:(MySQL,mysql,java,数据库)