数据库事务隔离:详解及Java面试题
目录
一、事务
二、为什么需要事务隔离?
1. 脏读(Dirty Read)
2. 不可重复读(Non-Repeatable Read)
3. 幻读(Phantom Read)
三、事务隔离级别
四、MySQL 中的事务隔离级别设置
1. 查看当前隔离级别
2. 设置全局隔离级别(对所有新连接生效)
3. 设置当前会话的隔离级别
4. 在事务开始前显式设置(推荐)
五、各隔离级别适用场景
六、Java 秋招高频事务隔离面试题
1. 什么是数据库事务?事务有哪些特性?
2. 什么是事务的隔离性?为什么要保证隔离性?
3. 数据库事务的四个隔离级别是什么?分别解决什么问题?
4. MySQL 默认的事务隔离级别是什么?为什么?
5. 什么是脏读、不可重复读、幻读?请分别举例说明。
6. MySQL 的 REPEATABLE READ 级别能避免幻读吗?为什么?
7. 事务隔离级别越高越好吗?为什么?
8. 如何在 Java / JDBC 中设置事务隔离级别?
一、事务
在数据库系统中,事务(Transaction)是一组不可分割的 SQL 操作序列,这些操作要么全部成功,要么全部失败回滚。事务具有四个重要的特性,即 ACID:
A(Atomicity,原子性):事务是一个不可分割的整体,要么全部执行,要么全部不执行。•
C(Consistency,一致性):事务将数据库从一个一致的状态转变到另一个一致的状态。
I(Isolation,隔离性):多个并发事务之间互不干扰,各自执行如同串行执行一般
D(Durability,持久性):一旦事务提交,其结果就是永久性的,即使系统崩溃也不会丢失。
其中,隔离性(Isolation) 是保障事务并发执行时数据正确性的关键,也是本文讨论的核心内容。本文将详细介绍数据库事务的隔离性概念、并发问题、隔离级别及其在 Java 面试中的常见考点。
二、为什么需要事务隔离?
在多用户并发访问数据库时,多个事务可能会同时读写相同的数据。如果没有有效的隔离机制,就可能导致数据的不一致和异常现象,主要包括以下三类典型问题:
1. 脏读(Dirty Read)
定义:一个事务读取到了另一个事务尚未提交的数据。
问题:如果后一个事务最终回滚,那么前一个事务读取到的就是无效的“脏数据”。
示例:
- 事务A修改某条记录,但未提交。
- 事务B读取了事务A修改后的数据。
- 若事务A最终回滚,则事务B读到的数据就是脏数据。
2. 不可重复读(Non-Repeatable Read)
定义:在同一个事务内,多次读取同一数据,由于其他事务的修改并提交,导致前后读取结果不一致。
问题:事务内多次读取同一数据,结果却不同,影响业务逻辑的正确性。
示例:
- 事务A第一次读取某字段值为100。
- 事务B修改该字段值为200并提交。
- 事务A再次读取该字段,得到200,与第一次不一致。
3. 幻读(Phantom Read)
定义:在同一个事务中,执行相同的查询语句,由于其他事务插入或删除了符合该查询条件的新记录,导致查询结果集不一致,就像出现了“幻影”一样。
问题:事务内两次执行同样的范围查询,返回的记录数不同,影响统计等业务逻辑。
示例:
- 事务A查询年龄大于18的用户,得到10条记录。
- 事务B插入了一个年龄大于18的新用户并提交。
- 事务A再次查询,得到11条记录,出现了“幻影记录”。
三、事务隔离级别
为了解决上述并发问题,数据库系统提供了不同的 事务隔离级别(Transaction Isolation Level),用于控制事务之间的可见性与影响程度。SQL 标准定义了四种隔离级别,从低到高依次为:
隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
---|---|---|---|---|
READ UNCOMMITTED(读未提交) | 可能 | 可能 | 可能 | 允许读取未提交的数据,隔离性最弱,一般不推荐使用。 |
READ COMMITTED(读已提交) | 不可能 | 可能 | 可能 | 只能读取其他事务已经提交的数据,Oracle 等数据库的默认级别。 |
REPEATABLE READ(可重复读) | 不可能 | 不可能 | 可能(但 MySQL 通过 MVCC 解决) | 同一事务内多次读取同一数据保持一致,MySQL InnoDB 默认级别。 |
SERIALIZABLE(串行化) | 不可能 | 不可能 | 不可能 | 最高隔离级别,强制事务串行执行,完全避免并发问题,但性能最差。 |
注意:虽然标准中 REPEATABLE READ 级别是可能发生幻读的,但 MySQL 的 InnoDB 引擎通过 MVCC(多版本并发控制)和间隙锁机制,在该隔离级别下实际上有效避免了幻读问题,这也是 MySQL 的一大特性。
四、MySQL 中的事务隔离级别设置
MySQL 默认的隔离级别为 REPEATABLE READ(可重复读),并且通过 InnoDB 存储引擎的并发控制机制,在该级别下已经能够很好地处理并发事务问题。
1. 查看当前隔离级别
SELECT @@transaction_isolation;
-- 或者老版本
SELECT @@tx_isolation;
2. 设置全局隔离级别(对所有新连接生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
注意:全局设置通常需要重启服务或者在配置文件(如 my.cnf / my.ini)中配置才能永久生效。
3. 设置当前会话的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
4. 在事务开始前显式设置(推荐)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
-- 执行 SQL 操作
COMMIT;
五、各隔离级别适用场景
隔离级别 | 适用场景 |
---|---|
READ UNCOMMITTED | 几乎不使用,仅用于特殊调试场景。 |
READ COMMITTED | 适用于对数据一致性要求较高,但允许一定的读取差异,如 Oracle 默认。 |
REPEATABLE READ | MySQL 默认,适合大多数业务场景,保证事务内数据读取一致性。 |
SERIALIZABLE | 对一致性要求极高的场景,如金融系统,但性能开销大。 |
六、Java 秋招高频事务隔离面试题
以下是数据库事务隔离级别相关的 Java 后端/数据库方向高频面试题,供大家秋招/面试准备参考:
1. 什么是数据库事务?事务有哪些特性?
答:事务是数据库中一组不可分割的 SQL 操作序列,具有 ACID 四大特性:原子性、一致性、隔离性、持久性。
2. 什么是事务的隔离性?为什么要保证隔离性?
答:隔离性是指多个并发事务之间互不干扰,各自执行如同串行执行一般。目的是为了避免脏读、不可重复读、幻读等并发问题,保证数据的一致性和正确性。
3. 数据库事务的四个隔离级别是什么?分别解决什么问题?
答:分别是 READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。
- READ UNCOMMITTED:可能发生脏读、不可重复读、幻读。•
- READ COMMITTED:避免脏读,但可能发生不可重复读和幻读。•
- REPEATABLE READ:避免脏读和不可重复读,MySQL 中通过 MVCC 还避免了幻读。•
- SERIALIZABLE:完全串行化执行,避免所有并发问题,但性能最差。
4. MySQL 默认的事务隔离级别是什么?为什么?
答:MySQL InnoDB 存储引擎默认的事务隔离级别是 REPEATABLE READ(可重复读)。该级别通过 MVCC(多版本并发控制)机制,在保证事务内数据一致性的同时,还有效避免了幻读问题,适合大多数业务场景。
5. 什么是脏读、不可重复读、幻读?请分别举例说明。
答:
- 脏读:读取到其他事务未提交的数据。
- 不可重复读:同一事务内多次读取同一数据,因其他事务提交修改导致结果不同。
- 幻读:同一事务内执行相同的范围查询,因其他事务插入/删除数据导致结果集不一致。
6. MySQL 的 REPEATABLE READ 级别能避免幻读吗?为什么?
答:能。虽然标准定义中该级别可能发生幻读,但 MySQL InnoDB 引擎通过 MVCC(多版本并发控制)和间隙锁(Gap Lock)机制,在 REPEATABLE READ 级别下有效防止了幻读的产生。
7. 事务隔离级别越高越好吗?为什么?
答:不是。隔离级别越高,并发性能通常越低,如 SERIALIZABLE 会强制事务串行执行,可能导致严重的性能瓶颈。实际开发中需要在数据一致性与系统性能之间做权衡,选择合适的隔离级别。
8. 如何在 Java / JDBC 中设置事务隔离级别?
答:可以通过 Connection
对象设置:
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
常量包括:
- TRANSACTION_READ_UNCOMMITTED
- TRANSACTION_READ_COMMITTED
- TRANSACTION_REPEATABLE_READ
- TRANSACTION_SERIALIZABLE
通常需要在开启事务前进行设置,并配合 connection.setAutoCommit(false)
使用。