当多个线程写入相同的表时发生死锁

时间:2013-08-19 10:19:08

标签: java spring sql-server-2008 concurrency ibatis

在我的应用程序中,我有一段代码,其中包含以下活动

1.Run脚本A,用于生成文件的数据生成 2.解析文件并从文件中读取数据
3.将数据存储在3个不同的表中,然后将该日期的现有行标记为陈旧 4.运行脚本B
5.由B生成的稀疏文件 6.将数据存储在2个不同的表中,然后将该日期的现有行标记为陈旧 7.记录表中的活动审核并发送邮件

现在,这7个步骤将针对10-12个不同的实体并行完成。平台是JAVA,Spring,Ibatis,我正在为新的每个线程做整个过程 以ISOLATION LEVEL作为READ COMMITTED进行交易。线程由ThreadPoolExecutor管理,corepoolsize = maxPoolsize为10.表的大小约为30K,为每个表写入或更新的数据量约为100行。正在使用的数据库是SQL服务器。

问题是没有@Transactional表示法按预期工作,但是当在每个线程的事务中完成7个步骤时,进程卡住并且根本不继续。 在调查处于暂停状态的查询时,事实证明3-4个查询正在尝试执行第3步并陷入“将该日期的现有条目更新为陈旧”。 这也导致锁升级,5-10分钟后整个数据库被锁定,防止任何其他操作导致死锁。
请注意,不会发生实际的死锁,操作根本不会继续。
尝试的解决方案:在表上创建非聚集索引,以帮助SQL服务器识别和锁定特定行 请提出一些解决方案或问题的可能原因。

修改

经过一番调查和分析,我找到了解决方案。在生成所有数据之后,我已经在线程结束处移动了步骤3,6,7。因此,这些步骤现在在一个单独的方法中完成,@ Transaction符号仅应用于方法,而不是在事务中执行整个线程操作。 此外,这个方法已经成为一个“同步”方法,因此即使有10个线程处于活动状态,在任何时间点只有一个1个线程会写入DB。这样可以消除多个线程试图写入DB的担忧同时。
我已经测试了这种方法,现在没有出现过前面提到的问题,只是想知道是否用 @Transactional 注释方法并使其同步很好,因为它会产生不良影响。另外,以同步方法写入DB是一种好习惯。

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

1 个答案:

答案 0 :(得分:0)

桌子上采用了什么样的锁定方案?

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