Java 多个线程写入同一个表时发生死锁

Java 多个线程写入同一个表时发生死锁,java,spring,sql-server-2008,concurrency,ibatis,Java,Spring,Sql Server 2008,Concurrency,Ibatis,在我的应用程序中,我有一段代码,其中发生了以下活动 1.运行脚本A生成数据,生成文件 2.解析文件并从文件中读取数据 3.将数据存储在3个不同的表中,并在存储之前将该日期的现有行标记为过时。 4.运行脚本B 5.解析B生成的文件 6.将数据存储在两个不同的表中,并在存储之前将该日期的现有行标记为过时。 7.在表中记录活动审核并发送邮件 现在,这7个步骤将针对10-12个不同的实体并行完成。平台是JAVA、Spring、Ibatis,我以一种新的方式为每个线程执行整个过程 隔离级别为读取已提交的事

在我的应用程序中,我有一段代码,其中发生了以下活动

1.运行脚本A生成数据,生成文件
2.解析文件并从文件中读取数据
3.将数据存储在3个不同的表中,并在存储之前将该日期的现有行标记为过时。
4.运行脚本B
5.解析B生成的文件
6.将数据存储在两个不同的表中,并在存储之前将该日期的现有行标记为过时。
7.在表中记录活动审核并发送邮件

现在,这7个步骤将针对10-12个不同的实体并行完成。平台是JAVA、Spring、Ibatis,我以一种新的方式为每个线程执行整个过程 隔离级别为读取已提交的事务。线程由ThreadPoolExecutor管理,corepoolsize=maxPoolsize为10。表的大小约为30K,为每个表写入或更新的数据量约为100行。正在使用的数据库是SQL server

问题是如果没有@Transactional符号,事情就会按预期进行,但是当每个线程在事务中完成7个步骤时,流程就会卡住,无法继续。 在调查处于挂起状态的查询时,发现有3-4个查询试图执行步骤3,并陷入“将该日期的现有条目更新为过时”的困境。 这也会导致锁升级,5-10分钟后整个数据库都会被锁定,从而防止任何其他操作导致某种死锁。
请注意,实际的死锁不会发生,操作不会继续。
尝试的解决方案:在表上创建非聚集索引以帮助SQL server识别和锁定特定行。
请提出一些解决方案或可能的原因

编辑

经过一些调查和分析,我想出了一个解决办法。在生成所有数据之后,我移动了线程末尾的步骤3、6、7。因此,这些步骤现在是在一个单独的方法中完成的,@Transaction表示法只应用于该方法,而不是在一个事务中执行整个线程操作。 此外,此方法已成为“同步”方法,因此,即使有10个线程处于活动状态,在任何时间点都只有1个线程将写入DB。因此,这消除了多个线程同时尝试写入DB的担忧。
我已经测试过这种方法,前面看到的问题现在都不存在了,但我只是想知道,同时使用@Transactional注释一个方法并使其同步是否可以,因为这样会产生不良影响。此外,以同步方法写入DB也是一种良好的做法。

对于那些对先前问题的实际原因感兴趣的人,我已经提到了一个可能的原因,这个原因可能是正确的,也可能是不正确的。
线程卡住的一个可能原因是线程池执行器的最大大小为10个线程,而Apache DBCP池的最大大小为8个DB连接。因此,当第一个线程进入一个事务并保持空闲超过5分钟(minEvicatableTime)时,它被逐出,其连接被提供给另一个线程。现在,获得连接的其他线程无法写入数据,因为表被第一个线程锁定,而第一个线程无法完成,因为它没有DB连接。

在表上应用了什么样的锁定方案

我们有一个类似的应用程序,用户可以批量上传表格中的excel表格。
为了防止这种死锁,我们为该用户预订任务,直到他完成任务为止。在此期间,没有其他用户可以使用相同的任务。

这与或有关吗?我不知道问题是由于锁升级还是由于事务未提交。理想情况下,当更新的行数远小于5000时,不应发生锁升级。也许您应该发布一些代码,然后检查上述活动是如何实现的,尤其是对于3、6、7。并且可能添加任何与事务相关的配置(如果有)。步骤3、6、7是使用Ibatis的批更新/写入。我已经用我找到的解决方案编辑了这个问题。不,我们不能采用这种方法,因为在步骤1中发生的数据生成部分需要很多时间,对于某些实体来说需要1个多小时,所以我们希望使过程并行。