当前位置: 首页 > 知识库问答 >
问题:

如何在使用spring提交更新后锁定select并释放锁?

孙福
2023-03-14

我从过去几个月开始使用spring,我有一个关于交易的问题。我在我的spring批处理作业中有一个java方法,它首先执行一个select操作以获取前100行,状态为“未完成”,并对所选行进行更新以将状态更改为“进行中”。由于我正在处理大约1000万条记录,我想运行批处理作业的多个实例,每个实例都有多个线程。对于单个实例,为了确保两个线程不会获取同一组记录,我将方法设置为同步。但是,如果我运行批处理作业的多个实例(多个JVM),即使我使用“乐观”或“悲观锁定”或“选择更新”,因为我们无法在选择期间锁定记录,这两个实例也很可能会获取相同的记录集。下面是所示的示例。事务1获取了100条记录,同时事务2也获取了100个记录,但如果我启用了锁定,事务2将等待事务1更新并提交。但事务2再次执行相同的更新。

Spring有没有办法让事务2的选择操作等待事务1的选择完成?

Transaction1        Transaction2
fetch 100 records   
                    fetch 100 records
update 100 records



 commit         
                    update 100 records
                    commit



@Transactional
public synchronized List<Student> processStudentRecords(){
List<Student> students = getNotCompletedRecords();
if(null != students && students.size() > 0){
    updateStatusToInProgress(students);
}
return student;
}

注意:我不能先执行更新,然后选择。如果有任何替代方法建议,我将不胜感激。

共有1个答案

商昂然
2023-03-14

事务同步应该留给数据库服务器,而不是在应用程序级别进行管理。从数据库服务器的角度来看,无论您有多少JVM(线程),它们都是请求读/写操作的并发数据库客户端。你不应该为这种担心而烦恼。

不过,您应该做的是在解决方案的设计中尽量减少争用,例如,通过使用(远程)分区技术。

如果我运行批处理作业的多个实例(多个JVM),即使我使用“乐观”或“悲观锁定”或“选择更新”,也很有可能两个实例都会获取相同的记录集,因为我们无法在选择期间锁定记录

对数据进行分区将从设计上消除所有这些问题。如果您给每个实例一组数据来处理,那么一个工人就不可能选择另一个工人的相同记录。迈克尔在这个回答中举了一个详细的例子:https://stackoverflow.com/a/54889092/5019386.

(逻辑)分区并不能解决争用问题,因为所有的工作线程都将从同一个表中读取数据或向同一个表中写入数据,但这就是您试图解决的问题的本质。我要说的是,在你的设计中,你不需要开始锁定/解锁表,把这个留给数据库。一些数据库服务器,比如Oracle,可以将同一个表的数据写入磁盘上的不同分区,以优化并发访问(如果您使用分区,这可能会有所帮助),但这仍然是Oracle的事情,而不是Spring(或任何其他框架)的事情。

不是每个人都买得起Oracle,所以我会在概念层面寻找解决方案。我已经成功地使用了以下解决方案(“伪”物理分区)来解决与您类似的问题:

  • 步骤1(串行):将未处理的数据复制/分区到临时表(串行)
  • 步骤2(并行):在这些表上运行多个工作程序,而不是在具有数百万行的源表上运行
  • 步骤3(串行):将处理后的数据复制/更新回原始表

步骤2解决了争用问题。通常,与步骤2相比,(步骤1和步骤3)的成本可以忽略不计(如果步骤2是连续进行的,则成本更可忽略不计)。如果处理是瓶颈,这种方法很有效。

希望这有帮助。

 类似资料:
  • 问题内容: 在生产数据库中,我们每小时运行以下伪代码SQL批查询: 现在,此查询本身并不需要很快,但是我注意到它已经被锁定,即使它只是从中读取。这使得其他一些非常简单的查询需要大约25秒(这是其他查询所花费的时间)。 然后我发现InnoDB表实际上是由SELECT锁定的!https://www.percona.com/blog/2006/07/12/insert- into-select-perf

  • 我的问题类似于这个问题MySQL在连接丢失/断开的情况下回滚事务,但那是5年前的事了。 如果一个客户端(比如jdbc或其他什么)锁定了表中的一行,执行一些语句,那么网络就关闭了,所以mysql永远不会从客户端收到或命令,mysql是否支持自动回滚这个事务(解锁行)? 我引用它说,但是有多长时间以及在哪里设置它? 在类似的问题中,公认的答案是使用,如果设置为10秒这样的小数字,那么池中的空闲连接(如

  • 问题内容: 我在使用Java在Windows中删除文件时遇到一些问题。由于某种原因,java会锁定我的文件,但我不知道为什么。这是我的代码: file.delete()以及在资源管理器中手动尝试拒绝删除该文件,因为它仍在使用中。尽管在Linux中一切似乎都很好。 我在某处缺少close()吗?我可以确认首先使文件成为关闭文件的方法,因为我可以在使用file.delete()运行上述代码之前删除文件

  • 预备条件: Off-By-One 漏洞(基于栈) VM 配置:Fedora 20(x86) 继续使用已经被释放的堆内存指针叫做释放后使用。这个漏洞会导致任意代码执行。 漏洞代码: 编译命令: $gcc -o vuln vuln.c $sudo chown root vuln $sudo chgrp root vuln $sudo chmod +s vuln 注意:不像上一篇文章,ASLR 在这里是

  • 这是我的伪代码: 我想问,如果条件等于“key”的行已经被删除,那么“select for update”阻止的锁是否可以自动解锁,这意味着如果另一个进程在此点进入并选择相同的“key”,它就不能被此进程阻止?

  • 我在JTA和Wildfly8中使用Eclipselink,问题是在提交用户事务并将数据插入数据库后,它不会更新相关实体,所以如果我重新加载页面或打开页面的新实例,它加载数据时不会插入行。