MySQL数据库的事务隔离级别
讲解MySQL四种事务隔离级别,分析其如何解决脏读、不可重复读、幻读等并发问题,并提供MySQL中的查看与设置方法。
我们来详细讲解一下 MySQL 数据库的事务隔离级别。这是一个非常核心且重要的概念,直接关系到数据库的并发性能和数据一致性。
什么是事务和隔离性?
首先,我们简单回顾一下事务(Transaction)的四大特性(ACID):
- 原子性 (Atomicity): 事务是一个不可分割的工作单元,事务中的操作要么都发生,要么都不发生。
- 一致性 (Consistency): 事务必须使数据库从一个一致性状态变换到另一个一致性状态。
- 隔离性 (Isolation): 多个并发事务之间是相互隔离的,一个事务的执行不应被其他事务干扰。
- 持久性 (Durability): 一个事务一旦被提交,它对数据库中数据的改变就是永久性的。
我们今天要讨论的事务隔离级别,就是为了定义隔离性 (Isolation) 的程度。因为完全的隔离(即让所有事务串行执行)会极大地降低数据库的并发性能。因此,SQL 标准定义了四种不同的隔离级别,允许开发者在数据一致性和并发性能之间做出权衡。
并发事务可能带来的问题
在讨论隔离级别之前,我们必须先了解,如果隔离性不足,多个事务并发执行时可能会遇到哪些问题:
脏读 (Dirty Read):
- 定义: 一个事务读取到了另一个事务尚未提交的数据。如果那个事务最终回滚(Rollback),那么第一个事务读取到的就是无效的“脏”数据。
- 例子: 事务A修改了某行数据但未提交,事务B读取了这行被修改的数据。之后事务A回滚,那么事务B读取到的数据就是不存在的。
不可重复读 (Non-repeatable Read):
- 定义: 在同一个事务内,多次读取同一行数据,得到的结果却不一样。这是因为在两次读取之间,有另一个事务修改了这行数据并提交了。
- 重点: 关注的是同一行数据的修改(UPDATE)或删除(DELETE)。
- 例子: 事务A读取某商品价格为100元。此时事务B将该商品价格修改为120元并提交。事务A再次读取该商品价格时,发现变成了120元。
幻读 (Phantom Read):
- 定义: 在同一个事务内,多次执行同一个范围查询,返回的结果集行数不同。这是因为在两次查询之间,有另一个事务插入(INSERT)或删除(DELETE)了符合该查询条件的数据并提交了。
- 重点: 关注的是一个范围内数据的增加或减少。它和不可重复读的区别在于,幻读是行数变了,像出现了“幻影”一样。
- 例子: 事务A查询所有年龄大于20岁的员工,得到10条记录。此时事务B插入一个年龄为25岁的新员工并提交。事务A再次执行相同的查询,发现得到了11条记录。
四种事务隔离级别
SQL 标准定义了以下四种隔离级别,从低到高,隔离性越来越强,但并发性能通常也越来越差。
1. 读未提交 (Read Uncommitted)
- 隔离程度: 最低级别。一个事务可以看到其他事务未提交的修改。
- 解决的问题: 无。
- 可能出现的问题: 脏读、不可重复读、幻读。
- 应用场景: 性能要求极高,但对数据一致性要求很低的场景。实际应用中非常罕见。
2. 读已提交 (Read Committed)
- 隔离程度: 一个事务只能读取到其他事务已经提交的数据。这是大多数主流数据库(如 Oracle, SQL Server)的默认隔离级别。
- 解决的问题: 避免了脏读。
- 可能出现的问题: 不可重复读、幻读。
- 工作原理(常见实现): 通过 MVCC (多版本并发控制),每次
SELECT时都生成一个新的 Read View(读视图),所以能看到其他已提交事务的修改。
3. 可重复读 (Repeatable Read)
- 隔离程度: 保证在同一个事务中,多次读取同一行数据的结果都是一致的。这是 MySQL InnoDB 存储引擎的默认隔离级别。
- 解决的问题: 避免了脏读和不可重复读。
- 可能出现的问题: 标准 SQL 定义下,仍然可能出现幻读。
- 工作原理(InnoDB实现): 通过 MVCC 实现。事务在开始时创建一个 Read View(快照),后续的所有读取操作都基于这个快照,直到事务结束。
- 特殊之处 (MySQL/InnoDB): MySQL 的 InnoDB 引擎在
REPEATABLE READ级别下,通过间隙锁 (Gap Lock) 机制,在很大程度上解决了幻读问题。因此,很多人认为 MySQL 的可重复读级别已经杜绝了幻读。
4. 可串行化 (Serializable)
- 隔离程度: 最高级别。强制事务串行执行,即一个接一个地执行。
- 解决的问题: 避免了脏读、不可重复读和幻读。
- 可能出现的问题: 无。
- 工作原理: 通常通过对读取的每一行数据都加上锁(读写锁),以及范围锁,来阻止其他事务的并发修改。
- 缺点: 并发性能最差,因为大量的读写操作都会被阻塞,容易导致超时和死锁。
总结表格
| 隔离级别 | 脏读 (Dirty Read) | 不可重复读 (Non-repeatable Read) | 幻读 (Phantom Read) |
|---|---|---|---|
| 读未提交 (Read Uncommitted) | ❌ (可能发生) | ❌ (可能发生) | ❌ (可能发生) |
| 读已提交 (Read Committed) | ✅ (避免) | ❌ (可能发生) | ❌ (可能发生) |
| 可重复读 (Repeatable Read) | ✅ (避免) | ✅ (避免) | ⚠️ (标准下可能发生, InnoDB基本避免) |
| 可串行化 (Serializable) | ✅ (避免) | ✅ (避免) | ✅ (避免) |
在 MySQL 中如何操作
1. 查看当前隔离级别
可以查看全局(GLOBAL)或当前会话(SESSION)的隔离级别。
sql
-- 查看当前会话隔离级别
SELECT @@transaction_isolation;
-- 查看系统全局隔离级别
SELECT @@global.transaction_isolation;
注意: 在旧版本 MySQL 中,变量名是
tx_isolation,现在推荐使用transaction_isolation。
2. 设置隔离级别
可以为当前会话设置,也可以为之后所有的新连接设置全局级别。
sql
-- 设置当前会话的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置全局隔离级别 (对新建立的连接生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
可用的级别值包括:READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE。
如何选择合适的隔离级别?
REPEATABLE READ(MySQL 默认): 对于大多数应用来说,MySQL 的默认级别已经足够好。它提供了很高的数据一致性,同时由于 MVCC 的存在,性能也很好。它能防止脏读和不可重复读,并且在 InnoDB 中基本避免了幻读。READ COMMITTED: 如果你的应用不需要“可重复读”的保证(即一个事务内多次读取同一行数据可以不同),并且希望获得更高的并发性(因为它产生的锁范围比可重复读小),那么这个级别是不错的选择。很多互联网公司会将其设置为默认级别。SERIALIZABLE: 只有在对数据一致性要求极高,且能接受性能损失的情况下才使用,例如涉及银行转账、库存扣减等绝对不能出错的场景。READ UNCOMMITTED: 几乎不使用。
希望这个详细的解释能帮助你完全理解 MySQL 的事务隔离级别!