In-Memory:内存优化数据的持久化和还原

时间:2022-06-13 06:30:14

数据持久化是还原的前提,没有数据的持久化,就无法还原内存优化表的数据,SQL Server In-Memory OLTP的内存数据能够持久化存储,这意味着内存数据能够在SQL Server实例重启之后自动还原。在创建持久化的内存优化表时,必须设置选项:memory_optimized=on,durability=schema_and_data。内存优化表的持久化由两个进程实现:Checkpoint和事务日志记录,在服务器重启之后,SQL Server通过存储在磁盘上的事务日志和Checkpoint数据,能够将内存优化表还原到任意一个事务一致性的时间点。Checkpoint进程用于将数据更新写入CheckPoint文件,缩短数据还原的时间;将数据更新写入事务日志文件实现事务的持久化存储,由于任何已经提交的数据更新都被持久化存储,因此,内存数据库通过Checkpoint和事务日志,能够将数据写入到持久化存储设备,实现数据持久化,并能通过已存储的数据还原内存优化表。

一,数据持久化的特性

流(Stream)是数据的Bytes流,以Append-Only方式写入数据,这意味着,流写入文件只能在流的末尾写入数据。内存优化表的Checkpoint流和事务日志流都是流写入文件,流写入方式的特点是写入速度极快,减少了持久化数据的时间延迟。SQL Server通过把内存优化表的Checkpoint数据流和事务日志流写入Disk,实现数据的持久化存储。

日志流是在执行数据行版本的更新(插入和删除)操作时,由已经提交的事务更新产生的Log Records,事务日志流有如下特性:

  • 事务日志写入到事务日志文件(.ldf)中;
  • 和硬盘表(Disk-based Table)不同的是,内存优化表的事务日志经过优化,减少日志记录的数量,只存储已提交的事务日志,用于事务的redo操作,不存储undo的事务日志;
  • 在事务提交时,产生事务日志,只记录插入和删除的行版本事务;
  • 不记录内存优化表的Index Operation的事务日志,除了Columnstore Index的压缩阶段;所有的Index都在还原时重新创建;

Checkpoint数据流由三部分组成:

  • Data Stream包含插入操作的行版本数据;
  • Delta Stream包含删除操作的行版本数据;
  • LOB流(Large Data Stream)包含创建ColumnStore Index时,对LOB Column进行压缩的数据;

内存优化表数据存储在一个单独的FileGroup中,在该FileGroup下创建的File实际上是一个Directory或Container,用于存储Checkpoint流文件;Checkpoint流文件在文件尾端顺序写入新的数据;在SQL Server中,Checkpoint文件以 FileStream方式存储,由SQL Server负责管理文件的创建,删除和归并。

二,事务日志的优化

内存优化表产生的事务日志经过优化,减少了日志记录的数量和写入次数。SQL Server只将最小数量的日志写入到事务日志文件(.ldf)中,这体现在,事务日志只执行Redo操作,不记录undo事务;SQL Server把一组事务日志合并,在一个大的Log Record中包含多条更新操作,减少了日志写入的次数。

1,事务日志文件只能执行Redo操作,不记录undo事务

SQL Server对于内存内存优化表,只将已提交事务更新写入事务日志文件,这样,行版本的插入和删除操作都被写入到磁盘文件中,用于执行事务的redo操作,还原数据库。由于日志记录在事务提交时产生,未提交的事务日志没有不会产生日志记录,事务日志文件也不会记录undo事务,这就减少日志记录的数量。

2,合并事务日志,在一个Log Record中包含多个数据更新操作

对于内存优化表事务,SQL Server不使用预写日志机制(WAL,Write-Ahead Logging),日志记录只在事务提交时产生。对于WAL,SQL Server在将数据更新写入Disk之前,先将Log Record写入到事务日志文件;在Checkpoint事件发生时,SQL Server会将尚未提交的事务日志写入到事务日志文件中;对于内存优化表,未提交的日志记录永远不可能写入到磁盘,这是对日志记录的一个优化;此外,内存优化对多个数据更新分组,在一个log record中记录多条数据更新操作,这样做,既减少了事务日志的总体大小和管理开销,也减少事务日志的写入次数。

三,Checkpoint进程

对于硬盘表(Disk-Based Table),Checkpoint操作的作用是减少数据库还原的时间,保持事务日志中活动事务尽可能少;对于持久化的内存优化表,Checkpoint进程的作用是使数据更新被持久化写入到磁盘,这样,在服务器重启时,SQL Server能够利用存储在Disk上的数据还原内存优化表。在查询处理的过程中,事务不会去读取Disk上的数据,Disk上的数据只有一个作用,就是当服务器重启时,将数据填充到内存中,还原内存优化表。

Checkpoint进程的特点:

  • 持续性(Continuous):Checkpoint 关联的IO操作随着日志活动积累而持续增加;
  • 流IO(Streaming I/O):Checkpoint文件使用流写入方式,将数据顺序写入到文件的末尾;

Checkpoint进程同时作用在硬盘表和内存优化表上,执行Checkpoint操作的方式有两种:

  • 手动执行Checkpoint操作:在SSMS中显式执行checkpoint命令,SQL Server同时对硬盘表(Disk-Based Table)和内存优化表(Memory-Optimized Table)执行Checkpoint操作;
  • 自动执行Checkpoint操作:从上一次Checkpoint事件结束后,如果事务日志的Size增长大约1.5GB,那么SQL Server执行一次Checkpoint操作;事务日志的增长用于记录新的Log Records,在事务对硬盘表和内存优化表执行更新操作产生的日志记录;有可能,Checkpoint事件完全是由硬盘表数据的更新触发的;

四,Checkpoint 文件

SQL Server将Checkpoint数据存储在四种类型的文件中,分别是Data文件,Delta文件,Large Object文件和Root文件,这些文件存储在Checkpoint文件的Container中,这个Container实际上是一个包含Memory_Optimized_Data的FileGroup的"File"。在第一次创建内存优化表时,SQL Server预先创建16个不同大小的空闲文件(Free Files),大小是2MB的N次方,最小8MB,最大1GB,这些文件的初始State是PreCreated。当需要使用文件时,SQL Server从Free Files集合中获取一个大小适当的Free File,将文件的状态由PreCreated转换为适当的State。如果文件的State是PreCreated,文件的类型是Free。

1,初始文件的大小

在初始状态下,SQL Server为每个文件类型至少创建3个足够大小的Free files,共12个Free Files,每个文件类型的初始大小取决于跟服务器的配置,不同文件的初始大小如图:

In-Memory:内存优化数据的持久化和还原

2,Data/Delta文件对

Data 文件 和 Delta 文件是最主要的Checkpoint文件,包含了对内存优化表执行操作的所有更新操作的信息。由于它们总是成对出现,称作data/delta文件对,也称作CFP(Checkpoint File Pair),CFP只会在文件末尾顺序写入新的数据,即以Append-Only方式写入数据,唯一的区别是写入的行版本数据不同:

  • Data 文件只存储数据行的插入版本,在执行Insert 和Update操作时,会产生插入版本数据;
  • Delta文件只存储数据行的插入版本,在执行Delete和Update操作,会产生删除版本数据;

Data文件和Delta文件都会覆盖一定的时间戳范围,从begin timestamp开始的所有行版本数据都包含在相同的Data文件和Delta文件中,在还原阶段,从Data文件中读取数据,同时,从Delta文件读取数据,用于从Data文件中过滤数据行,避免将已经删除的行版本重新加载到内存中。由于data文件和delta文件是1:1对应的,数据还原的最小单位是data/delta文件对,不同的data/delta文件对能够并发处理。

3,LOB文件和Root文件

Large Data 文件用于存储LOB Column的数据值,或者ColumnStore Index的行组(RowGroup)内容,如果没有LOB column或 Columnstore Index,SQL Server仍然会预先创建Large Data文件,但是,这些文件将保持FREE状态。

Root文件用于追踪Checkpoint事件,每当发生Checkpoint事件时,一个新的Active root 文件将会被创建。

4,Checkpoint文件的状态

Checkpoint 文件的5种状态分别是:PRECREATED,UNDER CONSTRUCTION,ACTIVE,MERGE TARGET和WAITING FOR LOG TRUNCATION。SQL Server预先创建12个Free Files,初始状态是PreCreated。在使用这些文件时,SQL Server将文件的状态转换为Under Construction 或 Merge Target,文件状态的转换过程如下图:

In-Memory:内存优化数据的持久化和还原

通过DMV:sys.dm_db_xtp_checkpoint_files (Transact-SQL) 查看Checkpoint文件的State和类型。

五,Checkpoint文件的归并和删除

每次Checkpoint操作发生时,Checkpoint文件都会增加,然而,随着时间的积累,内存优化表插入越来越多的行版本,越来越多的行版本被删除,导致Data 文件和Delta文件记录的数据量越来越多,使得读取Data/Delta文件对的时间消耗越来越大,这会严重影响数据库还原操作,增加还原的时间。

解决该问题的方案是把时间戳范围相邻的,未删除数据行比例低于指定阈值的多个Checkpoint文件归并到一个新的Checkpoint文件对中。例如,有两个Data文件,DF1和DF2,它们的timestamp范围相邻,并且未被删除的数据行比例低于指定阈值,把这两个文件合并成一个新的data文件,DF3,该文件的时间戳范围覆盖原始文件DF1和DF2,是DF1和DF2时间戳范围的并集;在做数据归并时,核对数据文件DF1和DF2对应的delta文件,从DF1和DF2中删除所有被标记为Delted的行版本,使DF3中只存储Inserted的行版本数据,在归并结束后,DF3对应的delta文件是空的。

1,自动归并(Merge)

Checkpoin文件对归并操作是自动进行的,SQL Server内部创建一个后台进程,周期性地检查所有的Data/Delta文件对,将能够归并的Checkpoint文件对划分到一个集合中,使每个集合包含两个或多个时间戳范围连续的data/delta文件对,位于相同集合中的多个文件对必须能够存储在单个128MB的Data文件中,归并的策略如下表所示:

In-Memory:内存优化数据的持久化和还原

如果两个时间戳范围相邻的数据文件60%是满的(60% Full,40%的数据行是deleted的),那么它们不会被Merge,每个Checkpoint文件中40%的空间将不会被使用。因此,持久化的内存优化表占用的总磁盘空间,比内存优化数据实际占用的存储空间要大。在最差的情况下,实际占用的磁盘空间是内存优化数据的两倍。

2,自动垃圾回收(Garbage Collection)

一旦Mege操作完成,原始的Checkpoint文件对将不再使用,转换到 WAITING FOR LOG TRUNCATION 状态,只要相应的事务日志被截断,那么垃圾回收进程就开始执行清理操作,物理删除不再使用的Checkpoint文件对。在一个Checkpoint文件对删除之前,SQL Server必须保证这些文件不再被使用;垃圾回收进程是自动执行的,不需要任何干预,事务日志的截断发生的时机是:

  • 备份事务日志:进行事务日志备份时,将截断事务日志文件;
  • 数据库处于auto_truncate模式:事务日志文件只记录活动事务的日志,一旦事务完成,将自动截断;

六,内存优化表的还原

在SQL Server实例重启时,SQL Server 以并发执行的方式还原内存优化表和硬盘表数据。每一个data文件存储的是插入的行版本数据,但是插入的行版本数据受到delta文件的过滤,每一个delta文件中存储不需要从对应的data文件加载的数据行。SQL Server对每一个data/delta文件对进行优化配置,使数据加载进程能够在多个IO流(IO Steam)中并发执行,每一个IO流都能单独处理一个data/delta文件对,多个IO流同时加载数据,提高数据还原的速度。

每一个内存数据库都仅有一个包含Memory_Optimized_Data的文件组(FileGroup),在该FileGroup下创建的每一个File,叫做一个Container,主要用于存储data/delta文件对,SQL Server为每一个Container创建一个delta-map,映射每一个Data 文件中被Delta 文件过滤的数据,这样,被删除的数据就不会重新插入到内存优化表中。还原操作是流并发的,在对Container下的所有Data/Delta文件对进行流处理时,SQL Server将数据加载任务分配给所有的CPU内核,根据delta-map,并发式处理Data/Delta文件对。

一旦Checkpoint 文件加载完成,SQL Server开始读取尾事务日志,从上一次Checkpoing结束的时间戳(timestamp)开始,重新执行事务日志中记录的Insert和Delete操作,等到所有的事务日志重新执行完成,数据库就还原到服务器停机的时刻,或备份操作开始的时间。在SQL Server 2016中,事务日志的读取和Redo操作是并发执行的,所以,数据和事务日志都是并发操作,数据还原的过程是非常快的。

参考文档:

SQL Server In-Memory OLTP Internals for SQL Server 2016