我的 SQL 查询会使用陈旧数据吗?我该如何预防?

Max*_*cob 6 sql-server concurrency isolation-level

我有两个表 ( SJob& SJobDependent),我需要为存储过程中的某些逻辑加入它们。它们都有一列 ( job)以一对多关系连接它们- 一条SJob记录对应零个或多个SJobDependent记录。

这是我的 SQL 查询:

-- Return any records that are active and have no unsatisfied dependencies.
SELECT * FROM SJob
LEFT JOIN SJobDependent
    ON SJob.job = SJobDependent.job
    AND SJobDependent.satisfied = 0
WHERE SJobDependent.jobDependentID IS NULL
AND SJob.state = 'active'
Run Code Online (Sandbox Code Playgroud)

这是SQL Server Studio的实际执行计划

SQL Server Studio 实际执行计划

由于代码的编写方式:

-- Return any records that are active and have no unsatisfied dependencies.
SELECT * FROM SJob
LEFT JOIN SJobDependent
    ON SJob.job = SJobDependent.job
    AND SJobDependent.satisfied = 0
WHERE SJobDependent.jobDependentID IS NULL
AND SJob.state = 'active'
Run Code Online (Sandbox Code Playgroud)

我担心在 SQL 查询运行时可能会发生这种情况:

  1. 扫描 SJobDependent。
  2. 已插入 SJobDependent 记录。
  3. 开始扫描 SJob。SJob.state 已“准备就绪”。
  4. SJob 已更新。这会阻止读取 SJob?
  5. SJob 的结束扫描。SJob.state 是“活动的”。

我担心的问题是我的 SQL 查询返回SJob在“活动”状态 ( SJob.state = 'active') 中找到的记录,但无法看到相关SJobDependent记录。

这个问题是否会发生,还是我过度分析了 SQL 查询?

如果这是一个值得担心的合理问题,我可以做些什么来解决它?我愿意接受解决方案。

一个想法我已经很给力的扫描SJobDependent的扫描后发生SJob。这甚至可能吗?这样做的影响/后果是什么?

实际执行计划中显示的扫描是按特定顺序发生的,还是总是随机调用?

注:由于在AMtwo的回答指出,可重复读隔离级别可能会解决我的问题,因为事实上,只需要在效果的读取开始

AMt*_*two 8

如果您在 SQL Server 中使用默认隔离级别(已提交读),那么您肯定会遇到有关不一致读取的各种问题。Paul White 描述了这里的问题。

如果您希望读取查询读取的数据与它在给定时间点的外观完全一致,我建议您考虑读取提交的快照隔离(RCSI)。使用 RCSI,您的查询将返回与单个时间点(查询的开始)一致的数据。如果用户 A 开始SELECT查询,而用户 B 正在并发执行更新,则用户 A 将读取“旧”值,因为它将读取数据的快照,这与查询开始时一致。

RCSI 的问题在于它是一个数据库级别的设置。与 Read Uncommitted 不同,您不能将其设置为会话范围的设置。在进行更改之前,您必须更全面地考虑此更改。但是一般来说,如果您需要对该查询进行一致的读取,则您可能希望对整个应用程序进行一致的读取。

虽然可重复读取隔离级别可能看起来很有吸引力来解决您的问题,但请注意链接帖子中的此详细信息:

可重复读隔离级别保证数据 在第一次读取后在事务的生命周期内不会更改

这意味着在访问数据之前仍然可以更改数据,但是在您的查询运行期间。它也受到一些与 Read Committed 隔离级别相同的不一致读取的影响——尤其是幻像。