SQL Server 2022 新特性:Ledger 账本表详解(防篡改审计利器)

前言

数据安全与审计合规是企业数据库管理的核心需求之一。传统方案依赖触发器、CDC(变更数据捕获)或第三方审计工具,配置复杂、维护成本高,且难以从根本上防止具有高权限的 DBA 篡改历史记录。

SQL Server 2022 引入了 Ledger(账本) 功能------一种基于区块链哈希链思想的数据库级防篡改机制。它能够:

  • 自动记录所有 INSERT / UPDATE / DELETE 操作的完整历史
  • 防止篡改:即使 DBA 也无法静默修改历史记录
  • 提供加密验证:通过哈希摘要证明数据完整性
  • 满足合规要求:金融、医疗、审计等场景开箱即用

本文通过完整示例,带你掌握 Ledger 账本表的核心用法。


准备测试数据

复制代码
-- 创建测试数据库(启用 Ledger 功能)
CREATE DATABASE LedgerDemo;
GO

USE LedgerDemo;
GO

-- 创建"可更新账本表":记录员工工资信息
CREATE TABLE dbo.EmployeeSalary
(
    EmployeeID   INT           NOT NULL PRIMARY KEY,
    EmployeeName NVARCHAR(50)  NOT NULL,
    Department   NVARCHAR(30)  NOT NULL,
    Salary       DECIMAL(10,2) NOT NULL,
    UpdatedBy    NVARCHAR(50)  NOT NULL DEFAULT SYSTEM_USER
)
WITH (SYSTEM_VERSIONING = ON, LEDGER = ON);
GO

-- 插入初始员工薪资数据
INSERT INTO dbo.EmployeeSalary (EmployeeID, EmployeeName, Department, Salary, UpdatedBy)
VALUES
    (1001, N'张伟',   N'研发部', 18000.00, N'HR_Admin'),
    (1002, N'李娜',   N'财务部', 15000.00, N'HR_Admin'),
    (1003, N'王强',   N'销售部', 12000.00, N'HR_Admin'),
    (1004, N'赵雷',   N'研发部', 20000.00, N'HR_Admin'),
    (1005, N'陈晓燕', N'人事部',  9800.00, N'HR_Admin');
GO

说明WITH (SYSTEM_VERSIONING = ON, LEDGER = ON) 是创建可更新账本表的关键语法。SQL Server 会自动生成配套的历史表和账本视图。


示例一:查看账本表的自动生成结构

复制代码
-- 查看 Ledger 相关系统对象
SELECT 
    t.name                        AS 表名,
    t.ledger_type_desc            AS Ledger类型,
    t.ledger_view_name            AS 账本视图名,
    t.history_table_name          AS 历史表名
FROM sys.tables t
WHERE t.is_ledger_on = 1;
GO

-- 查看账本视图的列结构
SELECT 
    COLUMN_NAME         AS 列名,
    DATA_TYPE           AS 数据类型,
    IS_NULLABLE         AS 可空
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'MSSQL_LedgerHistoryFor_EmployeeSalary'
   OR TABLE_NAME LIKE '%EmployeeSalary_Ledger%'
ORDER BY ORDINAL_POSITION;
GO

查询结果(sys.tables 账本信息):

表名 Ledger类型 账本视图名 历史表名
EmployeeSalary UPDATABLE_LEDGER_TABLE EmployeeSalary_Ledger MSSQL_LedgerHistoryFor_EmployeeSalary

说明:SQL Server 自动创建了:

  • 历史表MSSQL_LedgerHistoryFor_EmployeeSalary(存储旧版本记录)
  • 账本视图EmployeeSalary_Ledger(合并当前+历史,含操作类型标记)

示例二:执行数据变更并查看账本视图

复制代码
-- 模拟业务操作:调整张伟的薪资(涨薪)
UPDATE dbo.EmployeeSalary
SET Salary = 21000.00, UpdatedBy = N'Manager_Liu'
WHERE EmployeeID = 1001;
GO

-- 模拟:李娜离职,删除记录
DELETE FROM dbo.EmployeeSalary
WHERE EmployeeID = 1002;
GO

-- 模拟:新员工入职
INSERT INTO dbo.EmployeeSalary (EmployeeID, EmployeeName, Department, Salary, UpdatedBy)
VALUES (1006, N'刘洋', N'研发部', 19500.00, N'HR_Admin');
GO

-- 查询账本视图,查看完整操作历史
SELECT
    EmployeeID,
    EmployeeName,
    Department,
    Salary,
    UpdatedBy,
    ledger_operation_type_desc  AS 操作类型,
    ledger_transaction_id       AS 事务ID,
    ledger_sequence_number      AS 序列号
FROM dbo.EmployeeSalary_Ledger
ORDER BY ledger_transaction_id, ledger_sequence_number;
GO

账本视图查询结果:

EmployeeID EmployeeName Department Salary UpdatedBy 操作类型 事务ID 序列号
1001 张伟 研发部 18000.00 HR_Admin INSERT 101 0
1002 李娜 财务部 15000.00 HR_Admin INSERT 101 1
1003 王强 销售部 12000.00 HR_Admin INSERT 101 2
1004 赵雷 研发部 20000.00 HR_Admin INSERT 101 3
1005 陈晓燕 人事部 9800.00 HR_Admin INSERT 101 4
1001 张伟 研发部 18000.00 HR_Admin DELETE 102 0
1001 张伟 研发部 21000.00 Manager_Liu INSERT 102 1
1002 李娜 财务部 15000.00 HR_Admin DELETE 103 0
1006 刘洋 研发部 19500.00 HR_Admin INSERT 104 0

说明 :UPDATE 操作在账本视图中被分解为 DELETE(旧值)+ INSERT(新值) 两条记录,确保每一次变更都有迹可查。


示例三:溯源查询------还原员工薪资变更历史

复制代码
-- 查询员工 1001(张伟)的完整薪资变更轨迹
SELECT
    EmployeeID,
    EmployeeName,
    Salary                      AS 薪资,
    UpdatedBy                   AS 操作人,
    ledger_operation_type_desc  AS 操作类型,
    ledger_transaction_id       AS 事务ID
FROM dbo.EmployeeSalary_Ledger
WHERE EmployeeID = 1001
ORDER BY ledger_transaction_id, ledger_sequence_number;
GO

-- 查询被删除的员工记录(在账本视图中仍保留)
SELECT
    EmployeeID,
    EmployeeName,
    Department,
    Salary,
    ledger_operation_type_desc AS 操作类型,
    ledger_transaction_id      AS 事务ID
FROM dbo.EmployeeSalary_Ledger
WHERE ledger_operation_type_desc = 'DELETE'
ORDER BY ledger_transaction_id;
GO

张伟薪资变更轨迹:

EmployeeID EmployeeName 薪资 操作人 操作类型 事务ID
1001 张伟 18000.00 HR_Admin INSERT 101
1001 张伟 18000.00 HR_Admin DELETE 102
1001 张伟 21000.00 Manager_Liu INSERT 102

说明:通过账本视图,可以清楚地看到张伟薪资从 18000 变更为 21000 的完整过程,包括操作人。即使没有额外审计日志,也能完整还原历史。


示例四:防篡改验证------生成并核验数据库摘要

复制代码
-- 生成数据库账本摘要(用于后续验证数据完整性)
EXECUTE sp_generate_database_ledger_digest;
GO

摘要输出示例(JSON格式):

json

复制

复制代码
{
    "database_name": "LedgerDemo",
    "block_id": 4,
    "hash": "0x3F8A2B1C...",
    "last_transaction_commit_time": "2026-04-21T08:45:12",
    "digest_version": 1
}

-- 验证账本完整性(检测是否有人在数据库外部直接修改数据)
DECLARE @digest NVARCHAR(MAX) = N'{
    "database_name": "LedgerDemo",
    "block_id": 4,
    "hash": "0x3F8A2B1C...",
    "last_transaction_commit_time": "2026-04-21T08:45:12",
    "digest_version": 1
}';

EXECUTE sp_verify_database_ledger @digest;
GO

验证结果(数据未被篡改时):

消息
Ledger verification successful.

说明

  • sp_generate_database_ledger_digest:生成包含哈希摘要的 JSON,应定期保存到外部安全存储(如 Azure Blob、AWS S3 或本地文件)
  • sp_verify_database_ledger:用保存的摘要验证当前数据库状态,如果有人直接绕过 SQL Server 修改底层数据文件,验证将失败

示例五:仅追加账本表(Append-only Ledger)

复制代码
-- 创建"仅追加账本表":适合日志、审计流水等只写场景
CREATE TABLE dbo.AuditLog
(
    LogID       INT IDENTITY(1,1) PRIMARY KEY,
    EventTime   DATETIME2        NOT NULL DEFAULT SYSDATETIME(),
    EventType   NVARCHAR(50)     NOT NULL,
    Operator    NVARCHAR(50)     NOT NULL,
    Description NVARCHAR(200)    NOT NULL
)
WITH (LEDGER = ON (APPEND_ONLY = ON));
GO

-- 插入审计日志
INSERT INTO dbo.AuditLog (EventType, Operator, Description)
VALUES
    (N'LOGIN',  N'zhang_wei', N'用户从 192.168.1.101 登录系统'),
    (N'EXPORT', N'li_na',     N'导出财务报表 2026Q1'),
    (N'CONFIG', N'admin',     N'修改系统配置:MaxConnections=500');
GO

-- 尝试更新(会被拒绝)
-- UPDATE dbo.AuditLog SET EventType = N'MODIFIED' WHERE LogID = 1;
-- 报错:Cannot update or delete from a ledger table that is append-only.

-- 查询审计日志(含账本元数据)
SELECT
    LogID,
    EventTime,
    EventType,
    Operator,
    Description,
    ledger_transaction_id  AS 事务ID,
    ledger_sequence_number AS 序列号
FROM dbo.AuditLog
ORDER BY LogID;
GO

审计日志查询结果:

LogID EventTime EventType Operator Description 事务ID 序列号
1 2026-04-21 08:45:01 LOGIN zhang_wei 用户从 192.168.1.101 登录系统 105 0
2 2026-04-21 08:45:01 EXPORT li_na 导出财务报表 2026Q1 105 1
3 2026-04-21 08:45:02 CONFIG admin 修改系统配置:MaxConnections=500 106 0

说明 :仅追加账本表(APPEND_ONLY = ON)禁止 UPDATE 和 DELETE,适合审计流水、操作日志等场景,从语法层面彻底杜绝历史记录被删改。


与旧版写法对比

对比维度 传统方案(触发器/CDC) SQL Server 2022 Ledger
配置复杂度 高(需手动创建触发器或配置 CDC) 低(建表时加 LEDGER = ON 即可)
防 DBA 篡改 ❌ 不能(DBA 可禁用触发器/修改历史表) ✅ 能(哈希链保护,篡改可被检测)
历史查询 需手动设计历史表结构 自动生成历史表和账本视图
完整性证明 ❌ 无法提供密码学证明 ✅ 支持哈希摘要验证(可存外部)
UPDATE 展现方式 依实现而异 统一为 DELETE + INSERT 记录
仅追加场景 需额外逻辑控制 APPEND_ONLY = ON 原生支持
性能影响 触发器有较大开销 系统版本控制,开销较小
代码维护成本 几乎为零

旧写法(触发器模拟审计):

复制代码
-- 传统:用触发器记录薪资变更(繁琐且可被 DBA 绕过)
CREATE TABLE dbo.SalaryAudit_Old
(
    AuditID    INT IDENTITY PRIMARY KEY,
    EmployeeID INT,
    OldSalary  DECIMAL(10,2),
    NewSalary  DECIMAL(10,2),
    ChangedAt  DATETIME2 DEFAULT SYSDATETIME(),
    ChangedBy  NVARCHAR(50) DEFAULT SYSTEM_USER
);
GO

CREATE TRIGGER trg_SalaryChange
ON dbo.EmployeeSalary_Old
AFTER UPDATE
AS
BEGIN
    INSERT INTO dbo.SalaryAudit_Old (EmployeeID, OldSalary, NewSalary)
    SELECT d.EmployeeID, d.Salary, i.Salary
    FROM deleted d
    JOIN inserted i ON d.EmployeeID = i.EmployeeID
    WHERE d.Salary <> i.Salary;
END;
GO
-- 问题:DBA 可以执行 DISABLE TRIGGER 或直接修改 SalaryAudit_Old 表

新写法(Ledger 账本表):

复制代码
-- SQL Server 2022:建表时直接启用 Ledger,无需任何触发器
CREATE TABLE dbo.EmployeeSalary
(
    EmployeeID   INT           NOT NULL PRIMARY KEY,
    EmployeeName NVARCHAR(50)  NOT NULL,
    Salary       DECIMAL(10,2) NOT NULL
)
WITH (SYSTEM_VERSIONING = ON, LEDGER = ON);
-- 历史记录、账本视图、哈希保护:全部自动就绪

兼容性说明

版本 支持情况
SQL Server 2022(16.x) ✅ 完整支持(可更新账本表 + 仅追加账本表 + 摘要验证)
SQL Server 2019 及以下 ❌ 不支持
Azure SQL Database ✅ 完整支持(与 SQL Server 2022 同步)
Azure SQL Managed Instance ✅ 支持

注意事项:

  1. 数据库兼容级别:Ledger 功能不依赖兼容级别,只要版本是 SQL Server 2022 即可使用
  2. 不可逆性 :账本表一旦创建,不能通过 ALTER TABLE 关闭 Ledger 属性
  3. 历史表限制 :系统自动生成的历史表(MSSQL_LedgerHistoryFor_*)不能直接删除,只能通过删除主表来删除
  4. 仅追加表限制APPEND_ONLY = ON 的账本表不支持 SYSTEM_VERSIONING,两者互斥
  5. 摘要存储 :强烈建议将 sp_generate_database_ledger_digest 的输出定期保存到 数据库外部(Azure Blob/本地文件系统/不可变存储),否则摘要本身也可能被篡改

总结

SQL Server 2022 Ledger 账本表为数据防篡改审计提供了原生的、低成本的解决方案:

特性 要点
两种类型 可更新账本表(LEDGER = ON)和仅追加账本表(APPEND_ONLY = ON
自动历史追踪 UPDATE 自动拆分为 DELETE + INSERT,完整保留变更前后值
防篡改机制 哈希链 + 摘要验证,即使 DBA 也无法静默修改历史
账本视图 自动生成,合并当前数据与历史,直接查询即可溯源
合规场景 金融流水、薪酬记录、操作日志、医疗数据等强审计场景

核心建议:在有审计合规要求的业务表上,用 WITH (SYSTEM_VERSIONING = ON, LEDGER = ON) 替代传统触发器方案,配合定期保存摘要,即可构建可信的数据防篡改体系。

相关推荐
qq_342295822 小时前
SQL报表星型模型优化_事实表索引设计
jvm·数据库·python
u0109147602 小时前
SQL优化多表关联中的字符串连接字段_建立前缀索引提升JOIN
jvm·数据库·python
Maverick062 小时前
Oracle 会话连接查询
数据库·oracle
2301_777599372 小时前
Oracle环境下的设置主键与自增列指南_特定语法与可视化配置
jvm·数据库·python
a9511416422 小时前
Golang怎么用go get添加依赖_Golang如何在项目中引入第三方库【入门】
jvm·数据库·python
老王谈企服2 小时前
[信创选型] 2026国产化替代进入应用层:有没有通过国产化认证、能在麒麟系统上跑的合规Agent?
数据库·人工智能·ai
4t4run2 小时前
1、clickhouse 安装
数据库·clickhouse
Gauss松鼠会2 小时前
【openGauss】openGauss 磁盘引擎之 ustore
java·服务器·开发语言·前端·数据库·经验分享·gaussdb
Chasing__Dreams2 小时前
Mysql--基础知识点--105--分布式事务
数据库·分布式·mysql