mybatis事物如何避免脏读_Spring和Mybatis架构重现脏读,不可重复读和幻读

本文深入探讨了数据库事务的隔离性,解释了脏读、不可重复读和幻读的概念,以及如何在不同隔离级别下避免这些问题。介绍了可串行化、可重复读、读已提交和读未提交四种隔离级别,并通过SQL示例展示了脏读和不可重复读的重现步骤。
摘要由CSDN通过智能技术生成

隔离(数据库系统)

在数据库系统中,隔离决定了其他用户和系统如何看到事务完整性。 例如,当用户创建采购订单并创建了表头但未创建采购订单行时,表头对其他系统/用户是否可见(执行并行操作,如采购订单报告)?

较低的隔离级别会增加许多用户同时访问相同数据的能力,但会增加用户可能遇到的并发影响(如脏读或丢失更新)的数量。 相反,更高的隔离级别减少了用户可能遇到的并发影响的种类,但需要更多的系统资源,并增加了一个事务阻塞另一个事务的可能性。

隔离通常在数据库级别定义为一个属性,该属性定义一个操作所做的更改如何/何时对其他操作可见。 在较旧的系统上,它可以通过系统实现,例如通过使用临时表。 在两层系统中,需要事务处理(TP)管理器来维护隔离。 在n层系统(例如试图预订航班最后一个座位的多个网站)中,需要结合使用存储过程和交易管理来完成预订并向客户发送确认。

当一个事务被允许从一个被另一个正在运行的事务修改但尚未提交的行读取数据时,发生脏读(又名未提交依赖)。

隔离是ACID(Atomicity 原子性, Consistency 一致性, Isolation 隔离, Durability 耐久性)的属性之一。

并发控制

并发控制包含DBMS中处理隔离并保证相关正确性的基本机制。数据库和存储引擎(见上文)大量使用它来保证并发事务的正确执行,以及(不同的机制)其他DBMS进程的正确性。与事务相关的机制通常根据数据库数据访问操作时间(事务调度)将命令分类为以可串行性和可恢复性为特征的调度属性。限制数据库访问操作执行通常意味着性能(执行率)降低,因此并发控制机制通常被设计为在约束下提供尽可能最佳的性能。通常,如果可能的话,在不损害正确性的情况下,可串行性属性会受到制约以获得更好的性能。但是,可恢复性不会受到制约,因为这通常会违反数据库完整性约束。

两阶段锁定是DBMS中最常见的事务并发控制方法,用于为数据库访问的正确性提供可串行性和可恢复性。为了访问数据库对象,事务首先需要获取该对象的锁。根据访问操作类型(例如读取或写入对象)和锁定类型,如果另一个事务正在为该对象持有锁定,则可能会阻止并推迟获取该锁定。

隔离级别

在DBMS(数据库管理系统)中的四个ACID属性中,隔离属性是最经常放松的属性。当试图保持最高级别的隔离时,DBMS通常会获取可能导致并发性丢失或实现多版本并发控制的数据锁定。这需要为应用程序添加逻辑才能正常运行。

大多数DBMS提供了许多事务隔离级别,它们控制选择数据时发生的锁定程度。对于许多数据库应用程序,可以构造大多数数据库事务以避免需要高隔离级别(例如SERIALIZABLE级别),从而减少系统的锁定开销。程序员必须仔细分析数据库访问代码,以确保放松隔离不会导致难以发现的软件错误。相反,如果使用更高的隔离级别,则会增加死锁的可能性,这也需要仔细分析和编程技术来避免。

Serializable

这是最高的隔离级别。

使用基于锁的并发控制DBMS实现时,可序列化要求在事务结束时释放读取和写入锁(在选定数据上获取)。当SELECT查询使用范围WHERE子句时,还必须获取范围锁,特别是为了避免幻像读取现象。

使用基于非锁定的并发控制时,不会获取锁定;但是,如果系统在几个并发事务中检测到写冲突,则只允许其中一个事务提交。有关此主题的更多详细信息,请参阅快照隔离

来自:(第二份非正式评审草案)ISO / IEC 9075:1992,数据库语言SQL- 1992年7月30日:在隔离级别SERIALIZABLE下执行并发SQL事务保证可序列化。可序列化的执行被定义为执行并发执行的SQL事务的操作,这些操作产生与那些相同的SQL事务的一些串行执行相同的效果。串行执行是每个SQL事务在下一个SQL事务开始之前执行完成的一次执行。

Repeatable reads

在此隔离级别中,基于锁的并发控制DBMS实现会保持读取和写入锁定(在选定数据上获取)直到事务结束。但是,范围锁不受管理,因此可能发生幻像读取。

在这个隔离级别写偏序是可能的,写偏序——Write Skew,是一种由于允许两个不同的写入器(先前读取它们正在更新的列)写入到同一表中的同一列,导致列的数据是这两个事务的混合。其根本的原因是由于每个事务在更新过程中无法看到其他事务的更改的结果,导致各个事务提交之后的最终结果违反了一致性。

Read committed

在这个隔离级别中,基于锁的并发控制DBMS实现会保持写锁(在选定数据上获取)直到事务结束,但是只要SELECT操作被执行,读锁就会被释放(所以不可重复读取现象可以发生在这个隔离级别)。与前一级一样,范围锁不受管理。

简单来说,read committed是一个隔离级别,它保证读取数据时读取的任何数据都被提交。它只是限制读者看到任何中间的,未提交的,“脏”的阅读。它不会承诺如果事务重新发布读取,它会找到相同的数据;数据读取后可以自由更改。

Read uncommitted

这是最低的隔离级别。在这个级别中,允许脏读,因此一个事务可能会看到其他事务未做出的更改。

由于每个隔离级别都比下面更强,因为没有更高的隔离级别允许较低级别禁止的操作,该标准允许DBMS以比所请求的更高的隔离级别运行事务(例如,“读取已提交”事务实际上可以在“可重复读”隔离级别执行)。

读取现象

当事务1读取事务2可能已经改变的数据时,ANSI / ISO标准SQL 92引用三种不同的读取现象。

脏读

脏读(又名未提交依赖)发生在当事务允许从某行读取数据,但该行正在被其他事务修改并且未提交的时候。

脏读的工作方式与不可重复读取类似; 但是,第二个事务不需要为第一个查询返回不同的结果。 READ UNCOMMITTED隔离级别中可能阻止的唯一事情是更新在结果中出现乱序; 也就是说,前面的更新将始终在后面更新之前出现在结果集中。

脏读sql

在我们的示例中,事务2会更改一行,但不会提交更改。 事务1然后读取未提交的数据。 现在,如果事务2回滚其更改(已由事务1读取)或更新对数据库的不同更改,则数据视图在事务1的记录中可能是错误的。

重现步骤:

1. 设置propagation="REQUIRED" isolation="READ_UNCOMMITTED";

2. 执行查询记录1,Thread.sleep(3000);

3. 在事务1休眠期间,执行事务2更新记录1;

4. 再次执行查询记录1;

5. 事务2抛出异常,发生回滚;

两次结果不一致,发生脏读。若改为isolation="READ_COMMITTED",两次结果一致。

不可重复的读

发生不可重复读取时,在事务过程中,行被检索两次,行之间的值在读取之间不同。

当执行SELECT时未获取读取锁定时,或者在执行SELECT操作时立即释放获取的受影响行上的锁定时,基于锁定的并发控制方法中可能出现不可重复读取现象。 在多版本并发控制方法下,当由提交冲突影响的事务必须回滚的要求放宽时,可能发生不可重复读取。

不可重复读sql

重现步骤:

1. 设置propagation="REQUIRED" isolation="READ_COMMITTED";

2. 执行查询记录1,Thread.sleep(3000);

3. 在事务1休眠期间,事务2更新记录1并提交;

4. 再次执行查询记录1;

两次结果不一致,发生不可重复读。若改为isolation="REPEATABLE_READ",结果一致。

幻读

在事务处理过程中,当另一个事务将新行添加到正在读取的记录时,会发生幻像读取。

在执行SELECT ... WHERE操作时没有获取范围锁定时可能会发生这种情况。 当事务1重复有范围的SELECT ... WHERE查询并且在两个操作之间,事务2创建(即INSERT)满足WHERE的新行(即,在目标表中)时,幻像读取异常是非重复读取的特例。

幻读sql

重现步骤:

1. 设置propagation="REQUIRED" isolation="REPEATABLE_READ";

2. 执行查询记录10-30,Thread.sleep(3000);

3. 在事务1休眠期间,事务2更新记录10-30并提交;

4. 再次执行查询记录10-30;

两次结果一致,没有发生幻读。有可能是一定几率的。

为了重现幻读,下面我又试了几种配置:

重现幻读尝试

注意:

数据库要关闭一级缓存,不然同样的查询,执行两次,第二次拿到的会是缓存中的数据,可以在映射mapping中这样配置:

关闭一级缓存

参考:

写偏序——Write Skew。根本的原因是由于每个事务在更新过程中无法看到其他事务的更改的结果,导致各个事务提交之后的最终结果违反了一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值