前段时间我遇到竞争条件问题时,2个单独的交易试图同时检查记录是否存在(4个字段),如果不存在 - 创建一个新的。
我的环境:
MS SQL Server
,Spring Data
/ JPA
/ Hibernate
这是一个重复的记录问题。我实现了模拟并发调用的测试,因此能够(在99.99%的执行时间内非常稳定)来重现这个问题。
现在我通过在这4个字段上应用唯一约束来修复此问题。此时我的测试无法重现此问题。我真的很高兴,但说实话,我并不完全理解它是如何工作的。这正是我创建这个问题的原因 - 我不明白为什么例如我的测试没有ConstraintViolationException
失败,而两个并发事务同时检查记录存在然后尝试创建它(每个)。根据我的测试我的理解 - 两个交易同时工作,并且在第一次检查期间不应该找到任何记录。之后,他们应该尝试创建新记录,其中一个事务应该能够执行,另一个事务应该失败ConstraintViolationException
。但代码工作正常,一切正常,没有任何例外。
Spring Data / JPA / Hibernate级别甚至MS SQL Server是否存在任何内部同步机制,可防止并发事务处理不正确的工作并允许它们等待彼此的工作结果?请解释。谢谢!
答案 0 :(得分:2)
这完全取决于您在表上拥有的索引以及当时查询使用的索引。如果不同进程使用不同的执行计划来检查行的不存在,则由于SQL Server发出资源锁定的方式,它们都将返回true并且都添加记录。
从[1]开始:
- 执行写入操作时,SQL Server不会锁定相关的操作 索引...,只有相关的数据行。
- 执行读取操作时,SQL Server仅锁定在其中找到并使用的对象(例如索引,数据行等) 它的访问路径。
醇>
通过在这4个字段上添加唯一约束,您隐式且有效地添加了覆盖索引,这导致所有进程使用相同的查询计划,因此在相同对象上以相同的顺序获取资源锁。
Data mismatch when querying with different indexes
有关资源锁定的详细信息,请阅读: [1] https://www.mssqltips.com/sqlservertip/1485/using-sql-server-indexes-to-bypass-locks/