了解了数据库事务的隔离级别,知道在什么隔离级别下会存在哪些问题。根据不同业务对数据一致性的不同需求,在当前数据库隔离级别不能保证数据一致性的情况下,合理的编写程序代码,必要的地方加锁,才能保证程序正确运行。
假设有一个账户(account = 123)买书的事务A业务逻辑是:
1、开启事务。
2、查询账户余额是否充足,能买100元的图书。
3、如果余额充足,创建购买图书的订单。
4、更新账户余额扣减100账户余额。
5、提交事务。
在各种数据库隔离级别下会发生什么问题?
隔离级别:读未提交
如果隔离级别为读未提交,有另一个事务B:
1、开启事务。
2、扣减100账户余额。
3、事务回滚。
当事务A执行完第二步检查账户余额充足创建了订单后,事务B开始执行,事务B执行完第二步扣减账户余额后,账户余额为0,事务A在扣减余额时,因为隔离级别为读未提交,事务A查询到了事务B没有提交的修改,更新余额为-100,最终事务B 回滚了,但是账户余额被A事务错误计算为了-100,这就是因为数据库隔离级别为读未提交,发生了一个事务读取到了另一个事务未提交的数据,这就是脏读问题。
隔离级别:读已提交
如果隔离级别为读已提交,就能避免上一个场景的脏读问题,这时候如果把事务B修改为:
1、开启事务。
2、扣减100账户余额。
3、提交事务。
最后一步修改为提交事务才能对事务A造成影响。这样的话当事务A创建了订单后,事务B开始执行并提交了事务,扣减账户余额为0,事务A读取到了事务B提交后的结果后,继续更新账户余额为-100,虽然账户余额是对的,但是最终导致账户余额被扣成了负的,事务A的第二步检查账户余额失去了意义。事务A开始检查过账户余额充足但是读取到了事务B提交的结果,同一个事务A两次查询结果不一致,这就是不可重复读问题。
隔离级别:可重复读
如果隔离级别为可重复读,当A开始事务创建订单后,账户表的(account = '123’的这条数据被锁定了),事务B得等到事务A提交才能修改,就避免了上一个不可重复读的问题。这时候假如有另一个事务C是:
1、开启事务。
2、查询账户的交易记录数。
3、打印所有的交易记录数。
4、提交事务。
当事务A提交后事务C查询交易记录数为1,这时候事务B执行并提交了,事务C打印交易记录,却打印出了两条,同一个事务C内多次查询返回的结果集(交易记录条数)不一样,这就是幻读。
隔离级别:串行化
事务串行执行。可以避免以上脏读、不可重复读、幻读问题。但是效率低下。
总结
oracle的默认隔离级别是:读已提交
mysql的默认隔离级别是:可重复读
了解了数据库事务的隔离级别,知道在什么隔离级别下会存在哪些问题。根据不同业务对数据一致性的不同需求,在当前数据库隔离级别不能保证数据一致性的情况下,合理的编写程序代码,必要的地方加锁,才能保证程序正确运行。