使用Java锁定或捕获数据库异常

时间:2019-02-28 04:38:23

标签: java postgresql hibernate spring-boot race-condition

每次输入新数据时,我都会检查数据库,如果有与新数据相关的记录,我会获取它并根据新数据和旧记录更新记录;如果没有记录,我将为新数据创建一个新记录;

问题:当有多个线程执行相同的操作时,它自然会形成read -> check -> update模式,从而产生race condition


我尝试了以下解决方案,但它们似乎都不太好。

  1. aFieldOrSeveralFields向数据库添加唯一约束,尝试捕获SQLIntegrityConstraintViolationException,然后重试read -> check -> update进程;

    • 我可以抓到Exception,但对我来说似乎很危险;
    • 或者我可以通过e instanceof SQLIntegrityConstraintViolationException进行检查,并让其他异常冒出来,但看上去难看;
  2. 在整个synchronised中使用read -> check -> update,但由于存在明显的I / O操作,因此似乎无效


更新于2019-03-11

最后,为了平衡丑陋的处理有效性,我使用synchronised将任务拆分为较小的任务,以确保数据的一致性,直到现在没有明显的性能问题出现。

2 个答案:

答案 0 :(得分:0)

我认为您在做正确的事情,但是我不明白为什么在这里抛出异常? (也许您可以澄清一下)。

如我所见,正确的流程是这样的:

record <- readFromDB(uniqueFields)
if record does not exist:
    record <- createRecord()
updateFields(record) //here you should update the non-unique fields
saveToDB(record)     //it shouldn't throw an exception since the unique fields didn't change

另一种可能的解决方案是根据您的用例使用ON DUPLICATE KEY UPDATE / IGNORE,详细了解here

答案 1 :(得分:0)

实际上,这取决于您的服务方案。如果您需要立即回调,可以通过三种方式。

  

第一个,悲观锁。这是用于与资金有关的情况,例如帐户余额。

select xx from xxx.. for update
//check
//update 
  

第二,乐观锁。如果您对第一个效率不满意。这可能是您的选择。同时,如果出现比赛情况,您必须负担复杂的计划。仅仅抛出一个异常不是企业计划。

select version, xxx,... from ... //version column is for optimistic lock
//check
update .... set ... where version = (the version you get above)
  

或者,也许您可​​以尝试使用带有消息队列的事件驱动模型