问题标签 [read-committed-snapshot]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
1 回答
84 浏览

sql-server - tsql 等待更改,读取已提交快照打开

嗨 Stackoverflow 社区,

让我请求您的帮助,因为我确实遇到了一个关键问题。

我们有两个链接服务器,都是 Microsoft SQL Server:CRM 和 DW 服务器。CRM系统的一些变化会触发一个程序来立即获取DW服务器的更新,它的工作方式是CRM系统调用DW服务器来更新记录。在我的情况下,来自CRM 和 DW sql 服务器的CRM 系统的更新被称为同时,问题就从这里开始了。

DW 服务器仅在事务开始之前尝试读取更改并获取记录。是的,这是因为 CRM 服务器使用:

读取提交的快照

不幸的是,我们无法更改 CRM sql 服务器上的隔离级别。简单的解释——CRM 来自第三方提供商,他们想限制我们做出这些可能性。

有没有其他方法,等待事务提交,然后在提交后读取最新数据?

如果缺少信息,请告诉我,然后我将提供更多见解。

0 投票
1 回答
42 浏览

sql - 在 RCSI 下并行插入不存在的行

我尝试将一些行加载到目标表中,但前提是它们尚不存在。一个经典的左连接查询解决了这个问题:

此插入从不同用户并行运行多次,这些用户大部分时间都尝试插入相同的数据。

我看到来自不同用户的表中有很多重复项(SomeData 列上还没有唯一约束),这很奇怪,因为左连接应该检测到这一点并防止插入已经在表中的数据。

我调查并看到数据库处于 RCSI(读取提交的快照隔离)级别。我的想法是所有并行会话都认为他们是第一个插入数据的,并且都成功地做到了。

我怎么能在 RCSI 下插入呢?如果有一个会话已经在执行其左连接插入内容,我希望每个会话都等待。

任何帮助表示赞赏,谢谢!PS:不幸的是,我不允许更改数据库隔离级别。

0 投票
2 回答
213 浏览

sql-server - SQL Server - 启用读取已提交快照时处理事务的方式不一致

概述:

SQL 似乎在执行更新时从另一个会话中的不完整事务中读取“脏”信息,即使我们试图只读取已提交的数据。一些非常具体的标准必须是真实的,这个问题才会发生(很难重现 - 但我们可以在这里这样做)。

如何重现行为

SQL2017 或 SQL2019 或 Azure SQL

  1. 确定要测试的数据库并打开 Read Committed Snapshot。(设置 READ_COMMITTED_SNAPSHOT ON WITH NO_WAIT)

  2. 在数据库中运行 ScenarioPrep.SQL 脚本(如下)以创建测试对象。

    • 这将创建 2 个表——一个“父”表和一个“子”表。
    • 请注意,父表有两个条目,“地球”和“火星”
    • 请注意,子表有 1 个条目,其父行的 id 名为“Earth”。火星没有孩子。
  3. 首先打开两个附加的 SSMS 会话,准备同时运行剩余的两个脚本(如下),其中 One.sql 在一个窗口中,Two.sql 在另一个窗口中。

    • 请注意,One.sql 设置初始条件,然后在事务中包含两个更新语句。
    • 请注意,Two.sql 尝试更新也在第一个脚本中更新的记录。
  4. 执行 One.sql 脚本。此脚本在事务中间有一个延迟,以帮助重现测试条件。

  5. 在 One.sql 运行时(特别是在延迟期间),在第二个窗口中执行 Two.sql。

  6. 请注意第二个窗口中的意外结果。地球记录意外更新。更新不应该成功,因为在任何时候都没有一个已提交的事务,其父“地球”记录的状态为 2,子记录的状态为 0。在事务之前,地球及其子记录都在状态为 0。交易后,地球和它的孩子都处于状态 2。

但是,Two.sql 确实成功更新了“地球”记录,因为它以某种方式将父级读取为状态为 2,而子级在延迟期间读取为状态为 0——但此时这不是一个提交的事务. 这是更新发生时不应该看到的“脏”状态。更新应该看到在另一个会话中提交事务之前或之后的状态。

预期的结果是 Two.sql 脚本不会更新任何记录,因为在任何时候都没有一个 COMMITTED 事务,其中 earth 处于状态 2 而它的孩子处于状态 0。事实上,在大多数情况下,这证明是真的。

观察

仅当以下所有条件都为真时,才会出现此问题:

  1. 数据库处于已提交读快照模式。
  2. 火星记录存在(即使它不受任何交易的影响
  3. Mars 记录的状态为 2(部分匹配 Two.sql 中的 where 子句)
  4. Mars 记录在表中比 Earth 记录更早(具有较低的主键值)。
  5. 执行的操作是 UPDATE(用 select 替换更新会产生预期的结果)。
  6. 更新发生在跨两个或多个表的联接期间。
  7. Two.SQL 的事务模式是 Read Committed(具有讽刺意味的是,Read Uncommitted 正确阻塞,直到另一个事务完成并按预期工作)。
  8. 当然,在第一个事务中必须有足够的延迟才能发生测试场景。

如上所述,以下任何更改都会导致问题消失: 更改父表中 Mars 和 Earth Record 的顺序。从父表中删除 mars 记录(证明不相关的记录会影响另一个记录的更新方式。)将 Mars 记录置于 2 以外的状态。将 Update 更改为其他一些操作,例如 select。开启已提交读快照模式。

当然,从 One.sql 中的两个更新语句中删除事务会导致问题一直发生,但在这种情况下会发生这种情况。将两个更新语句包装在事务中的全部目的是避免这种情况。

回购代码

以下是重现该行为所需的三个代码文件:

场景准备.sql:

一.sql:

二.Sql: