# 事务

注意

事务』本身是数据库领域中的概念,而非编程语言(如 Java)中的概念!是因为我们要在代码中去操作数据库,因此,我们的代码中『才会涉及』、『才有了事务』的概念。不要搞错了因果关系!

# 事务的 ACID 属性

数据库事务的正确执行的 4 个基本要素是 原子性Atommicity)、一致性Consistency)、隔离性Isolation)和 持久性Durability)

问题 描述
原子性             整个事务中的操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节,仅完成一部分。
一致性 事务必须使数据库从一个一致状态变为另一个一致状态。
隔离性 事务的隔离性是指一个事务的执行过程中不被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的事务之间不能互相干扰。
持久性 在事务完成以后,该事务对数据库所做的更改便持久保存在数据库中,并不会被回滚。

# 三大问题

三大问题从 严重 到 轻度 以此如下:

# 脏读问题

对于两个事务 T1 和 T2 可能会出现如下情况,T1 读取了已经被 T2 更新但是还未提交 的字段,若此时 T2 回滚,那么 T1 读取到的内容就是临时且无效的。

时刻 事务一(老公) 事务二(老婆)
T1 查询余额,显示 10k ——
T2 —— 查询余额,显示 10k
T3 —— 网购 1千,显示 9k
T4 请客吃饭开销 1k,显示余额 8k ——
T5 提交事务 ——
T6 —— 回滚事务
T7 —— 最终余额 8k

所谓脏读,指的就是读到了“脏”数据,即一个事务读取到了另一个事务未提交的数据。

# 不可重复读问题

对于两个事务 T1 和 T2 可能会出现如下情况,T1 读取了一个字段,然后 T2 更新了这个字段。之后,T1 再次读取该字段时,会发现值发生了变化。

时刻 事务一(老公) 事务二(老婆)
T1 查询余额,显示10k ——
T2 —— 查询余额,显示 10k
T3 —— 网购,开销 1k,余额 9k
T4 请客吃饭,预计开销 2k ——
T5 —— 网购,开销 8k,余额 1k
T6 —— 提交事务
T7 吃完买单,显示余额 1k,不够付账。 ——

不可重复读,指的是理论上的同一条数据,重复读取,居然会不一样,不具备可重复性。

# 幻读问题

对于两个事务 T1 和 T2 可能会出现如下情况,T1 从一个表中读取了一个字段,然后 T2 在该表中插入了一些新的数据。之后,T1 再次读取该字段时,会发现多出来几行。

时刻 事务一(老公) 事务二(老婆)
T1 —— 查询信用卡消费记录,显示 10 条记录
T2 网购 ——
T3 提交事务 ——
T4 —— 打印消费记录,有11条记录

幻读,和不可重复读类似,第二次读取的数据相较于第一次居然发生了变化,仿佛看到了幻觉。

『不可重复读』和『幻读』有一定的相似性,都是指(在本人未改变的情况下)第二次读取的数据,与第一次读取结果不一样。不过它们描述的侧重点(及造成的影响程度)不一样。

  • 不可重复读问题,强调的是某一条数据的内容在“我”两次读取间,发生了改变。(因为 update 语句)
  • 幻读问题,强调的整个数据的数据总量,在“我”两次读取间,发生了改变。(因为 insert / delete 语句)
  • 幻读问题 造成的危害要小于不可重复读问题。

不可重复读幻读 在一定程度上是可接受的,而 脏读 是完全不可接受的。

# 四个隔离级别

隔离级别表示:当『我』操作这张表时,『其他人』对这张表还有多大的操作权限 。『我』的隔离级别越高,其他人的权利就越小,那么『他』要执行他想要执行的操作而没有权限时,那就只能 『我』操作完 。

数据库领域有四个隔离级别(注意,这并非 Java 中特有的概念),针对于上述三大问题,四个隔离级别,从 解决不了任何问题解决所有问题 ,每一级多解决一个问题。

隔离级别 解决问题 备注
READ_UNCOMMITTED 解决不了任何问题 ——
READ_COMMITTED 可以解决脏读问题 ——
REPEATABLE_READ 可以解决不可重复读问题 包括解决脏读问题
SERIALIZABLE 解决幻读问题 包括解决不可重复读脏读问题

隔离级别越高,事务间的相互干扰就越小,数据的一致性就越好,但同时并发性就越弱。

MySQL 支持四种隔离级别,默认事务隔离级别为 Repeatable Read

# JDBC 事务的自动提交和隔离级别

Connection 类中有如下实例方法:

// 设置事务开启自动提交
void setAutoCommit(boolean autoCommit) throws SQLException;

// 设置事务的隔离级别
void setTransactionIsolation(int level) throws SQLException
Connection.TRANSACTION_NONE
Connection.TRANSACTION_READ_UNCOMMITTED
Connection.TRANSACTION_READ_COMMITTED
Connection.TRANSACTION_REPEATABLE_READ
Connection.TRANSACTION_SERIALIZABLE