运用多线程批量保存数据问题导致根据id查找出现中间被插入数据情况【生产项目遇到此问题,特此记录下】
最近项目基于springboot结合mybatis的情况下,同时运用多线程的批量保存的情况下,发现了一个严重问题,代码中开启了多个线程对数据进行插入,此时在运用另一个接口对已经保存的数据进行根据id升序排序获取的情况下,会出现id出现错乱的情况。
上图中左边为一开始的查询结果,右边为过段时间后查询的结果,明显在中间插入了一段数据。
为什么出现这种问题?对事务进行复习下吧:
事务:
什么是事务?数据库事务是数据库管理系统执行过程中的一个逻辑单元,由一个有限的数据库操作序列构成。
事务的4大特性ACID:
A:原子性:事务中的所有操作要么所有都执行,要么全都不执行。
C:一致性:指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。 所谓的业务逻辑上的一致就相当于a的存款为100,b的存款为100 ,a给了b50后两人的存款和 仍然为200,并且要保证数据库定义的约束主外键约束和内部约束等。
I:隔离性:指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间,每个事务之间感受不到系统中有其他事务在并发执行。
数据库提供了一系列隔离级别,这些隔离级别按由强到弱分别为:
可串行化 > 可重复读 > 读已提交 > 读未提交
不同的隔离级别对数据库使用者的表现不一样。将事务按照一定的隔离级别隔离是并发控制的主要目标。
D:永久性,事务提交后对数据库的影响是永久的
因为插入数据的表格在建立的时候是根据id来进行自增,说明事务的处理存在着一定的问题,在批量保存数据时,所有的线程都调用了同一个事务,导致数据在提交的时候已经获取了对应的id但并没有将事务真正的进行提交。也就是事务中的操作错乱,所以根据事务的隔离性,我们可以通过调用不同的线程,创建不同的事务。
在代码中,保存的时候,调用了该注释,对该注释进行了了解,表示调用了事务,其默认值为PROPAGATION_REQUIRED具体说明如下:
事务传播行为类型
说明
PROPAGATION_REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW
新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作
那么出现该问题的原因是如何?如下:
也就造成了 线程b中的id为比较后面的数据先出现在获取的内容里面,而最后获取的时候多出来中间的id列。
而调用了PROPAGATION_REQUIRES_NEW后,每个线程独立事务,根据线程的隔离性,每个事务都进行了各种的提交,便可以解决。
————————————————
版权声明:本文为CSDN博主「乐观的大鹏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38340127/article/details/96998390