MSSQL的更改跟踪功能

0    164    1

Tags:

👉 本文共约18735个字,系统预计阅读时间或需71分钟。

简介

更改跟踪是一种轻量型解决方案,它为应用程序提供了一种有效的更改跟踪机制。 通常,若要使应用程序能够查询对数据库中的数据所做的更改和访问与这些更改相关的信息,应用程序开发人员必须实现自定义更改跟踪机制。 这些机制通常涉及多项工作,并且常常涉及触发器、“时间戳”列和新表组合来存储跟踪信息,同时还会涉及使用自定义清理过程。

不同类型的应用程序对其所需的有关更改的信息量有不同的要求。 应用程序可以使用更改跟踪来回答以下有关对用户表所做更改的问题:

  • 用户表中有哪些行发生了更改?
    • 所需的只是行已更改的事实,而不是行更改的次数或任何中间更改的值。
    • 可以从所跟踪的表中直接获取最新的数据。
  • 某行是否已更改?

    • 当在同一事务中进行更改时,必须提供并记录行已更改的事实以及有关这一更改的信息。

    备注

如果应用程序需要有关所有所做更改的信息以及所更改数据的中间值,则可能适合使用变更数据捕获,而不适合使用更改跟踪。 有关详细信息,请参阅关于变更数据捕获 (SQL Server)

单向和双向同步应用程序

需要将数据与 SQL Server 数据库引擎实例同步的应用程序必须能够查询更改。 更改跟踪可用作单向和双向同步应用程序的基础。

单向同步应用程序

可以生成使用更改跟踪的单向同步应用程序,如客户端或中间层缓存应用程序。 如下图所示,缓存应用程序要求在 数据库引擎 中存储数据并在其他数据存储区中缓存数据。 应用程序必须能够使用对数据库表所做的任何更改来使缓存保持最新。 没有要传回到的 数据库引擎的更改。

Diagram showing one-way synchronization applications.

双向同步应用程序

也可以生成使用更改跟踪的双向同步应用程序。 在此方案中, 数据库引擎 实例中的数据与一个或多个数据存储区同步。 可以更新这些存储区中的数据,并且这些更改必须再同步到 数据库引擎中。

Diagram showing two-way synchronization applications.

偶尔连接的应用程序就是双向同步应用程序的一个很好的示例。 在这种类型的应用程序中,客户端应用程序查询并更新本地存储区。 当客户端与服务器之间存在连接时,应用程序会与服务器同步,并更改两个方向的数据流。

双向同步应用程序必须能够检测冲突。 如果在两次同步之间的时间两个数据存储区中的相同数据发生了更改,则会出现冲突。 有了检测冲突的功能,应用程序可以确保不会丢失这些更改。

更改跟踪的工作方式

要配置更改跟踪,可以使用 DDL 语句或 SQL Server Management Studio。 有关详细信息,请参阅 启用和禁用更改跟踪 (SQL Server)。 若要跟踪更改,必须首先对数据库启用更改跟踪,然后对该数据库内要跟踪的表启用更改跟踪。 表定义无需任何更改,也不会创建任何触发器。

为表配置了更改跟踪后,任何影响该表中的行的 DML 语句都将导致针对每个有所修改的行的更改跟踪信息被记录下来。 若要查询已更改的行并获取有关这些更改的信息,可以使用 更改跟踪功能

主键列的值是来自所跟踪的并记录更改信息的表中的唯一信息。 这些值用于标识发生更改的行。 要获取这些行的最新数据,应用程序可以使用主键列值联接源表和所跟踪的表。

使用更改跟踪也可以获取与每个行所做更改相关的信息。 例如,导致更改(插入、更新或删除)的 DML 操作的类型或作为更新操作的一部分而更改的列。

更改跟踪清理

所有表(已启用更改跟踪功能)的更改跟踪信息存储在内存中行存储中。 与每个表(已启用更改跟踪功能)关联的更改跟踪数据会在每个检查点从内存中行存储刷新到磁盘上的相应内部表。 检查点的内存中行存储还会在行移到磁盘上的表之后进行清除。

启用更改跟踪功能的每个表都有一个磁盘上的内部表,更改跟踪功能可以使用该表来确定更改版本以及自特定版本以来更改过的行。 每次苏醒时,“自动清除”线程就会扫描 SQL Server 实例上的所有用户数据库,标识启用了更改跟踪的数据库。 磁盘上的每个内部表都会根据数据库的保持期设置清除其过期记录。

在 SQL Server 2014 (12.x) 和 SQL Server 2016 (13.x) 的 Service Pack 中增加了存储过程,用于对更改跟踪内部表执行手动清理。 KB173157 中提供了该存储过程的详细信息。

启用和禁用更改跟踪 (SQL Server)

对数据库启用更改跟踪

你必须先在数据库级别启用更改跟踪,然后才能使用更改跟踪。 下面的示例显示了如何使用 ALTER DATABASE 来启用更改跟踪。

SQL

你还可以通过使用数据库属性(“更改跟踪”页)对话框,在 SQL Server Management Studio 中启用更改跟踪。 如果数据库包含内存优化表,则无法使用 SQL Server Management Studio 启用更改跟踪。 若要启用,请使用 T-SQL。

可以在启用更改跟踪时指定 CHANGE_RETENTION 和 AUTO_CLEANUP 选项,并且可以在启用更改跟踪后随时更改这些值。

更改保持期值指定了更改跟踪信息的保留时间。 早于此时间的更改跟踪信息将被定期删除。 设置该值时,应考虑应用程序与数据库中的表进行同步的频率。 指定的保持期必须至少等于最大同步时间间隔。 如果应用程序获取更改的时间间隔过长,则返回的结果可能不正确,因为某些更改信息可能已被删除。 若要避免获取错误的结果,应用程序可以使用 CHANGE_TRACKING_MIN_VALID_VERSION 系统函数来确定同步之间的时间间隔是否已太长。

可使用 AUTO_CLEANUP 选项来启用或禁用删除陈旧的更改跟踪信息的清除任务。 如果出现临时性问题使得应用程序无法同步,并且在问题解决之前必须暂停用于删除早于保持期的更改跟踪信息的进程,则该设置会很有用。

对于使用更改跟踪的任何数据库,请注意以下事项:

  • 若要使用更改跟踪,必须将数据库兼容级别设为 90 或更高。 如果数据库的兼容级别低于 90,则可以配置更改跟踪。 但是,用于获取更改跟踪信息的 CHANGETABLE 函数将返回错误。
  • 使用快照隔离是帮助确保所有更改跟踪信息保持一致的最简单方式。 因此,我们强烈建议将数据库的快照隔离设为 ON。 有关详细信息,请参阅使用更改跟踪 (SQL Server)

对表启用更改跟踪

对于要跟踪的每个表都必须启用更改跟踪。 启用更改跟踪后,将会为表中受 DML 操作影响的所有行保留更改跟踪信息。

下面的示例显示了如何使用 ALTER TABLE 来对表启用更改跟踪。

SQL

你还可以通过使用数据库属性(“变更跟踪”页)对话框,在 SQL Server Management Studio 中对表启用更改跟踪。

当 TRACK_COLUMNS_UPDATED 选项设为 ON 时,SQL Server 数据库会将有关哪些列已更新的额外信息存储到内部更改跟踪表中。 列跟踪使应用程序可以只同步那些已更新的列。 这可以提高效率和性能。 但是,由于保留列跟踪信息增加了一些额外的存储开销,因而默认情况下此选项设为 OFF。

为表或数据库禁用更改跟踪

必须首先为所有启用了更改跟踪的表禁用更改跟踪,然后才能将数据库的更改跟踪设为 OFF。 若要确定数据库中哪些表启用了更改跟踪,请使用 sys.change_tracking_tables 目录视图。

下面的示例显示了如何使用 ALTER TABLE 对表禁用更改跟踪。

SQL

当数据库中没有用于跟踪更改的表时,便可以禁用数据库的更改跟踪。 下面的示例显示如何使用 ALTER DATABASE 对数据库禁用更改跟踪。

SQL

管理更改跟踪

以下各节列出了与管理更改跟踪相关的目录视图、权限和设置。

目录视图

若要确定哪些表和数据库启用了更改跟踪,可以使用以下目录视图:

此外, sys.internal_tables 目录视图还列出了对用户表启用更改跟踪时所创建的内部表。

安全性

若要使用 更改跟踪函数访问更改跟踪信息,主体必须拥有以下权限:

  • 至少针对主键列(已启用更改跟踪的表针对被查询表的主键列)拥有 SELECT 权限。
  • 对于要获取其更改的表拥有 VIEW CHANGE TRACKING 权限。 要求拥有 VIEW CHANGE TRACKING 权限的原因如下:
    • 更改跟踪记录包含有关已删除行的信息,具体而言,就是已删除行的主键值。 在删除了某些敏感数据之后,某个主体可能已被授予针对启用了更改跟踪的表的 SELECT 权限。 在这种情况下,你不会希望该主体能够使用更改跟踪来访问那些已删除的信息。
    • 更改跟踪信息可以存储有关更新操作所更改的列的信息。 某个主体可能无权访问包含敏感信息的列。 但是,由于有更改跟踪信息,因此主体可以确定某列的值是否已更新,但是该主体无法确定该列的值。

了解更改跟踪开销

启用表的更改跟踪后,会影响某些管理操作。 下表列出了应当注意的操作和影响。

展开表

操作启用更改跟踪后
DROP TABLE会删除已删除表的所有更改跟踪信息。
ALTER TABLE DROP CONSTRAINT删除 PRIMARY KEY 约束的尝试将失败。 必须先禁用更改跟踪,然后才能删除 PRIMARY KEY 约束。
ALTER TABLE DROP COLUMN如果要删除的列是主键的一部分,则不允许删除该列,而不管是否启用了更改跟踪。 如果要删除的列不是主键的一部分,则可以成功删除该列。 但是,首先应了解此操作对同步此数据的任何应用程序的影响。 如果为该表启用了列更改跟踪,则可能仍会将已删除的列作为更改跟踪信息的一部分返回。 已删除列的处理由应用程序负责。
ALTER TABLE ADD COLUMN如果将新列添加到启用了更改跟踪的表中,则不会跟踪该列的添加。 只会跟踪对新列所做的更新和更改。
ALTER TABLE ALTER COLUMN不会跟踪非主键列的数据类型更改。
ALTER TABLE SWITCH如果其中一个表或两个表都启用了更改跟踪,则切换分区将失败。
DROP INDEX 或 ALTER INDEX DISABLE不能删除或禁用强制使用主键的索引。
TRUNCATE TABLE可以对启用了更改跟踪的表执行截断表操作。 但是,不会跟踪由该操作删除的行,并且会更新最低有效版本。 当应用程序检查其版本时,检查结果会表明该版本太陈旧,需要进行重新初始化。 这与禁用后又重新启用表的更改跟踪的效果相同。

由于在操作过程中会存储更改跟踪信息,因此使用更改跟踪会增加 DML 操作的一些开销。

对 DML 的影响

更改跟踪已经过优化,以尽可能减小对 DML 操作的性能影响。 对表使用更改跟踪所导致的性能开销增加类似于为表创建了一个索引并需要维护该索引时而导致的开销。

对于由 DML 操作更改的每一行,都会向内部更改跟踪表中添加一行。 这种与 DML 操作相关的影响取决于各种因素,例如:

  • 主键列数
  • 用户表行中所更改的数据量
  • 事务中所执行的操作数

如果使用了快照隔离,则它也会影响所有 DML 操作的性能,而不管是否启用了更改跟踪。

对存储的影响

更改跟踪数据存储在以下类型的内部表中:

  • 内部更改表

    启用了更改跟踪的每个用户表都有一个内部更改表。

  • 内部事务表

    数据库有一个内部事务表。

这些内部表对存储要求有下列影响:

  • 对于用户表中每行的每个更改,都会向内部更改表中添加一行。 该行有一个较小的固定开销,外加一个大小等于主键列大小的可变开销。 该行可以包含由应用程序设置的可选上下文信息。 此外,如果启用了列跟踪,则每个发生更改的列还需要在跟踪表中占用 4 字节。
  • 对于每个已提交的事务,都会向内部事务表中添加一行。

对于其他内部表,可以使用 sp_spaceused 存储过程来确定用于更改跟踪表的空间。 可以使用 sys.internal_tables 目录视图来获取这些内部表的名称,如下例所示。

SQL

使用更改跟踪 (SQL Server)

使用更改跟踪的应用程序必须能够获取跟踪的更改,将这些更改应用到其他数据存储区并更新源数据库。 本文介绍了如何执行这些任务,以及在发生故障转移且必须从备份还原数据库时,角色更改跟踪如何进行。

通过使用更改跟踪函数获取更改

介绍如何使用更改跟踪功能来获取更改以及有关对数据库所做的更改的信息。

关于更改跟踪函数

应用程序可以使用以下函数来获取在数据库中所做的更改以及有关这些更改的信息:

CHANGETABLE(CHANGES …) 函数
此行集函数用于查询更改信息。 该函数查询内部更改跟踪表中存储的数据。 该函数返回的结果集中包含已更改的行的主键和其他更改信息,例如,操作、更新的列以及行版本。

CHANGETABLE(CHANGES …) 将上次同步版本作为参数。 上次同步版本是使用 @last_synchronization_version 变量获得的。 上次同步版本的语义如下所示:

  • 进行调用的客户端已获取更改,并知道直至上次同步版本(含该版本)所做的所有更改。

  • 因此,CHANGETABLE(CHANGES …) 返回在上次同步版本之后进行的所有更改。

    下图说明了如何使用 CHANGETABLE(CHANGES …) 获取更改。

    Diagram that shows an example of change tracking query output.

    在此示例中,客户端 A 上次在上午 9:30 同步,而客户端 B 上次在上午 10:30 同步。 上午 10:00 以及上午 11:00 对数据进行了多次更改。 下面汇总了这些跟踪的更改。

    CHANGETABLE(CHANGES...) 输出 - 上午 11:30

    客户端 A 上次在上午 9:30 同步。

    展开表

    产品 IDOperation列数
    139更新名称、价格
    140删除-
    141插入-

    客户端 B 上次在上午 10:30 同步。

    展开表

    产品 ID操作列数
    139更新价格
    140删除-
    141更新价格

CHANGE_TRACKING_CURRENT_VERSION() 函数
用于获取当前版本,以供下次查询更改时使用。 该版本表示上次提交的事务的版本。

CHANGE_TRACKING_MIN_VALID_VERSION() 函数
用于获取客户端能够具有的并且仍能从 CHANGETABLE() 获取有效结果的最低有效版本。 客户端应将上次同步版本与此函数返回的值进行对照检查。 如果上次同步版本低于此函数返回的版本,客户端将无法从 CHANGETABLE() 获取有效结果,而必须重新进行初始化。

获取初始数据

在应用程序第一次获取更改之前,应用程序必须发送查询以获取初始数据和同步版本。 应用程序必须直接从表中获取相应的数据,然后使用 CHANGE_TRACKING_CURRENT_VERSION() 获取初始版本。 第一次获取更改时,会将此版本传递给 CHANGETABLE(CHANGES …)。

下面的示例说明了如何获取初始同步版本和初始数据集。

SQL

使用更改跟踪函数获取更改

要获取表中已更改的行以及有关更改的信息,请使用 CHANGETABLE(CHANGES...)。例如,以下查询获取 SalesLT.Product 表的更改。

SQL

通常,客户端需要获取行的最新数据,而不仅仅是行的主键。 因此,应用程序将来自 CHANGETABLE(CHANGES …) 的结果与用户表中的数据联接在一起。 例如,下面的查询与 SalesLT.Product 表联接在一起以获取 NameListPrice 列的值。 请注意,本例中使用了 OUTER JOIN。 若要确保返回有关从用户表中删除的那些行的更改信息,则必须使用此运算符。

SQL

若要获取在下次更改枚举中使用的版本,请使用 CHANGE_TRACKING_CURRENT_VERSION(),如下面的示例所示。

SQL

当应用程序获取更改时,它必须同时使用 CHANGETABLE(CHANGES…) 和 CHANGE_TRACKING_CURRENT_VERSION(),如下面的示例所示。

SQL

版本号

启用了更改跟踪的数据库具有一个版本计数器;在对启用了更改跟踪的表进行更改时,该计数器会随之递增。 每个更改的行都有一个关联的版本号。 将请求发送到应用程序以查询更改时,将调用一个函数以提供版本号。 该函数返回在该版本之后所做的所有更改的相关信息。 从某种意义上讲,更改跟踪版本在概念上与 rowversion 数据类型类似。

验证上次同步的版本

更改的相关信息将保留有限的一段时间。 时间长度是由 CHANGE_RETENTION 参数控制的,可以将该参数指定为 ALTER DATABASE 的一部分。

为 CHANGE_RETENTION 指定的时间决定了所有应用程序必须每隔多长时间从数据库中请求一次更改。 如果应用程序使用的 last_synchronization_version 值早于表的最低有效同步版本,该应用程序将无法执行有效的更改枚举。 这是因为,可能已清除了某些更改信息。 在应用程序使用 CHANGETABLE(CHANGES ...) 获取更改之前,该应用程序必须验证计划传递给 CHANGETABLE(CHANGES ...) 的 last_synchronization_version 值。如果 last_synchronization_version 的值无效,则该应用程序必须重新初始化所有数据。

下面的示例说明了如何验证每个表的 last_synchronization_version 值的有效性。

SQL

正如下面的示例所示,可以对照数据库中的所有表检查 last_synchronization_version 值的有效性。

SQL

使用列跟踪

通过使用列跟踪,应用程序可以仅获取已更改的列数据,而不是获取整个行。 例如,请考虑以下情况:某个表包含一个或多个较大但很少更改的列,并且还包含其他经常更改的列。 如果未使用列跟踪,应用程序只能确定某一行已更改并且必须同步所有数据(包括大型列数据)。 但是,通过使用列跟踪,应用程序可以确定是否更改了大型列数据,并且仅同步已更改的数据。

列跟踪信息出现在 CHANGETABLE(CHANGES …) 函数返回的 SYS_CHANGE_COLUMNS 列中。

可以使用列跟踪,以便为未更改的列返回 NULL。 如果可以将列更改为 NULL,则必须返回一个单独的列以指示是否更改了该列。

在下面的示例中,如果 CT_ThumbnailPhoto 列未更改,则为该列返回 NULL。 该列本身也可能为 NULL ,因为已将其更改为 NULL ;应用程序可以使用 CT_ThumbNailPhoto_Changed 列来确定是否更改了该列。

SQL

获取一致且正确的结果

若要获取更改的表数据,你需要执行多个步骤。 如果没有考虑到并处理某些问题,可能会返回不一致或错误的结果。

例如,要获取对 Sales 表和 SalesOrders 表所做的更改,应用程序应执行以下步骤:

  1. 使用 CHANGE_TRACKING_MIN_VALID_VERSION() 验证上次同步版本。
  2. 使用 CHANGE_TRACKING_CURRENT_VERSION() 获取可供下次获取更改时使用的版本。
  3. 使用 CHANGETABLE(CHANGES ...) 获取对 Sales 表所做的更改。
  4. 使用 CHANGETABLE(CHANGES ...) 获取对 SalesOrders 表所做的更改。

数据库中运行的两个进程可能会影响上述步骤返回的结果:

  • 清除进程在后台运行,并删除早于指定保持期的更改跟踪信息。

    清除进程是一个单独的后台进程,它使用在为数据库配置更改跟踪时指定的保持期。 问题是清除进程可能会在验证上次同步版本之后以及调用 CHANGETABLE(CHANGES…) 之前运行。 到检索更改时,刚刚还有效的上次同步版本可能不再有效。 因此,可能会返回错误的结果。

  • 正在 Sales 和 SalesOrders 表中执行 DML 操作,如下面的操作:

    • 在使用 CHANGE_TRACKING_CURRENT_VERSION() 获取下次使用的版本后,可能对表进行了更改。 因此,返回的更改可能超过预期数量。
    • 可能在从 Sales 表中提取更改的调用以及从 SalesOrders 表中提取更改的调用之间提交了事务。 因此,SalesOrder 表的结果可能包含 Sales 表中不存在的外键值。

若要解决上面列出的难题,建议你使用快照隔离。 这有助于确保更改信息的一致性,并避免出现与后台清除任务有关的争用情况。 如果没有使用快照事务,在开发使用更改跟踪的应用程序时,可能需要增加很多工作量。

使用快照隔离

从设计上,更改跟踪可以很好地与快照隔离配合使用。 必须为数据库启用快照隔离。 获取更改所需的所有步骤必须包含在快照事务中。 这可确保快照事务中的查询看不见在获取更改时对数据所做的所有更改。

若要获取快照事务中的数据,请执行以下步骤:

  1. 将事务隔离级别设置为快照,然后启动一个事务。
  2. 使用 CHANGE_TRACKING_MIN_VALID_VERSION() 验证上次同步版本。
  3. 使用 CHANGE_TRACKING_CURRENT_VERSION() 获取下次要使用的版本。
  4. 使用 CHANGETABLE(CHANGES ...) 获取对 Sales 表所做的更改
  5. 使用 CHANGETABLE(CHANGES ...) 获取对 SalesOrders 表所做的更改
  6. 提交事务。

由于获取更改所需的所有步骤都是在快照事务中执行的,因此,应注意以下事项:

  • 如果清除是在验证上次同步版本之后进行的,则来自 CHANGETABLE(CHANGES ...) 的结果仍将有效,因为在事务中看不见清除执行的删除操作。
  • 在获取下次同步版本后对 Sales 表或 SalesOrders 表所做的所有更改将不可见,并且 CHANGETABLE(CHANGES ...) 调用绝不会返回版本晚于 CHANGE_TRACKING_CURRENT_VERSION() 返回结果的更改。 还会保持 Sales 表和 SalesOrders 表之间的一致性,因为在 CHANGETABLE(CHANGES ...) 调用之间提交的事务将不可见。

下面的示例说明了如何为数据库启用快照隔离。

SQL

快照事务是按如下方式使用的:

SQL

有关快照事务的详细信息,请参阅 SET TRANSACTION ISOLATION LEVEL (Transact-SQL)

清除和快照隔离

使用快照隔离的数据库中存在打开的事务时,对同一个数据库或同一个实例中的两个不同数据库同时启用快照隔离和更改跟踪可能导致清除过程将过期的行保留在 sys.syscommittab 中。 可能会发生这种情况,因为更改跟踪清除进程会在执行清除时考虑到实例范围内的低水印(即安全清除版本)。 这样做是为了确保更改跟踪自动清理过程不会删除启用了快照隔离的数据库中的打开事务可能需要的任何行。 尽可能使读取已提交的快照隔离和快照隔离事务保持简短,以确保 sys.syscommittab 中的过期行得到及时清理。

本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信dbaup66,谢谢!
AiDBA后续精彩内容已被站长无情隐藏,请输入验证码解锁本文!
验证码:
获取验证码: 请先关注本站微信公众号,然后回复“验证码”,获取验证码。在微信里搜索“AiDBA”或者“dbaup6”或者微信扫描右侧二维码都可以关注本站微信公众号。

标签:

Avatar photo

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复