我从最近几个月开始使用spring,对交易有疑问。我的春季批处理作业中有一个Java方法,该方法首先执行选择操作以获取状态为“未完成”的前100行,并对所选行进行更新以将状态更改为“进行中”。由于我正在处理大约1000万条记录,因此我想运行批处理作业的多个实例,并且每个实例都有多个线程。对于单个实例,为了确保两个线程不会获取相同的记录集,我将我的方法设置为同步化。但是,如果我运行批处理作业的多个实例(多个JVM),则即使我使用“乐观锁”或“悲观锁”或“选择更新”,两个实例仍可能会提取相同的记录集,因为选择期间我们无法锁定记录。下面是显示的示例。事务1已获取100条记录,而事务2也已获取100条记录,但是如果启用锁定,事务2将等待直到更新并提交事务1。但是事务2再次进行相同的更新。
在春季,是否有任何方法可以使事务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;
}
注意:我无法先执行更新,然后再选择。如果建议使用其他方法,我将不胜感激。
答案 0 :(得分:0)
事务同步应留给数据库服务器,而不应在应用程序级别进行管理。从数据库服务器的角度来看,无论您有多少个JVM(线程),它们都是并发数据库客户端,要求进行读/写操作。您不应该为此类问题而烦恼。
您应该做的是尝试在解决方案的设计中尽量减少争用,例如,使用(remote) partitioning technique。
如果我运行批处理作业的多个实例(多个JVM),则即使我使用“乐观锁”或“悲观锁”或“选择更新”,这两个实例也有可能提取相同的记录集”,因为我们无法在选择过程中锁定记录
分区数据将设计消除所有这些问题。如果为每个实例提供一组要处理的数据,则一个工作人员将不可能选择与另一个工作人员相同的记录。迈克尔在这个答案中给出了一个详细的示例:https://stackoverflow.com/a/54889092/5019386。
但是(逻辑)分区将不能解决争用问题,因为所有工作程序都将在同一张表中进行读取/写入,但这就是您要解决的问题的本质。我的意思是,您无需在设计中开始锁定/解锁表,只需将其留给数据库即可。一些数据库服务器(例如Oracle)可以将同一表的数据写入磁盘上的不同分区,以优化并发访问(如果使用分区,这可能会有所帮助),但这又是Oracle的业务,而不是Spring的业务(或任何其他框架)。
并不是每个人都能负担得起Oracle,因此我会在概念上寻求解决方案。我已成功使用以下解决方案(“伪”物理分区)解决了与您类似的问题:
步骤2消除了争用问题。通常,与第2步相比,(第1步+第3步)的成本可以忽略不计(如果连续进行第2步,则成本甚至可以忽略不计)。如果处理是瓶颈,这会很好地工作。
希望这会有所帮助。