每次有新数据进入时,我都会检查数据库,如果有与新数据相关的记录,我会得到它并根据新数据和旧记录更新记录;如果不存在记录,我将为新数据创建新记录;
问题:当有几个线程做同样的事情时,它自然会形成一个read -> check -> update
模式,导致race condition。
我尝试过以下解决方案,但两者似乎都不太好。
aFieldOrSeveralFields
添加db的唯一约束,尝试捕获SQLIntegrityConstraintViolationException
,然后重试read -> check -> update
进程;
我可以抓住Exception
,但它看起来很危险;
或者我可以通过e instanceof SQLIntegrityConstraintViolationException
检查它,让其他例外泡沫起来,但它看起来很难看;synchronised
到整个read -> check -> update
,但由于有明显的I / O操作,它似乎非常无效。最后,为了平衡丑陋的处理和有效性,我使用synchronised
将任务拆分为较小的任务以确保数据一致性,并且到目前为止没有明显的性能问题上升。
我认为你做的是正确的,但我不明白的是为什么会抛出异常? (也许你可以澄清一下)。
正如我所看到的,正确的流程如下:
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,
实际上,这取决于您的服务方案。如果您需要立即回调,有三种方法。
第一个,悲观锁定。这个用于与资金相关的场景,如账户余额。
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)
或者,也许您可以尝试使用消息队列的事件驱动模型