JDBC 的事务和隔离级别

这里简单写一下我对 JDBC 的事务和隔离级别的理解。

名词解释

事务

数据库事务是 DBMS 执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
一个事务一定是具有原子性(atomic)一致性(consistency)隔离性(isolation)持久性(durability),即 ACID
一个事务会包含一个或多个数据操作语句(data-manipulation statements) 和查询语句。
通常来说,事务会依照如下的流程执行:

  1. 开始一个事务

  2. 执行一系列操作或查询语句

  3. 如果没有发生错误,则提交这个事务,并将其结束

  4. 如果发生了错误,则回滚这个事务,并将其结束。

ACID

  • Atomic

原子性,即一个事务内的所有操作都被作为一个整体看待,要么全部成功,要么全部失败

  • Consistency

一致性,即事务中有操作失败时,这个事务所更改的数据都必须回滚至操作前的状态。

  • Isolation

隔离性,即事务所查看到的数据,要么是一个事务提交前的状态,要么是一个事务提交后的状态,而不可能是事务在执行中的状态。

  • Durability

持久性,即事务对系统的影响是永久的。

读现象

  • 脏读

当一个事务允许读取另一个事务修改但尚未提交的数据时,就有可能发生脏读。

  • 不可重复读

在一次事务中,对一行数据的两次读取获得了不同的结果。
该现象发生于在执行 SELECT 时没有获得读锁,或者在读取完毕后立刻释放了读锁。

  • 幻读

在事务执行过程中,两个完全一样的查询得到了不同的结果集,即是幻读。它是不可重复读的一个特殊场景。
当事务 1 在执行两次 SELECT ... WHERE 操作中间,事务 2 在这个表中生成了一行新数据,而这条新数据正好满足事务 1 的 WHERE 条件,导致事务 1 的两次查询得到了不同的结果集。

隔离级别

NONE

NONE 是一个特殊的级别,代表 JDBC 驱动不支持事务。

未提交读 (READ UNCOMMITED)

这个是最低的隔离级别。
这个隔离级别允许事务读取到其他事务尚未提交 (commit) 的数据,即允许脏读。

提交读 (READ COMMITED)

这个隔离级别中,DBMS 需要选定对象的写锁一直保持到事务结束,但是读锁会在 SELECT 操作完成后马上释放,所以有可能会发生 “不可重复读”。

可重复读 (REPEATABLE READ)

在这个隔离级别下,DBMS 需要对选定对象的读锁和写锁一直保持到事务结束,但是不要求范围锁,所以有可能发生幻读。

可串行化 (SERIALIZABLE)

这是最高的隔离级别。
在这个隔离级别下,要求 DBMS 在选定对象上的读锁和写锁一直保持到事务结束,如果使用了 WHERE 来描述范围时,则应当获取一个范围锁。这个隔离级别可以防止幻读。

隔离级别与读现象

隔离级别 脏读 不可重复读 幻读
未提交读 可能发生 可能发生 可能发生
提交读 不会发生 可能发生 可能发生
可重复读 不会发生 不会发生 可能发生
可串行化 不会发生 不会发生 不会发生

隔离级别与锁持续时间

隔离级别 写操作 读操作 范围操作
未提交读 当前语句执行完毕 当前语句执行完毕 当前语句执行完毕
提交读 当前事务提交 当前语句执行完毕 当前语句执行完毕
可重复读 当前事务提交 当前事务提交 当前语句执行完毕
可串行化 当前事务提交 当前事务提交 当前事务提交