数据库事务

作者:海鹰
此教程讲述数据库事务并发可能发生的问题,以及四种隔离级别
版权所有,未经允许,请勿随意转载。

事务并发可能发生的问题

丢失更新( Lost Update )

表现:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在,最后的更新将重写其它事务提交的更新,这将导致数据丢失。

解决方法:对行加锁,只允许一个更新事务。

第一类丢失更新

示例

时间 取款事务A 存款事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 汇入100元把余额改为1100元
T6 提交事务
T7 取出100元把余额改为900元
T8 撤销事务
T9 余额恢复为1000元(丢失更新)
T10 结束 结束
第二类丢失更新

示例

时间 取款事务A 转账事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 取出100元把余额改为900元
T6 提交事务
T7 汇入100元
T8 提交事务
T9 把余额改为1100元(丢失更新)
T10 结束 结束

脏读( Dirty Read )

表现:读取到其他事务中未提交的数据。

解决方法:如果在第一个事务提交前,任何其它事务不可读取其修改的值。

示例

时间 取款事务A 转账事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 汇入100元把余额改为1100元
T5 查询账户余额为1100元(读取脏数据)
T6 回滚
T7 取款1100元
T8 提交事务失败
T9 结束 结束

不可重复读( Non-repeatable Read )

表现:在一个事务中前后两次读取的结果不一致,导致了不可重复读。

解决方法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题

示例

时间 取款事务A 转账事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 汇入100元把余额改为1100元
T5 提交事务
T6 查询账户余额为1100元
T7 提交事务
T8 结束 结束

幻读( Phantom Read )

表现:当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入新记录或删除旧记录,当其它事务再次读取某个范围内的记录时,会产生幻行(Phantom Row)。

解决方法:如果在操作事务完成数据处理之前,任何其它事务都不可以添加新数据,则可以避免该问题。

示例

时间 查询学生事务A 插入新学生事务B
T1 开始事务
T2 开始事务
T3 查询学生为10人
T4 插入一个新学生
T5 查询学生为11人
T6 提交事务
T7 提交事务
T8 结束 结束

事务隔离级别

未提交读( READ UNCOMMITTED )

在该级别,事务中的修改,即使没有提交,对其它事务都是可见的。因此,在这个级别会导致脏读

这个级别会导致很多问题,从性能上来说,这个级别不会比其它的级别好太多,但却缺乏其它级别的好处,除非真的有非常的必要,在实际应用中很少使用。

提交读( READ COMMITTED )

一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其它事务都是不可见的。在这个级别会导致不可重复读

可重复读( REPEATABLE READ )

该级别保证了在同一个事务中多次读到同样记录的结果是一致的。

在这个级别无法解决幻读问题。

可串行化( SERIALIZABLE )

最高的隔离级别,强制事务串行执行。

这个级别会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题,实际应用中也很少用到这个隔离级别。

只有在非常需要确保数据的一致性,而且可以接受没有并发的情况下,才考虑该级别。

总结

隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读
READ UNCOMMITTED YES YES YES NO
READ COMMITTED NO YES YES NO
REPEATABLE READ NO NO YES NO
SERIALIZABLE NO NO NO YES

结语

本教程到此结束,欢迎指正,互相交流。
本教程中的示例来源于马士兵大神的视频教程。
版权所有,未经允许,请勿随意转载。