什么是数据库账本?

适用于: SQL Server 2022 (16.x) 及更高版本 Azure SQL 数据库Azure SQL 托管实例

数据库账本是账本功能的一部分。 由于数据库随着时间推移而发展,数据库账本不断捕获数据库的状态,同时账本表会进行更新。 它在逻辑上使用区块链和 Merkle 树数据结构。

任何更新账本表的操作都需要执行一些附加任务来维护历史数据并计算在数据库账本中捕获的摘要。 具体而言,对于更新的每一行,我们必须:

  • 在历史表中保留该行的早期版本。
  • 分配事务 ID 并生成新的序列号,将其保存在相应的系统列中。
  • 序列化行内容,并在计算此事务更新的所有行的哈希时将其包括在内。

账本通过扩展针对账本表的所有插入、更新和删除操作的数据操作语言 (DML) 查询计划来实现此目的。 为该行的新版本设置了事务 ID 和新生成的序列号。 然后,查询计划运算符执行一个特殊表达式,该表达式序列化行内容并计算其哈希,将其附加到存储在事务级别的 Merkle 树,该 Merkle 树包含此事务针对此账本表更新的所有行版本的哈希。 树的根表示此事务在此账本表中执行的所有更新和删除操作。 如果该事务更新了多个表,则会为每个表维护一个单独的 Merkle 树。 下图显示了一个 Merkle 树示例,该树存储账本表的更新行版本以及用于序列化行的格式。 除每个列的序列化值外,我们还包括有关行中的列数、各个列的序号、数据类型、长度和其他影响值解释方式的信息的元数据。

展示 Merkle 树如何存储账本表中更新后的行版本以及用于将行序列化的格式的示意图

为捕获数据库的状态,数据库账本为每个事务存储一个条目。 它捕获事务的相关元数据,例如其提交时间戳和执行用户的身份。 它还会捕获每个分类账表中已更新行的默克尔树根(参见上文)。 然后将这些条目追加到防篡改的数据结构,以便将来允许对完整性进行验证。 一个区块已关闭:

当一个区块关闭时,新事务将插入到一个新区块中。 接下来是区块生成过程:

  1. 从内存队列和 sys.database_ledger_transactions 系统目录视图中检索所有属于已关闭区块的事务。
  2. 计算这些交易的 Merkle 树根以及前一区块的哈希。
  3. 将已关闭的块持久保存在 sys.database_ledger_blocks 系统目录视图中。

由于这是一个常规的表更新,系统会自动保证其持久性。 为了保持单一的区块链结构,此操作采用单线程执行。 但它也是高效的,因为它只计算事务信息上的哈希,并且异步进行, 不会影响事务性能。

显示存储账本表交易记录的 Merkle 树的示意图。

有关账本如何提供数据完整性的详细信息,请参阅摘要管理和数据库验证。

数据库事务和块数据存储在哪里?

事务和块的数据实际上以行的形式存储在两个系统目录视图中:

  • sys.database_ledger_transactions:保留一行,用于存储数据库账本中每个事务的信息。 该信息包括此事务所属的块 ID 以及此事务在该块中的序号。
  • sys.database_ledger_blocks:为账本中的每个块保留一行,包括针对块内事务的 Merkle 树的根,以及形成区块链的上一个块的哈希。

若要查看数据库账本,请在 SQL Server Management Studio 或 SQL Server 开发人员工具中运行以下 T-SQL 语句。

SELECT * FROM sys.database_ledger_transactions;
GO

SELECT * FROM sys.database_ledger_blocks;
GO

以下账本表示例包含四个事务,这些事务在数据库账本的区块链中组成了一个块:

示例账本表的屏幕截图。

权限

查看数据库账本需要 VIEW LEDGER CONTENT 权限。 有关与账本表相关的权限的详细信息,请参阅权限。