每次输入新数据时,我都会检查数据库,如果有与新数据相关的记录,我会获取它并根据新数据和旧记录更新记录;如果没有记录,我将为新数据创建一个新记录;
问题:当有多个线程执行相同的操作时,它自然会形成read -> check -> update
模式,从而产生race condition。
我尝试了以下解决方案,但它们似乎都不太好。
为aFieldOrSeveralFields
向数据库添加唯一约束,尝试捕获SQLIntegrityConstraintViolationException
,然后重试read -> check -> update
进程;
Exception
,但对我来说似乎很危险; e instanceof SQLIntegrityConstraintViolationException
进行检查,并让其他异常冒出来,但看上去难看; 在整个synchronised
中使用read -> check -> update
,但由于存在明显的I / O操作,因此似乎无效。
最后,为了平衡丑陋的处理和有效性,我使用synchronised
将任务拆分为较小的任务,以确保数据的一致性,直到现在没有明显的性能问题出现。
答案 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)
或者,也许您可以尝试使用带有消息队列的事件驱动模型