内存优化表的持久性

适用于:SQL Server

内存中 OLTP 为内存优化表提供完全持久性。 更改内存优化表的事务提交时,SQL Server(与基于磁盘的表一样),保证更改是永久性的(在数据库重启后保留),前提是基础存储可用。 持续性有两个重要方面:事务日志记录和在磁盘存储上持久保存数据更改。

有关持久化表的大小限制的详细信息,请参阅估算内存优化表的内存需求

事务日志

对基于磁盘的表或持久的内存优化表所做的所有更改均捕获在一个或多个事务日志记录中。 提交事务时,SQL Server 将与事务关联的日志记录写入磁盘,然后再通知应用程序或用户会话事务已提交。 这可确保事务所做的更改具有持久性。 内存优化表的事务日志与基于磁盘的表使用的相同日志流完全集成。 此集成允许现有的事务日志备份、恢复和还原作继续工作,而无需执行任何额外的步骤。 但是,由于 In-Memory OLTP 可能会显著增加工作负荷的事务吞吐量,因此日志 IO 可能会成为性能瓶颈。 若要保持此增长的吞吐量,请确保日志 IO 子系统可以处理增加的负载。

数据和增量文件

内存优化表中的数据在内存中堆数据结构中存储为自由格式的数据行,它们通过内存中的一个或多个索引链接起来。 数据行没有页面结构,例如基于磁盘的表所使用的那些结构。 为了保持长期的持久性并允许截断事务日志,内存优化表中的操作存留在一组数据和差异文件中。 这些文件是使用异步后台进程基于事务日志而生成的。 数据文件和增量文件存储在一个或多个容器中(使用与 FILESTREAM 数据相同的机制)。 这些容器是内存优化文件组的一部分。

按严格顺序向这些文件写入数据,这样可将旋转介质的磁盘延迟降至最低。 可使用不同驱动器上的多个容器分散 I/O 活动。 当从磁盘上的数据文件和增量文件读取数据并加载到内存中时,分布在不同磁盘上的多个容器中的数据文件和增量文件可提高数据库还原/恢复性能。

用户事务不直接访问数据和增量文件。 所有数据读取和写入均使用内存中数据结构。

数据文件

数据文件包含来自一个或多个内存优化表的行,这些行是由多个事务作为 INSERT 或 UPDATE 操作的一部分插入的。 例如,一行可以来自内存优化表 T1,而下一行可以来自内存优化表 T2。 这些行按事务日志中事务的顺序追加到数据文件,使数据访问顺序进行。 这将使 I/O 吞吐量比随机 I/O 提高一个数量级。

一旦数据文件已满,新事务插入的行就存储于其他数据文件中。 随着时间的推移,持久内存优化表中的行存储在多个数据文件中,每个包含行的数据文件构成了不连续但连续的事务范围。 例如,事务提交时间戳范围为 (100, 200) 的数据文件包含由提交时间戳大于 100 并小于等于 200 的事务插入的所有行。 提交时间戳是在事务准备提交时分配给该事务的一个单调递增的数字。 每个事务都具有唯一的提交时间戳。

当行被删除或更新时,该行不会在数据文件中原地删除或更改,而是通过另一种类型的文件——增量文件——来跟踪已删除的行。 更新操作会被处理为针对每行的一组删除和插入操作。 这将消除数据文件上的随机 IO。

大小:在内存大于 16 GB 的计算机中,每个数据文件的大小约为 128 MB;在内存小于或等于 16 GB 的计算机中,其大小约为 16 MB。 在 SQL Server 2016 (13.x) 中,如果 SQL Server 认为存储子系统的速度足够快,则可以使用大型检查点模式。 在大型检查点模式下,数据文件的大小为 1 GB。 这可提高存储子系统处理高吞吐量工作负载时的效率。

增量文件

每个数据文件都配有一个具有相同事务范围的增量文件,该增量文件用于跟踪事务范围内被删除的行。 该数据文件和增量文件被称为检查点文件对(CFP),它是分配和释放的单位,也是合并操作的单位。 例如,对应事务范围 (100, 200) 的增量文件会存储该范围 (100, 200) 内事务插入后被删除的行。 与数据文件类似,增量文件也是按顺序访问的。

当一行被删除时,该行不会从数据文件中移除,而是将指向该行的引用追加到与该数据行插入时所处事务范围关联的增量文件中。 由于待删除的行已存在于数据文件中,因此增量文件仅存储引用信息{inserting_tx_id, row_id, deleting_tx_id},并按发起删除或更新操作的事务日志顺序排序。

大小:在内存大于 16 GB 的计算机中,每个数据文件的大小约为 16 MB;在内存小于或等于 16 GB 的计算机中,其大小约为 1 MB。 从 SQL Server 2016 (13.x) 开始,如果 SQL Server 认为存储子系统的速度足够快,则可使用大检查点模式。 在大检查点模式下,增量文件的大小为 128MB。

填充数据和增量文件

数据文件和增量文件是根据已提交事务在内存优化表上生成的事务日志记录填充的,并将关于插入和删除行信息追加到相应的数据文件和增量文件中。 与基于磁盘的表不同,后者在执行检查点时会通过随机 I/O 刷新数据/索引页,而内存优化表的持久化则是持续进行的后台操作。 由于一个事务可能会删除或更新由任何先前事务插入的任意行,因此会访问多个增量文件。 删除信息始终追加到增量文件的末尾。 例如,提交时间戳为 600 的事务插入一个新行,并删除提交时间戳为 150、250 和 450 的事务插入的行,如下图所示。 所有四项文件 I/O 操作(其中三项针对已删除行,一项针对新插入行)均为对相应增量文件和数据文件的“仅追加”操作。

内存优化表的读取日志记录的屏幕截图。

访问数据和增量文件

当发生以下事件时,会访问数据和增量文件对。

脱机检查点工作线程

该线程将内存优化数据行的插入和删除操作追加到相应的数据和增量文件对中。 SQL Server 2014 (12.x) 有一个脱机检查点工作线程。 SQL Server 2016 (13.x) 及更高版本有多个检查点工作线程。

合并操作

此操作会合并一个或多个数据和差异文件对,并创建新的数据和差异文件对。

在崩溃恢复期间

当 SQL Server 重新启动或数据库重新联机时,将使用数据和差异文件对来填充内存优化数据。 从相应的数据文件中读取行时,增量文件会充当过滤器,过滤掉已删除的行。 由于每对数据文件和增量文件都是相互独立的,因此会并行加载这些文件,以缩短将数据加载到内存中所需的时间。 将数据加载到内存中后,In-Memory OLTP 引擎会应用检查点文件尚未涵盖的活动事务日志记录,以便内存优化数据完成。

还原操作期间

从数据库备份创建内存中 OLTP 检查点文件,然后应用一个或多个事务日志备份。 与故障恢复一样,In-Memory OLTP 引擎并行将数据加载到内存中,以最大程度地减少对恢复时间的影响。

合并数据和增量文件

内存优化表的数据存储在一个或多个数据和增量文件对中(也称为检查点文件对,或 CFP)。 数据文件存储插入的行,而增量文件引用已删除的行。 在 OLTP 工作负载运行期间,随着 DML 操作对行进行更新、插入和删除,系统会创建新的 CFP 来持久化新行,并将对已删除行的引用追加到增量文件中。

随着时间的推移,随着 DML 操作的进行,数据和增量文件的数量会不断增加,从而导致磁盘空间占用增加以及恢复时间延长。

为了避免这些低效情况,本文后面描述的合并策略将合并较旧的封闭数据和增量文件,从而精简存储阵列以表示相同的数据集,减少文件数量。

合并操作以一个或多个相邻的封闭检查点文件对(CFP)作为输入,这些文件对由数据文件和增量文件(称为合并源)组成,并基于内部定义的合并策略,最终生成一个结果检查点文件对,称为合并目标。 源 CFP 中每个增量文件中的条目用于过滤相应数据文件中的行,以删除不需要的数据行。 源 CFP 中剩余的行被合并到一个目标 CFP 中。 合并完成后,生成的合并目标 CFP 将替换源 CFP(合并源)。 合并源 CFP 会经历一个过渡阶段,然后才会从存储中删除。

在下面的示例中,内存优化表文件组在时间戳 500 处有四对数据文件和增量文件,其中包含先前事务的数据。 例如,第一个数据文件中的行对应于时间戳大于 100 且小于等于 200 的事务,或者表示为 (100,200]。 在将标记为已删除的行计入后,第二个和第三个数据文件显示其占用率低于 50%。 合并操作将这两个 CFP 合并,并创建一个新的 CFP,其中包含时间戳大于 200 且小于等于 400 的事务,即这两个 CFP 的合并范围。 您会看到另一个范围为 (500, 600] 且事务范围 (200, 400] 的增量文件不为空的 CFP,这表明合并操作可以与事务活动(包括从源 CFP 中删除更多行)并行进行。

图表显示内存优化的表文件组。

后台线程根据合并策略评估所有已关闭的 CFP,然后为符合条件的 CFP 发起一个或多个合并请求。 这些合并请求由脱机检查点线程处理。 将定期进行对合并策略的评估,并且在关闭检查点时也会进行评估。

SQL Server 合并策略

SQL Server 实现以下合并策略:

  • 如果在考虑已删除行之后,两个或多个连续的 CFP 可以合并,且合并后的行能够放入一个目标大小的 CFP 中,则会安排合并操作。 如前所述,数据文件和增量文件的目标大小与原始大小设置相对应。

  • 如果数据文件超过目标大小的两倍,并且删除的行超过一半,则单个 CFP 可自行合并。 如果单个事务或多个并发事务插入或更新了大量数据,导致数据文件被迫超出其目标大小(因为一个事务不能跨越多个 CFP),则数据文件可能会超过目标大小。

下面是显示合并策略下要合并的 CFP 的一些示例:

相邻 CFP 的源文件(% 满) 合并选择
CFP0(30%),CFP1(50%),CFP2(50%),CFP3(90%) (CFP0、CFP1)

CFP2 未选择,因为它使生成的数据文件大于 100% 的理想大小。
CFP0 (30%), CFP1 (20%), CFP2 (50%), CFP3 (10%) (CFP0, CFP1, CFP2)。 文件从左侧开始选择。

CFP3 未选择,因为它使生成的数据文件大于 100% 的理想大小。
CFP0 (80%), CFP1 (30%), CFP2 (10%), CFP3 (40%) (CFP1, CFP2, CFP3)。 文件从左侧开始选择。

CFP0 将被跳过,因为如果与 CFP1 结合使用,则生成的数据文件大于理想大小的 100%。

不是具有可用空间的所有 CFP 都符合合并条件。 例如,如果两个相邻的 CFP 达到 60% 的容量,它们不符合合并条件,并且每个 CFP 都有 40% 的存储空间未使用。 在最坏情况下,所有 CFP 的占用率均为 50%,存储利用率仅为 50%。 虽然已删除的行可能因为 CFP 不符合合并条件而存在于存储中,但已删除的行可能已被内存垃圾回收机制从内存中移除。 存储和内存的管理与垃圾回收是独立的。 活动 CFP 占用的存储容量(并非所有 CFP 都在更新)可能达到内存中持久表大小的两倍。

CFP 的生命周期

CFP 在被释放之前会经历多个状态。 需要生成数据库检查点和日志备份,以通过这些阶段转换这些文件,并最终清理不再需要的文件。 请参阅 sys.dm_db_xtp_checkpoint_files 以了解有关这些阶段的说明。

你可以手动强制执行检查点,然后进行日志备份,以加快垃圾回收。 在生产场景中,作为备份策略的一部分所进行的自动检查点和日志备份无缝地使CFP过渡这些阶段,而无需任何手动干预。 垃圾回收过程的影响是,具有内存优化表的数据库的存储大小可能会比其在内存中的大小更大。 如果未发生检查点和日志备份,检查点文件的磁盘占用量会继续增长。