什么是数据库账本?

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

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

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

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

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

Diagram that shows a Merkle Tree storing the updated row versions of a ledger table and the format used to serialize the rows

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

关闭块后,新事务将被插入新的块中。 然后,块生成过程将执行以下操作:

  1. 从内存中的队列和 sys.database_ledger_transactions 系统目录视图检索属于已关闭块的所有事务
  2. 计算这些事务上的 Merkle 树根和上一个块的哈希。
  3. 保留 sys.database_ledger_blocks 系统目录视图中已关闭的块。

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

Diagram that shows a Merkle Tree storing the transactions of a ledger table.

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

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

事务和块的数据在物理上存储为两个系统目录视图中的行:

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

若要查看数据库账本,请在 SQL Server Management StudioAzure Data StudioSQL Server Developer Tools 中运行以下 T-SQL 语句。

SELECT * FROM sys.database_ledger_transactions;
GO

SELECT * FROM sys.database_ledger_blocks;
GO

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

Screenshot of an example ledger table.

权限

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