# 事务
注意
『事务』本身是数据库领域中的概念,而非编程语言(如 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
← 数据库连接池 Servie 层中使用事务 →