Memory-Optimized 表的持久性

In-Memory OLTP 为内存优化表提供完全持久性。 当更改内存优化表的事务提交时,SQL Server(与基于磁盘的表一样),保证更改是永久性的(在数据库重启后才会发生),前提是基础存储可用。 持久性有两个关键组件:事务日志记录并将数据更改保存到磁盘存储。

事务日志

对基于磁盘的表或持久内存优化表所做的所有更改都捕获在一个或多个事务日志记录中。 事务提交时,SQL Server 会将与事务关联的日志记录写入磁盘,然后再与事务提交的应用程序或用户会话通信。 这可以保证事务处理所做的更改是持久的。 内存优化表的事务日志与基于磁盘的表使用的同一日志流完全集成。 此集成允许现有的事务日志备份、恢复和还原作继续工作,而无需执行任何其他步骤。 但是,由于 In-Memory OLTP 可以显著增加工作负荷的事务吞吐量,因此需要确保正确配置事务日志存储来处理 IO 要求增加。

数据和增量文件

内存优化表中的数据以自由格式的数据行的形式存储,这些行通过内存中的一个或多个内存中索引进行链接。 数据行没有页结构,例如用于基于磁盘的表。 当应用程序准备好提交事务时,In-Memory OLTP 会生成事务的日志记录。 内存优化表的持久性通过一组数据和增量文件借助后台线程来完成。 数据和增量文件位于一个或多个容器中(使用用于 FILESTREAM 数据的相同机制)。 这些容器映射到名为内存优化文件组的新类型的文件组。

数据以严格顺序的方式写入这些文件,从而最大限度地减少旋转媒体的磁盘延迟。 可以在不同的磁盘上使用多个容器来分发 I/O 活动。 当从磁盘上的数据和增量文件读取数据到内存中时,不同磁盘上多个容器中的数据和增量文件将提高恢复性能。

应用程序不直接访问数据和增量文件。 所有数据读取和写入都使用内存中数据。

数据文件

数据文件包含来自一个或多个内存优化表的行,这些行是由多个事务作为 INSERT 或 UPDATE 操作的一部分插入或更新的。 例如,一行可以来自内存优化表 T1,下一行可以来自内存优化表 T2。 行按照事务日志中的事务顺序追加到数据文件中,使数据按顺序访问。 与随机 I/O 相比,这可实现更大规模的 I/O 吞吐量。 对于内存大于 16GB 的计算机,每个数据文件的大小约为 128MB,对于小于或等于 16GB 的计算机,每个数据文件的大小约为 16MB。 当数据文件已满时,来自新事务插入的行将被存储到另一个数据文件中。 随着时间的推移,持久内存优化表中的行存储在多个数据文件之一中,每个数据文件包含来自不连续但连续事务范围的行。 例如,在 (100, 200) 范围内具有事务提交时间戳的数据文件包含提交时间戳大于 100 且小于或等于 200 的事务插入的所有行。 提交时间戳是一个单调递增的数字,事务准备好提交时会分配该数字。 每个事务都有唯一的提交时间戳。

删除或更新行时,该行不会在数据文件中就地删除或更改,但已删除的行将跟踪在另一种类型的文件中:增量文件。 更新操作将作为删除和插入操作的元组进行处理,并为每一行操作。 这消除了数据文件上的随机 IO。

Delta 文件

每个数据文件与具有相同事务范围的差分文件配对,该差分文件记录事务在事务范围内插入的已删除行。 此数据和增量文件称为检查点文件对(CFP),它是分配和解除分配的单元,也是一种合并操作的单元。 例如,对应于事务范围 (100, 200) 的增量文件将存储由事务在范围 (100, 200) 中插入的已删除行。 与数据文件一样,增量文件按顺序访问。

删除行时,不会从数据文件中删除该行,而是将该行的引用追加到与插入此数据行的事务范围关联的增量文件中。 由于要删除的行已存在于数据文件中,因此增量文件仅存储引用信息 {inserting_tx_id, row_id, deleting_tx_id } ,并且它遵循原始删除或更新作的事务日志顺序。

填充数据和增量文件

数据和增量文件由名为离线检查点的后台线程生成。 此线程读取已提交事务在内存优化表上生成的事务日志记录,并将有关插入和删除行的信息追加到相关的数据和增量文件中。 与在检查点完成后通过随机 I/O 刷新数据/索引页的基于磁盘的表不同,内存优化表的持久性是一种持续的后台操作。 多个增量文件会被访问,因为事务可以删除或更新任何之前事务插入的行。 删除信息始终追加到增量文件的末尾。 例如,提交时间戳为 600 的事务插入一个新行,并删除提交时间戳为 150、250 和 450 的事务插入的行,如下图所示。 所有四个文件 I/O操作(对于已删除的行有三个,对于新插入的行为一个),都是仅追加操作至相应的增量和数据文件。

读取内存优化表的日志记录。

访问数据和增量文件

发生以下情况时,将访问数据和增量文件对。

脱机检查点线程此线程会将插入和删除追加到内存优化的数据行,并将其追加到相应的数据和增量文件对。

合并操作进行一个或多个数据和增量文件对的合并,并创建新的数据和增量文件对。

在崩溃恢复期间,当重新启动 SQL Server 或数据库重新联机时,内存优化的数据将通过加载数据和增量文件对进行填充。 从相应数据文件中读取行时,增量文件充当已删除行的筛选器。 由于每个数据和增量文件对都是独立的,因此这些文件会并行加载,以减少将数据填充到内存所需的时间。 将数据加载到内存中后,In-Memory OLTP 引擎会应用检查点文件尚未涵盖的活动事务日志记录,以便内存优化数据完成。

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

合并数据和增量文件

内存优化表的数据存储在一个或多个数据和增量文件对(也称为检查点文件对或 CFP)中。 数据文件存储插入的行,增量文件则用于标记已删除的行。 在执行 OLTP 工作负荷期间,当 DML 操作更新、插入和删除行时,会创建新的 CFP 来持久化新行,并将已删除行的引用追加到增量文件中。

以前关闭且当前处于活动状态的所有 CFP 的元数据存储在称为存储数组的内部数组结构中。 它是一个有限大小的(8,192 个条目)的 CFP 数组。 存储数组中的条目按事务范围排序。 要恢复包含内存优化表的数据库,存储阵列(包括日志的尾部)中的 CFP 表示所需的所有磁盘状态。

随着 DML操作的不断增多,CFP数量不断增加,导致存储阵列达到容量限制,这带来了以下挑战:

  • 已删除的行。 已删除的行保留在数据文件中,但在相应的增量文件中标记为已删除。 不再需要这些行,将从存储中删除。 如果未从 CFP 中删除已删除的行,它们会占用不必要的空间,并导致恢复时间变慢。

  • 存储阵列已满。 当存储阵列中分配了 8,000 个条目(其中 192 个条目被保留用于现有合并的竞争或允许手动合并),则无法在持久内存优化表上执行新的 DML 事务。 仅允许检查点操作和合并操作使用剩余条目。 这可确保 DML 事务不会填充数组,并且数组中的某些条目保留用于合并现有文件和回收数组中的空间。

  • 存储阵列操作开销。 内部进程在存储阵列中搜索以执行诸如查找增量文件的操作,来添加关于已删除行的信息。 这些操作的成本随着条目数量的增加而增加。

为了帮助防止这些效率低下,根据下面所述的合并策略合并较旧的闭合 CFP,因此会压缩存储阵列来表示同一组数据,减少 CFP 数量。

数据库中所有持久表的总内存中大小不应超过 250 GB。 使用最多 250 GB 内存的持久表(假设插入、删除和更新作)平均需要 500 GB 的存储空间。 为了支持 500 GB 的存储空间,内存优化文件组中需要 4,000 个数据文件和增量文件对。

数据库活动的短期激增可能会导致检查点和合并作滞后,这会增加所需的数据和增量文件对的数量。 为了适应数据库活动的短期激增,存储系统最多可以分配 8,000 个数据和增量文件对,最多可以分配 1TB 的存储。 达到该限制后,在检查点操作赶上之前,不允许对数据库进行新的交易。 如果内存中持久表的大小长时间超过 250GB,则有可能达到 8,000 个文件对限制。

合并操作作为输入,基于内部定义的合并策略,对一个或多个相邻的封闭CFP(称为合并源)进行处理,并生成一个结果CFP,称为合并目标。 源 CFP 的每个增量文件中的条目用于筛选相应数据文件中的行,以删除不需要的数据行。 将源 CFPs 中的剩余行合并到一个目标 CFP 中。 合并完成后,生成的合并目标 CFP 将会替换作为合并源的源 CFP。 合并源 CCP 在从存储中删除前会经历转换阶段。

在下面的示例中,内存优化表文件组在时间戳 500 处具有四个数据和增量文件对,其中包含以前事务中的数据。 例如,第一个数据文件中的行对应于时间戳大于 100 且小于或等于 200 的事务;或者表示为 (100, 200]。 在考虑到标记为已删除的行之后,第二个和第三个数据文件被显示为少于50%已满。 合并操作将这两个 CFP 合并,并创建一个新的 CFP,其中包含时间戳大于 200 且小于或等于 400 的交易,这是这两个 CFP 的合并范围。 你会看到另一个范围为(500, 600] 的 CFP 和对于事务范围(200, 400] 的非空增量文件,这表明合并操作可以与事务活动同时进行,包括从源 CFP 中删除更多行。

关系图显示内存优化表文件组

后台线程使用合并策略评估所有已关闭的 CFP,然后为符合条件的 CFP 启动一个或多个合并请求。 这些合并请求由脱机检查点线程处理。 合并策略的评估是定期完成的,也是关闭检查点时进行的。

SQL Server 2014 (12.x) 合并策略

SQL Server 2014 (12.x) 实现以下合并策略:

  • 如果在考虑已删除的行后可以合并 2 个或多个连续 CFP,则会计划合并合并,以便生成的行可以容纳理想大小的 1 个 CFP。 CFP 的理想大小如下确定:

    • 如果计算机内存小于或等于 16GB,则数据文件为 16MB,增量文件为 1MB。

    • 如果计算机内存大于 16GB,则数据文件为 128MB,增量文件为 16MB。

  • 如果数据文件超过 256 MB,且删除的行超过一半,则单个 CFP 可以自行合并。 例如,如果单个事务或多个并发事务插入或更新大量数据,则数据文件可能会增长超过 128MB,这迫使数据文件增长超出其理想大小,因为事务不能跨越多个 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)。 从左边开始选择文件。

未选择 CTP3,因为它将使生成的数据文件大于 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 正在更新)可能会达到内存中持久表大小的 2 倍。

如果需要,可以通过调用 sys.sp_xtp_merge_checkpoint_files(Transact-SQL)显式执行手动合并。

CFP 的生命周期

CPF 在解除分配之前,先通过多个状态进行转换。 在任何给定时间,CCP 都处于以下阶段之一:预创建、正在构造、主动、合并目标、合并源、备份/HA 所需的转换到 TOMBSTONE 和 TOMBSTONE。 有关这些阶段的说明,请参阅sys.dm_db_xtp_checkpoint_files(Transact-SQL)。

考虑到 CFP 在各种状态下占用的存储后,持久内存优化表可能占用的总存储比内存中表的大小大得多,可能超过两倍。 可以查询 DMV sys.dm_db_xtp_checkpoint_files(Transact-SQL), 列出内存优化文件组中的所有 CFP(检查点文件),包括其阶段信息。 如果将数据库配置为完整恢复模式或大容量日志恢复模式,则将 CFPs 从 MERGE SOURCE 状态转换为 TOMBSTONE 并最终进行垃圾回收可能需要最多五个检查点,每个检查点后随有事务日志备份。

你可以通过手动执行检查点,然后进行日志备份来加快垃圾回收处理,但这样会添加 5 个空的 CFP(5 个数据/增量文件对,每个数据文件的大小为 128MB)。 在生产场景中,作为备份策略的一部分进行的自动检查点和日志备份将无缝地使 CFP 过渡通过这些阶段,而无需任何手动干预。 垃圾回收过程的影响是,具有内存优化表的数据库可能与内存中的大小相比具有更大的存储大小。 CFP 的大小可以达到持久内存优化表在内存中大小的四倍,这并不罕见。

另请参阅

为 Memory-Optimized 对象创建和管理存储