隔离级别的重要性在于它平衡了数据一致性和性能。较高的隔离级别可以确保更好的数据完整性,但可能会降低性能;而较低的隔离级别则提高了性能,但可能会引发异常。
如果没有适当的隔离,可能会发生以下问题:
- 脏读:一个事务读取了另一个未提交事务写入的数据,而这些数据可能会被回滚。
- 不可重复读:一个事务两次读取同一行数据,发现值不同,因为另一个事务在两次读取之间修改并提交了该行数据。
- 幻读:一个事务检索满足某个条件的一组行,但在后续检查时发现这一组行已经发生变化,因为另一个事务插入或删除了行。
下图说明了四种隔离级别。
- 可序列化(Serializable):最高的隔离级别,事务之间完全隔离,仿佛事务是串行执行的而非并发执行的。提供最一致的结果,但在高并发下可能导致性能瓶颈。
- 可重复读取(Repeatable Read):事务期间读取的数据与事务开始时保持一致。一致性较好,性能略有降低。
- 已提交读取(Read Committed):只有在事务提交后才能读取修改的数据。一致性与性能之间的良好平衡。
- 未提交读取(Read Uncommitted):在事务提交之前,其他事务可以读取修改的数据。速度快,但数据一致性风险高
隔离由 MVCC(多版本一致性控制)和锁来保证
图中以可重复读取为例,演示了 MVCC 的工作原理:
- 每一行有两个隐藏列:transaction_id 和 roll_pointer。当事务 A 开始时,会创建一个事务 ID = 201 的新读视图。不久后,事务 B 开始,又创建了一个新的读取视图,transaction_id=202。
- 现在,事务 A 将余额修改为 200,日志中创建了一条新记录,roll_pointer 指向旧记录。在事务 A 提交之前,事务 B 读取了余额数据。事务 B 发现事务 ID 201 没有提交,于是读取了下一条提交记录(事务 ID=200)。
- 即使事务 A 提交了,事务 B 仍会根据事务 B 启动时创建的读取视图读取数据。因此,事务 B 总是读取余额=100 的数据。
为什么隔离级别很重要?
- 数据一致性:确保事务结束后,数据库处于一致状态。
- 数据完整性:防止出现丢失更新、脏读或冲突更改等问题。
- 并发控制:在访问数据的用户或操作数量与数据库一致性之间找到平衡。
- 性能优化:帮助在性能和严格的事务规则之间进行权衡和优化。
在生产环境中,我们要避免错误地设置隔离级别,这会造成不可预计的后果。