Memory-Optimized 表中的事务

在基于磁盘的表(使用 SNAPSHOT 隔离,或使用 READ_COMMITTED_SNAPSHOT)上的行版本控制机制提供了一种乐观并发控制形式。 读者和写入方不会互相阻止。 使用内存优化表时,编写器不会阻止编写器。 在基于磁盘的表上进行行版本控制时,一个事务将锁定该行,尝试更新该行的并发事务将被阻止。 在内存优化表中没有锁定机制。 相反,如果两个事务尝试更新同一行,将发生写入/写入冲突(错误 41302)。

与基于磁盘的表不同,内存优化表在更高隔离级别(REPEATABLE READ 和 SERIALIZABLE)下允许进行乐观并发控制。 不会采取锁来强制实施隔离级别。 相反,在事务验证结束时,可确保可重复读取或可序列化性假设。 如果违反假设,事务将终止。 有关详细信息,请参阅 事务隔离级别

内存优化表的重要事务语义包括:

  • 多版本管理

  • 基于快照的事务隔离

  • 乐观的

  • 冲突检测

以下各节将介绍其中每个语义。

Memory-Optimized 表中的多版本控制

内存优化表中的行可以具有不同的版本。 并发事务可能会访问同一行的不同版本。

内存优化的表格数据是基于版本的。 对于任何行,可能有不同的行版本在不同时间点有效。 当READ_COMMITTED_SNAPSHOT或ALLOW_SNAPSHOT_ISOLATION为 ON 时,基于磁盘的表会维护不同的行版本。 内存优化表维护不同的行版本,即使READ_COMMITTED_SNAPSHOT和ALLOW_SNAPSHOT_ISOLATION处于关闭状态。 内存优化表的行版本不会在 tempdb 中维护。 相反,行版本会作为内存优化数据结构的一部分进行内联维护,以将行存储在内存中。

Memory-Optimized 表的 Snapshot-Based 事务隔离

单个事务中的所有操作都使用相同的内存优化表的事务一致性快照。 内存优化表的所有事务隔离都是基于快照的。 例如,使用可序列化隔离级别访问内存优化表的事务将在同一事务一致的快照上执行所有操作。

访问内存优化表的事务使用此行版本控制来获取表中行的事务一致性快照。 事务中任何语句读取的数据将是事务启动时存在的数据的事务一致性版本。 因此,并发运行的事务所做的任何修改在当前事务的语句中都是不可见的。

Memory-Optimized 表的乐观并发控制

冲突和失败很少见,内存优化表上的事务假定与并发事务没有冲突,并且操作成功。 事务不对内存优化表采取锁或闩锁,以保证事务隔离。 作家不会阻挡读者。 作家不会阻碍作家。 相反,事务在假设不会与其他事务发生冲突的(乐观)前提下进行。 不使用锁和闩锁,并且不等待其他事务处理完相同的行可以提高性能。

此外,如果事务(TxA)读取了另一个正在提交过程中的事务(TxB)插入或修改的行,事务(TxA)将乐观地假设事务(TxB)会提交成功,而不是等待事务提交完成。 在这种情况下,事务 TxA 将依赖于事务 TxB。

冲突检测、验证和提交依赖项检查

SQL Server 可检测并发事务之间的冲突和隔离级别违规,并会终结其中一个冲突的事务。 需要重试此事务。 有关更多信息,请参阅 Memory-Optimized 表事务重试逻辑指南

系统乐观地假设没有冲突,也没有违反事务隔离。 如果发生任何可能导致数据库中不一致或可能违反事务隔离的冲突,则会检测到这些冲突,并终止事务。

如果检测到冲突,事务将终止,并且客户端需要重试。

下表汇总了访问内存优化表的事务的错误条件。

访问内存优化表事务时的错误条件。

错误 情景
写入冲突。 尝试更新一条自事务开始以来就已被更新的记录。 更新或删除已由并发事务更新或删除的行。
可重复读取验证失败。 事务读取的行自事务启动以来已更改(已更新或删除)。 使用 REPEATABLE READ 和 SERIALIZABLE 事务隔离级别时,通常会进行可重复读取验证。
序列化验证失败。 自事务启动以来,已在事务中的扫描范围之一中插入新的(虚拟)行。 如果行在事务启动前已提交到数据库,则该行对事务可见。 使用 SERIALIZABLE 隔离和验证 PRIMARY KEY 约束时,通常会进行 SERIALIZABLE 验证。
提交依赖项失败。 该事务依赖于另一个未能提交的事务,原因是此表中的某个故障、内存不足条件或未能提交到事务日志。 读取/写入和只读事务都可能发生此故障。

事务生存期

上表中提到的失败可能在事务期间的不同点发生。 下图说明了访问内存优化表的事务的阶段。

事务的生存期。 访问内存优化表的事务的生存期。

常规处理

在此阶段,将执行用户发出的 Transact-SQL 语句。 从表中读取行,并将新行版本写入数据库。 该事务与所有其他并发事务隔离。 该事务使用事务开始时已经存在的内存优化表的快照。

在此事务阶段对表的写入对其他事务尚不可见,但有一个例外:行更新和删除在其他事务中的更新和删除操作中是可见的,以便检测写入冲突。

如果更新或删除作业看到自事务逻辑开始以来该行已经被更新或删除,则作业将失败,并出现错误 41302。 错误 41302 的消息是“当前事务尝试更新自此事务启动以来已更新的表 X 中的记录。 交易已中止。

即使 XACT_ABORT 设为 OFF,此错误也会导致事务失败。这意味着在用户会话结束时,事务将会回滚。 无法提交失败的事务,并且仅支持不写入日志且不访问内存优化表的读取操作。

提交依赖项

在常规处理期间,事务可以读取由验证或提交阶段中的其他事务写入的行,但尚未提交。 因为在验证阶段开始时事务已经被分配了逻辑结束时间,所以这些行是可见的。

如果事务读取此类未提交的行,它将形成对该事务的提交依赖关系。 这有两个主要含义:

  • 事务只有在其所依赖的事务已提交之后,才能提交。 换句话说,在清除所有依赖项之前,它无法进入提交阶段。

  • 此外,在清除所有依赖项之前,结果集不会返回到客户端。 这可以防止客户端观察未提交的数据。

如果任何依赖事务未能提交,就会发生提交依赖关系失败。 这意味着该事务将无法提交并出现错误 41301(“当前事务所依赖的上一个事务已中止,当前事务无法再提交)。

验证阶段

在验证阶段,系统验证请求的事务隔离级别所需的假设在事务的逻辑开始和逻辑结束之间是否为 true。

在验证阶段开始时,事务将分配一个逻辑结束时间。 数据库中写入的行版本会在事务的逻辑结束时间点对其他事务可见。 有关更多信息,请查看提交依赖项

可重复读取验证

如果事务的隔离级别为 REPEATABLE READ 或 SERIALIZABLE,或者在 REPEATABLE READ 或 SERIALIZABLE 隔离下访问表(有关详细信息,请参阅事务隔离级别中关于各个操作的隔离部分),则系统会验证读取是否可重复。 这意味着它验证事务读取的那些行版本在事务逻辑结束时间仍然是有效的行版本。

如果任何行已更新或更改,则事务无法提交并出现错误 41305(“由于可重复读取验证失败导致当前事务未能提交”。

在插入、更新或删除操作之后,以及事务提交之前,删除表时也可能发生此错误。 这仅适用于本机编译的存储过程中的插入、更新或删除操作。 通过解释型 Transact-SQL 执行的此类写入操作会导致 DROP TABLE 语句被阻塞,直到事务提交才继续。

可序列化验证

可序列化验证在两种情况下执行:

  • 如果事务的隔离级别为 SERIALIZABLE,或者在 SERIALIZABLE 隔离下访问表。

  • 如果在唯一索引中插入行,例如为 PRIMARY KEY 约束创建的索引。 系统验证是否没有同时插入具有相同键的行。

系统验证数据库中没有被写入虚拟行。 对事务执行的读取操作进行评估,以确认这些读取操作的扫描范围内没有插入任何新行。

在唯一索引中插入键包括隐式读取作,以确定密钥不是重复项。 唯一索引的可序列化验证可确保在两个事务同时插入同一个键时,这些索引不能有重复项。

如果检测到虚拟行,则事务无法提交并出现错误 41325(“当前事务由于可序列化验证失败而未能提交”。

提交处理

如果验证成功且所有事务依赖项都清除,事务将进入提交处理阶段。 在此阶段,持久表的更改将写入日志,并将日志写入磁盘,以确保持久性。 将事务的日志记录写入磁盘后,控制权将返回到客户端。

清除此事务的所有提交依赖关系,所有等待该事务提交的事务都可以继续进行。

局限性

  • 内存优化表不支持跨数据库事务。 访问内存优化表的每个事务都无法访问多个数据库,但对 tempdb 的读写访问和对系统数据库主数据库的只读访问除外。

  • 内存优化表不支持分布式事务。 以 BEGIN DISTRIBUTED TRANSACTION 开头的分布式事务无法访问内存优化表。

  • 内存优化表不支持锁定。 不支持在内存优化表中通过锁定提示(如 TABLOCK、XLOCK、ROWLOCK)进行显式锁定。

另请参阅

了解 Memory-Optimized 表上的事务