事例:Sping动态项目,配置定时任务,使用cron表达式时,配置了此定时任务的事务为REQUIRES_NEW,在定时任务最后启动一个异步线程对定时任务前面入库数据进行一些处理;

现象为:

在调试状态下,业务逻辑,最终数据处理结果都正常;

实际环境中:系统定时任务按时执行,但是数据处理结果永远不正确

 

排除问题:(1)首先以为代码异常,查看代码有没有逻辑错误,

           本地断点调试,远程断点调试,数据处理都正常,(心中一群草泥马飘过);

       去除断点,本地和远程运行,问题在现;

          分析区别:由于断点;

       (2)排除代码逻辑问题,考虑数据入库问题,获取日志,分析日志业务流程运行顺序;

                     通过对日志的分析,发现定时任务是线程pool-4-thread-1,启动的异步线程是 pool-7-thread-4,发现在异常线程执行时,对定时任务入库的数据查询结果返回为空,没有查到,也就是说,

        定时任务入库的数据没有被异步线程读取到,这个是什么原因呢?

        找了资料,发现问题出在Spring事务级别上,REQUIRES_NEW代表创建一个新事务,如果存在则暂停当前事务。

        而在此例子中出现的原因是此时出现了幻读,mysql默认的隔离级别为 Repeatable read 重复读,该级别会产生幻读现象。简单来讲就是定时任务入库数据尚未真正入库(入库事务有spring事务,数据库事务,只有当这两个事务都结束,才代

        表数据真正可查),异步任务就在读取结果了,此时表中是没有数据的。

 

知识补充:以下内容来自博客:https://blog.csdn.net/cy_7030/java/article/details/91802949

1数据库事务的隔离级别:

     数据库事务的隔离级别有4个,由低到高依次为Read uncommitted( 读未提交)、Read committed(读提交)、Repeatable read(重复读)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。 

    READ UNCOMMITTED
      READ UNCOMMITTED是限制性最弱的隔离级别,因为该级别忽略其他事务放置的锁。使用READ UNCOMMITTED级别执行的事务,可以读取尚未由其他事务提交的修改后的数据值,这些行为称为“脏”读。这是因为在Read Uncommitted级别下,

    读取数据不需要加S锁,这样就不会跟被修改的数据上的X锁冲突。比如,事务1修改一行,事务2在事务1提交之前读取了这一行。如果事务1回滚,事务2就读取了一行没有提交的数据,这样的数据我们认为是不存在的。

    READ COMMITTED

      READ COMMITTED(Nonrepeatable reads)是SQL Server默认的隔离级别。该级别通过指定语句不能读取其他事务已修改但是尚未提交的数据值,禁止执行脏读。在当前事务中的各个语句执行之间,其他事务仍可以修改、插入或删除数据,从而

    产生无法重复的读操作,或“影子”数据。比如,事务1读取了一行,事务2修改或者删除这一行并且提交。如果事务1想再一次读取这一行,它将获得修改后的数据或者发现这一样已经被删除,因此事务的第二次读取结果与第一次读取结果不同,因此也

    叫不可重复读

    REPEATABLE READ
      REPEATABLE READ是比READ COMMITTED限制性更强的隔离级别。该级别包括READ COMMITTED,并且另外指定了在当前事务提交之前,其他任何事务均不可以修改或删除当前事务已读取的数据。并发性低于 READ COMMITTED,因为

    已读数据的共享锁在整个事务期间持有,而不是在每个语句结束时释放。比如,事务1读取了一行,事务2想修改或者删除这一行并且提交,但是因为事务1尚未提交,数据行中有事务1的锁,事务2无法进行更新操作,因此事务2阻塞。如果这时候事务1

    想再一次读取这一行,它读取结果与第一次读取结果相同,因此叫可重复读。

    REPEATABLE READ隔离级别保证了在相同的查询条件下,同一个事务中的两个查询,第二次读取的内容肯定包换第一次读到的内容。

    SERIALIZABLE 

      SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。该级别包括REPEATABLE READ,并增加了在事务完成之前,其他事务不能向事务已读取的范围插入新行的限制。比如,事务1读取了一

    系列满足搜索条件的行。事务2在执行SQL statement产生一行或者多行满足事务1搜索条件的行时会冲突,则事务2回滚。这时事务1再次读取了一系列满足相同搜索条件的行,第二次读取的结果和第一次读取的结果相同。

    隔离级别与锁的关系
      在Read Uncommitted级别下,读操作不加S锁;
      在Read Committed级别下,读操作需要加S锁,但是在语句执行完以后释放S锁;
      在Repeatable Read级别下,读操作需要加S锁,但是在事务提交之前并不释放S锁,也就是必须等待事务执行完毕以后才释放S锁。
      在Serialize级别下,会在Repeatable Read级别的基础上,添加一个范围锁。保证一个事务内的两次查询结果完全一样,而不会出现第一次查询结果是第二次查询结果的子集。

 

 

        

posted on 2020-04-09 10:51  探路_先锋  阅读(2823)  评论(0编辑  收藏  举报
……