数据库隔离级别定义了一个事务中的操作与其他并发事务之间相互隔离的程度。在多用户环境中,它们在管理事务之间的交互、维护数据的完整性和一致性方面至关重要。
隔离级别的重要性在于它平衡了数据一致性和性能。较高的隔离级别可以确保更好的数据完整性,但可能会降低性能;而较低的隔离级别则提高了性能,但可能会引发异常。
如果没有适当的隔离,可能会发生以下问题:
- 脏读:一个事务读取了另一个未提交事务写入的数据,而这些数据可能会被回滚。
- 不可重复读:一个事务两次读取同一行数据,发现值不同,因为另一个事务在两次读取之间修改并提交了该行数据。
- 幻读:一个事务检索满足某个条件的一组行,但在后续检查时发现这一组行已经发生变化,因为另一个事务插入或删除了行。
下图说明了四种隔离级别。
- 可序列化(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 的数据。
为什么隔离级别很重要?
- 数据一致性:确保事务结束后,数据库处于一致状态。
- 数据完整性:防止出现丢失更新、脏读或冲突更改等问题。
- 并发控制:在访问数据的用户或操作数量与数据库一致性之间找到平衡。
- 性能优化:帮助在性能和严格的事务规则之间进行权衡和优化。
在生产环境中,我们要避免错误地设置隔离级别,这会造成不可预计的后果。