SQL Server 2019 数据库的备份与恢复 --- 语法知识点及使用方法详解
一、备份与恢复介绍
数据库备份是防止数据丢失、应对灾难、满足合规审计的核心手段。SQL Server 提供多种备份类型和恢复模式,支持灵活的恢复策略。
📌 核心目标:
- 备份:创建数据库在特定时间点的副本。
- 恢复:将数据库还原到某个备份时间点或指定时间点。
二、备份类型
| 备份类型 | 说明 | 适用场景 |
|---|---|---|
| 完整备份(Full Backup) | 备份整个数据库(数据 + 日志) | 基础备份,所有恢复的基础 |
| 差异备份(Differential Backup) | 备份自上次完整备份以来更改的数据 | 减少备份大小和时间,需依赖完整备份 |
| 事务日志备份(Transaction Log Backup) | 备份自上次日志备份以来的事务日志 | 支持时间点恢复,需完整或差异备份配合 |
| 文件/文件组备份(File/Filegroup Backup) | 备份特定文件或文件组 | 大型数据库,部分文件损坏时快速恢复 |
⚠️ 注意:
- 差异备份基于最近一次完整备份,与中间差异备份无关。
- 事务日志备份需在完整恢复模式 或大容量日志恢复模式下进行。
- 文件备份适用于简单恢复模式以外的场景。
三、恢复模式
恢复模式决定事务日志管理方式,影响备份和恢复能力。
| 恢复模式 | 说明 | 备份支持 | 恢复能力 |
|---|---|---|---|
| 简单恢复模式(SIMPLE) | 自动截断日志,不支持日志备份 | 完整、差异、文件备份 | 仅能恢复到完整或差异备份点 |
| 完整恢复模式(FULL) | 保留所有日志,支持日志备份 | 完整、差异、日志、文件备份 | 支持时间点恢复 |
| 大容量日志恢复模式(BULK_LOGGED) | 最小化大容量操作日志记录 | 完整、差异、日志、文件备份 | 支持时间点恢复(大容量操作期间除外) |
📌 选择建议:
- 开发/测试环境 → 简单模式。
- 生产OLTP系统 → 完整模式。
- 数据仓库/ETL期间 → 临时切换为大容量日志模式。
3.1 配置恢复模式
语法:
sql
ALTER DATABASE database_name
SET RECOVERY { FULL | BULK_LOGGED | SIMPLE }
案例1:查看和设置恢复模式
sql
-- 查看当前数据库恢复模式
SELECT name, recovery_model_desc
FROM sys.databases
WHERE name = 'CompanyDB';
-- 设置为完整恢复模式(生产推荐)
ALTER DATABASE CompanyDB SET RECOVERY FULL;
-- 设置为简单恢复模式(开发环境)
ALTER DATABASE CompanyDB SET RECOVERY SIMPLE;
-- 设置为大容量日志模式(执行大容量导入前)
ALTER DATABASE CompanyDB SET RECOVERY BULK_LOGGED;
GO
注释:
- 修改恢复模式后,建议立即进行一次完整备份。
- 从简单模式切换到完整模式后,第一个事务日志备份会失败,需先做完整备份。
四、备份设备
备份设备是 SQL Server 存储备份的逻辑容器,可映射到磁盘文件或磁带设备。
4.1 备份设备类型
- 磁盘设备 :
.bak文件(最常用)。 - 磁带设备:物理磁带(企业级备份)。
- Azure Blob 存储:云备份(SQL Server 2012 SP1+)。
📌 推荐使用磁盘文件,路径需确保 SQL Server 服务账户有读写权限。
4.2 创建备份设备
语法:
sql
-- 创建永久备份设备(逻辑名映射到物理路径)
EXEC sp_addumpdevice
@devtype = 'disk' | 'tape',
@logicalname = 'logical_device_name',
@physicalname = 'physical_path';
-- 创建临时备份设备(直接在 BACKUP 语句中指定路径)
案例2:创建永久备份设备
sql
-- 创建磁盘备份设备(逻辑名:CompanyDB_Full_Bak)
EXEC sp_addumpdevice
@devtype = 'disk',
@logicalname = 'CompanyDB_Full_Bak',
@physicalname = 'D:\SQLBackups\CompanyDB_Full.bak';
-- 创建日志备份设备
EXEC sp_addumpdevice
@devtype = 'disk',
@logicalname = 'CompanyDB_Log_Bak',
@physicalname = 'D:\SQLBackups\CompanyDB_Log.trn'; -- .trn 通常用于日志备份
-- 验证设备创建
SELECT * FROM sys.backup_devices;
GO
注释:
sp_addumpdevice创建的是永久逻辑设备,可重复使用。- 物理路径需确保 SQL Server 服务账户(如 NT Service\MSSQLSERVER)有写入权限。
- 逻辑设备名是数据库引擎内部名称,与物理文件名无关。
4.3 查看备份设备
语法:
sql
-- 查看所有备份设备
SELECT * FROM sys.backup_devices;
-- 使用系统存储过程
EXEC sp_helpdevice; -- 显示所有设备(包括数据设备)
案例3:查看备份设备信息
sql
-- 查询备份设备
SELECT
name AS LogicalDeviceName,
physical_name AS PhysicalPath,
type_desc AS DeviceType
FROM sys.backup_devices;
-- 查看设备使用情况(需结合备份历史)
SELECT
bs.database_name,
bs.backup_start_date,
bs.backup_finish_date,
bmf.physical_device_name,
bs.type AS BackupType -- D=完整, I=差异, L=日志
FROM msdb.dbo.backupset bs
JOIN msdb.dbo.backupmediafamily bmf ON bs.media_set_id = bmf.media_set_id
WHERE bs.database_name = 'CompanyDB'
ORDER BY bs.backup_start_date DESC;
GO
注释:
msdb数据库存储备份历史记录。backupset.type:D=完整,I=差异,L=日志。
4.4 删除备份设备
语法:
sql
EXEC sp_dropdevice @logicalname = 'logical_device_name' [, @delfile = 'delfile'];
案例4:删除备份设备
sql
-- 删除逻辑设备(不删除物理文件)
EXEC sp_dropdevice @logicalname = 'CompanyDB_Full_Bak';
-- 删除逻辑设备并删除物理文件(谨慎使用!)
EXEC sp_dropdevice @logicalname = 'CompanyDB_Log_Bak', @delfile = 'delfile';
-- 验证删除
SELECT * FROM sys.backup_devices WHERE name LIKE 'CompanyDB%';
GO
⚠️ 警告:
@delfile = 'delfile'会物理删除文件,不可恢复!- 生产环境建议先手动备份文件,再删除逻辑设备。
五、使用 Transact-SQL 语句备份数据库
5.1 完整备份与差异备份
语法:
sql
-- 完整备份
BACKUP DATABASE database_name
TO DISK = 'path' | TAPE = 'path' | <backup_device>
[ WITH { FORMAT | INIT | SKIP | ... } ]
-- 差异备份
BACKUP DATABASE database_name
TO DISK = 'path'
WITH DIFFERENTIAL;
案例5:完整备份与差异备份
sql
USE master;
GO
-- 1. 完整备份(基础备份)
BACKUP DATABASE CompanyDB
TO DISK = 'D:\SQLBackups\CompanyDB_Full_20250914.bak'
WITH
FORMAT, -- 初始化媒体集(覆盖现有备份)
INIT, -- 覆盖现有备份集(默认)
NAME = 'Full Backup of CompanyDB',
DESCRIPTION = 'Full backup before major update',
STATS = 10; -- 每完成10%显示进度
-- 2. 执行一些数据修改(模拟业务操作)
USE CompanyDB;
GO
INSERT INTO dbo.Orders (CustomerID, OrderDate) VALUES (1, GETDATE());
GO
-- 3. 差异备份(仅备份更改部分)
BACKUP DATABASE CompanyDB
TO DISK = 'D:\SQLBackups\CompanyDB_Diff_20250914_1.bak'
WITH
DIFFERENTIAL, -- 关键字:差异备份
NAME = 'Differential Backup 1',
DESCRIPTION = 'After inserting test orders',
STATS = 10;
-- 4. 再次修改数据
INSERT INTO dbo.Orders (CustomerID, OrderDate) VALUES (2, GETDATE());
GO
-- 5. 第二次差异备份(仍基于第一次完整备份)
BACKUP DATABASE CompanyDB
TO DISK = 'D:\SQLBackups\CompanyDB_Diff_20250914_2.bak'
WITH DIFFERENTIAL, NAME = 'Differential Backup 2', STATS = 10;
-- 查看备份集信息
RESTORE HEADERONLY FROM DISK = 'D:\SQLBackups\CompanyDB_Full_20250914.bak';
RESTORE HEADERONLY FROM DISK = 'D:\SQLBackups\CompanyDB_Diff_20250914_2.bak';
GO
注释:
FORMAT:初始化备份媒体,删除所有备份集。INIT:追加到媒体,但覆盖同名备份集(默认行为)。DIFFERENTIAL:指定差异备份。STATS = 10:显示备份进度(每10%)。RESTORE HEADERONLY:查看备份文件头信息(包含备份类型、LSN等)。
5.2 文件和文件组备份
适用于大型数据库,可单独备份/恢复特定文件组。
语法:
sql
-- 文件备份
BACKUP DATABASE database_name
FILE = 'logical_file_name'
TO DISK = 'path'
-- 文件组备份
BACKUP DATABASE database_name
FILEGROUP = 'filegroup_name'
TO DISK = 'path'
案例6:文件组备份
sql
-- 假设 CompanyDB 有文件组 'PRIMARY' 和 'FG_Sales'
-- 查看文件组
SELECT
name AS FileGroupName,
type_desc
FROM sys.filegroups;
-- 查看文件
SELECT
name AS LogicalFileName,
physical_name AS PhysicalPath,
type_desc,
state_desc,
filegroup_name = FILEGROUP_NAME(data_space_id)
FROM sys.database_files;
-- 备份 PRIMARY 文件组
BACKUP DATABASE CompanyDB
FILEGROUP = 'PRIMARY'
TO DISK = 'D:\SQLBackups\CompanyDB_PRIMARY_FG.bak'
WITH NAME = 'Primary Filegroup Backup', STATS = 10;
-- 备份特定文件(如主数据文件)
BACKUP DATABASE CompanyDB
FILE = 'CompanyDB_Data' -- 逻辑文件名
TO DISK = 'D:\SQLBackups\CompanyDB_DataFile.bak'
WITH NAME = 'Data File Backup', STATS = 10;
GO
注释:
- 文件组备份需在完整 或大容量日志恢复模式下。
- 恢复时需先还原完整数据库或文件组,再应用日志。
5.3 事务日志备份
语法:
sql
BACKUP LOG database_name
TO DISK = 'path'
[ WITH { FORMAT | INIT | ... } ]
案例7:事务日志备份
sql
-- 确保数据库在完整恢复模式
ALTER DATABASE CompanyDB SET RECOVERY FULL;
GO
-- 如果是首次日志备份,需先做完整备份
BACKUP DATABASE CompanyDB TO DISK = 'D:\SQLBackups\CompanyDB_Full_Init.bak' WITH INIT;
GO
-- 执行业务操作
USE CompanyDB;
INSERT INTO dbo.Orders (CustomerID, OrderDate) VALUES (3, GETDATE());
GO
-- 备份事务日志
BACKUP LOG CompanyDB
TO DISK = 'D:\SQLBackups\CompanyDB_Log_20250914_1.trn'
WITH
NAME = 'Transaction Log Backup 1',
STATS = 10;
-- 再次操作并备份日志
INSERT INTO dbo.Orders (CustomerID, OrderDate) VALUES (4, GETDATE());
GO
BACKUP LOG CompanyDB
TO DISK = 'D:\SQLBackups\CompanyDB_Log_20250914_2.trn'
WITH NAME = 'Transaction Log Backup 2', STATS = 10;
-- 查看日志链
SELECT
database_name,
backup_start_date,
first_lsn, -- 日志序列号起始
last_lsn, -- 日志序列号结束
checkpoint_lsn,
database_backup_lsn -- 关联的完整备份LSN
FROM msdb.dbo.backupset
WHERE database_name = 'CompanyDB' AND type = 'L' -- L=Log
ORDER BY backup_start_date;
GO
注释:
- 事务日志备份必须在完整或大容量日志恢复模式下。
- 日志备份会截断日志(释放空间),但不收缩文件。
first_lsn和last_lsn用于确定日志备份的顺序和连续性。- 日志链断裂会导致无法恢复到最新状态!
六、在 SQL Server Management Studio (SSMS) 中还原数据库
⚠️ 还原前注意事项:
- 备份当前状态:还原前备份尾日志(如果可能)。
- 独占访问:还原时数据库需处于单用户模式或无连接。
- 空间检查:确保目标磁盘有足够空间。
- 权限 :执行还原的账户需有
sysadmin或dbcreator权限。- 还原顺序:完整 → 差异 → 日志(按时间顺序)。
6.1 还原数据库的方式
- 完整还原:从完整备份恢复整个数据库。
- 差异还原:先还原完整备份,再还原差异备份。
- 日志还原:还原完整/差异后,依次还原日志备份。
- 时间点还原:还原到指定日期和时间。
- 文件/文件组还原:还原特定文件或文件组。
6.2 还原数据库备份(SSMS 操作简述)
- 右键数据库 → 任务 → 还原 → 数据库。
- 选择源设备(备份文件)。
- 选择备份集(完整、差异、日志)。
- 选择"覆盖现有数据库"。
- 在"选项"页勾选"RESTRICTED_USER"或"单用户模式"。
- 点击"确定"执行还原。
📌 关键选项:
- RESTORE WITH REPLACE:强制覆盖(危险!)。
- RESTORE WITH NORECOVERY:还原后保持"正在还原"状态(用于后续日志还原)。
- RESTORE WITH RECOVERY:还原后使数据库可用(默认)。
七、使用 Transact-SQL 语句还原数据库
7.1 完整备份还原
语法:
sql
RESTORE DATABASE database_name
FROM DISK = 'path'
[ WITH { REPLACE | NORECOVERY | RECOVERY | ... } ]
案例8:还原完整备份
sql
-- 1. 将数据库设置为单用户模式(强制断开连接)
ALTER DATABASE CompanyDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
-- 2. 还原完整备份(覆盖现有数据库)
RESTORE DATABASE CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Full_20250914.bak'
WITH
REPLACE, -- 强制覆盖现有数据库
RECOVERY, -- 还原后使数据库可用
STATS = 10;
-- 3. 恢复为多用户模式
ALTER DATABASE CompanyDB SET MULTI_USER;
GO
-- 验证数据
USE CompanyDB;
SELECT COUNT(*) FROM dbo.Orders; -- 应包含备份时的数据
GO
注释:
SINGLE_USER WITH ROLLBACK IMMEDIATE:强制断开所有连接。REPLACE:忽略数据库名称匹配和日志链检查(谨慎使用)。RECOVERY:最后一步,使数据库在线。
7.2 差异备份还原
语法:
sql
-- 先还原完整备份(NORECOVERY)
RESTORE DATABASE database_name FROM DISK = 'full_path' WITH NORECOVERY;
-- 再还原差异备份(RECOVERY 或 NORECOVERY)
RESTORE DATABASE database_name FROM DISK = 'diff_path' WITH RECOVERY;
案例9:还原完整 + 差异备份
sql
-- 1. 设置单用户模式
ALTER DATABASE CompanyDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
-- 2. 还原完整备份(保持NORECOVERY状态)
RESTORE DATABASE CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Full_20250914.bak'
WITH
REPLACE,
NORECOVERY, -- 关键:保持"正在还原"状态
STATS = 10;
-- 3. 还原差异备份(应用更改)
RESTORE DATABASE CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Diff_20250914_2.bak'
WITH
RECOVERY, -- 关键:完成还原,数据库可用
STATS = 10;
-- 4. 恢复多用户模式
ALTER DATABASE CompanyDB SET MULTI_USER;
GO
-- 验证:应包含差异备份时的数据
USE CompanyDB;
SELECT COUNT(*) FROM dbo.Orders; -- 应比完整备份时多2条记录(案例5)
GO
注释:
NORECOVERY用于中间步骤,允许后续还原。RECOVERY用于最后一步,使数据库在线。
7.3 事务日志备份还原
语法:
sql
-- 还原日志备份(通常在完整/差异后)
RESTORE LOG database_name
FROM DISK = 'log_path'
WITH NORECOVERY | RECOVERY;
案例10:完整 + 差异 + 日志还原(时间点恢复基础)
sql
-- 目标:还原到第二个日志备份之后的状态
-- 1. 设置单用户模式
ALTER DATABASE CompanyDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
-- 2. 还原完整备份(NORECOVERY)
RESTORE DATABASE CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Full_Init.bak' -- 案例7的完整备份
WITH
REPLACE,
NORECOVERY,
STATS = 10;
-- 3. 还原第一个日志备份(NORECOVERY)
RESTORE LOG CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Log_20250914_1.trn'
WITH NORECOVERY, STATS = 10;
-- 4. 还原第二个日志备份(RECOVERY)
RESTORE LOG CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Log_20250914_2.trn'
WITH RECOVERY, STATS = 10; -- 最后一步,数据库可用
-- 5. 恢复多用户模式
ALTER DATABASE CompanyDB SET MULTI_USER;
GO
-- 验证:应包含日志备份时插入的记录(CustomerID=3,4)
USE CompanyDB;
SELECT CustomerID, OrderDate FROM dbo.Orders WHERE CustomerID IN (3,4);
GO
注释:
- 日志备份必须按顺序还原(按 LSN 或时间)。
- 最后一个日志备份用
RECOVERY。- 中间日志备份用
NORECOVERY。
7.4 文件和文件组备份还原
语法:
sql
-- 还原文件组
RESTORE DATABASE database_name
FILEGROUP = 'filegroup_name'
FROM DISK = 'path'
WITH NORECOVERY;
-- 还原后需应用日志
RESTORE LOG ... WITH RECOVERY;
案例11:还原文件组
sql
-- 假设 FG_Sales 文件组损坏,需还原
-- 1. 设置单用户模式
ALTER DATABASE CompanyDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
-- 2. 还原 FG_Sales 文件组(NORECOVERY)
RESTORE DATABASE CompanyDB
FILEGROUP = 'FG_Sales'
FROM DISK = 'D:\SQLBackups\CompanyDB_FG_Sales.bak'
WITH
REPLACE,
NORECOVERY,
STATS = 10;
-- 3. 还原必要的日志备份(使文件组数据一致)
RESTORE LOG CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Log_20250914_2.trn'
WITH RECOVERY, STATS = 10;
-- 4. 恢复多用户模式
ALTER DATABASE CompanyDB SET MULTI_USER;
GO
-- 验证文件组状态
SELECT
name AS FileGroupName,
is_read_only,
state_desc
FROM sys.filegroups
WHERE name = 'FG_Sales';
GO
注释:
- 文件组还原后,数据库可能处于"部分还原"状态。
- 必须应用足够的日志备份使文件组与数据库其他部分同步。
- 未还原的文件组可能处于"离线"状态。
7.5 将数据库还原到某个时间点
语法:
sql
-- 在日志还原时指定时间点
RESTORE LOG database_name
FROM DISK = 'log_path'
WITH
RECOVERY,
STOPAT = 'YYYY-MM-DD HH:MI:SS';
案例12:时间点还原(PITR - Point In Time Recovery)
sql
-- 目标:还原到 2025-09-14 14:30:00 的状态
-- 1. 设置单用户模式
ALTER DATABASE CompanyDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
-- 2. 还原完整备份(NORECOVERY)
RESTORE DATABASE CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Full_Init.bak'
WITH REPLACE, NORECOVERY, STATS = 10;
-- 3. 还原第一个日志备份(NORECOVERY)
RESTORE LOG CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Log_20250914_1.trn'
WITH NORECOVERY, STATS = 10;
-- 4. 还原第二个日志备份,但停止在指定时间点(RECOVERY)
RESTORE LOG CompanyDB
FROM DISK = 'D:\SQLBackups\CompanyDB_Log_20250914_2.trn'
WITH
RECOVERY,
STOPAT = '2025-09-14 14:30:00', -- 关键:指定时间点
STATS = 10;
-- 5. 恢复多用户模式
ALTER DATABASE CompanyDB SET MULTI_USER;
GO
-- 验证:数据应包含到 14:30:00 为止的事务
USE CompanyDB;
SELECT GETDATE() AS CurrentTime; -- 注意:数据库时间未改变,数据状态改变
GO
注释:
STOPAT可精确到秒。- 时间点必须在日志备份的时间范围内。
- 如果指定时间点在日志备份中间,SQL Server 会应用日志直到该时间点。
- 适用于误删除、误更新后的精确恢复。
八、建立自动备份的维护计划
使用 SQL Server Agent 创建自动备份任务。
⚠️ 前提:SQL Server Agent 服务必须启动。
案例13:创建自动完整备份维护计划(T-SQL 脚本)
sql
USE msdb;
GO
-- 1. 创建备份目录(确保路径存在且SQL服务账户有权限)
-- EXEC xp_create_subdir 'D:\AutoBackups\'; -- 取消注释以创建目录
-- 2. 创建作业(Job)
EXEC dbo.sp_add_job
@job_name = N'Daily Full Backup - CompanyDB',
@description = N'Automated daily full backup of CompanyDB',
@enabled = 1; -- 启用作业
-- 3. 添加作业步骤
DECLARE @job_id UNIQUEIDENTIFIER;
SELECT @job_id = job_id FROM dbo.sysjobs WHERE name = N'Daily Full Backup - CompanyDB';
EXEC sp_add_jobstep
@job_id = @job_id,
@step_name = N'Backup CompanyDB',
@subsystem = N'TSQL',
@command = N'
BACKUP DATABASE [CompanyDB]
TO DISK = N''D:\AutoBackups\CompanyDB_Full_'' +
REPLACE(CONVERT(VARCHAR, GETDATE(), 112), ''/'', '''') + ''.bak''
WITH
INIT,
COMPRESSION, -- 启用压缩(SQL Server 2008+)
NAME = N''Automated Full Backup'',
STATS = 10;
',
@on_success_action = 1, -- 成功后退出
@on_fail_action = 2; -- 失败后退出
-- 4. 创建作业调度(每天凌晨2点)
EXEC dbo.sp_add_schedule
@schedule_name = N'Daily 2AM',
@freq_type = 4, -- 每天
@freq_interval = 1, -- 每1天
@active_start_time = 020000; -- 02:00:00
-- 5. 将调度附加到作业
DECLARE @schedule_id INT;
SELECT @schedule_id = schedule_id FROM dbo.sysschedules WHERE name = N'Daily 2AM';
EXEC sp_attach_schedule
@job_id = @job_id,
@schedule_id = @schedule_id;
-- 6. 将作业添加到本地服务器
EXEC dbo.sp_add_jobserver
@job_id = @job_id,
@server_name = N'(local)';
-- 7. 启动作业(可选,测试用)
-- EXEC dbo.sp_start_job @job_name = N'Daily Full Backup - CompanyDB';
-- 8. 查看作业
SELECT name, enabled FROM dbo.sysjobs WHERE name LIKE '%CompanyDB%';
GO
注释:
COMPRESSION:启用备份压缩,减少磁盘空间和I/O(企业版/标准版支持)。REPLACE(CONVERT(VARCHAR, GETDATE(), 112), '/', ''):生成 YYYYMMDD 格式日期。- 作业历史记录存储在
msdb数据库中。- 可通过 SSMS → SQL Server Agent → 作业 查看和管理。
九、通过 Always Encrypted 安全功能为数据加密
Always Encrypted 是客户端加密技术,确保数据在传输和存储时始终加密,SQL Server 无法访问明文密钥。
📌 核心组件:
- 列主密钥(CMK):存储在客户端或密钥库(如Windows证书存储)。
- 列加密密钥(CEK):由 CMK 加密,存储在数据库中。
- 加密列:使用 CEK 加密的数据列。
案例14:配置 Always Encrypted
sql
USE CompanyDB;
GO
-- 步骤1:创建列主密钥(CMK)
-- 注意:此操作通常在 SSMS 中通过向导完成(需配置密钥存储)
-- 以下为 T-SQL 示例(需启用 Always Encrypted 功能)
-- 创建列主密钥元数据(指向Windows证书存储)
CREATE COLUMN MASTER KEY CMK_Auto1
WITH (
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
KEY_PATH = N'CurrentUser/My/CERTIFICATE_THUMBPRINT' -- 替换为实际证书指纹
);
GO
-- 步骤2:创建列加密密钥(CEK)
CREATE COLUMN ENCRYPTION KEY CEK_Auto1
WITH VALUES (
COLUMN_MASTER_KEY = CMK_Auto1,
ALGORITHM = 'RSA_OAEP',
ENCRYPTED_VALUE = 0x... -- 实际值由向导生成
);
GO
-- 步骤3:创建加密表或修改现有表
-- 创建新表
CREATE TABLE dbo.EncryptedCustomers (
CustomerID INT IDENTITY(1,1) PRIMARY KEY,
FirstName NVARCHAR(50) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (
COLUMN_ENCRYPTION_KEY = CEK_Auto1,
ENCRYPTION_TYPE = Deterministic, -- 支持等值查询
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
),
SSN CHAR(11) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (
COLUMN_ENCRYPTION_KEY = CEK_Auto1,
ENCRYPTION_TYPE = Randomized, -- 更安全,不支持查询
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
)
);
GO
-- 步骤4:插入数据(需在支持 Always Encrypted 的客户端执行,如SSMS启用AE)
-- INSERT INTO dbo.EncryptedCustomers (FirstName, SSN) VALUES ('Alice', '123-45-6789');
-- 步骤5:查询数据(客户端自动解密)
-- SELECT * FROM dbo.EncryptedCustomers; -- 在支持AE的客户端显示明文
-- 查看加密列信息
SELECT
t.name AS TableName,
c.name AS ColumnName,
cek.name AS ColumnEncryptionKeyName,
c.encryption_type_desc,
c.encryption_algorithm_name
FROM sys.columns c
JOIN sys.column_encryption_keys cek ON c.column_encryption_key_id = cek.column_encryption_key_id
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.encryption_type IS NOT NULL;
GO
注释:
- Deterministic:相同明文产生相同密文,支持等值查询、分组、连接。
- Randomized:相同明文产生不同密文,更安全,但不支持查询。
- 客户端要求:应用程序或 SSMS 必须启用 Always Encrypted 并能访问 CMK。
- SQL Server 永远无法看到明文数据!
十、动态数据屏蔽(Dynamic Data Masking)
动态数据屏蔽在查询结果中隐藏敏感数据,无需修改数据本身。
📌 适用场景:客服人员查看客户信息时隐藏部分手机号、邮箱等。
案例15:配置动态数据屏蔽
sql
USE CompanyDB;
GO
-- 1. 创建测试表
CREATE TABLE dbo.MaskedCustomers (
CustomerID INT IDENTITY(1,1) PRIMARY KEY,
FirstName NVARCHAR(50),
Email VARCHAR(100),
PhoneNumber VARCHAR(15),
CreditCard VARCHAR(19),
Salary DECIMAL(10,2)
);
GO
-- 2. 插入测试数据
INSERT INTO dbo.MaskedCustomers (FirstName, Email, PhoneNumber, CreditCard, Salary)
VALUES
('Alice', 'alice@example.com', '13800138000', '1234-5678-9012-3456', 85000.00),
('Bob', 'bob@test.org', '13900139000', '9876-5432-1098-7654', 92000.00);
GO
-- 3. 添加动态数据屏蔽规则
ALTER TABLE dbo.MaskedCustomers
ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()'); -- 邮箱格式屏蔽
ALTER TABLE dbo.MaskedCustomers
ALTER COLUMN PhoneNumber ADD MASKED WITH (FUNCTION = 'partial(3,"XXXXXXX",2)');
-- 保留前3后2,中间用X填充 → 138XXXXXXX00
ALTER TABLE dbo.MaskedCustomers
ALTER COLUMN CreditCard ADD MASKED WITH (FUNCTION = 'partial(0,"XXXX-XXXX-XXXX-",4)');
-- 显示后4位 → XXXX-XXXX-XXXX-3456
ALTER TABLE dbo.MaskedCustomers
ALTER COLUMN Salary ADD MASKED WITH (FUNCTION = 'default()'); -- 默认屏蔽(数字显示0)
GO
-- 4. 创建普通用户(无UNMASK权限)
CREATE USER masked_user WITHOUT LOGIN; -- 无登录用户用于测试
GRANT SELECT ON dbo.MaskedCustomers TO masked_user;
GO
-- 5. 以普通用户身份查询(数据被屏蔽)
EXECUTE AS USER = 'masked_user';
SELECT * FROM dbo.MaskedCustomers;
-- 结果示例:
-- Email: aXXX@XXXX.com
-- PhoneNumber: 138XXXXXXX00
-- CreditCard: XXXX-XXXX-XXXX-3456
-- Salary: 0.00
REVERT;
GO
-- 6. 创建特权用户(有UNMASK权限)
CREATE USER unmask_user WITHOUT LOGIN;
GRANT SELECT ON dbo.MaskedCustomers TO unmask_user;
GRANT UNMASK TO unmask_user; -- 关键:授予UNMASK权限
GO
-- 7. 以特权用户身份查询(显示明文)
EXECUTE AS USER = 'unmask_user';
SELECT * FROM dbo.MaskedCustomers;
-- 显示原始数据
REVERT;
GO
-- 8. 查看屏蔽规则
SELECT
t.name AS TableName,
c.name AS ColumnName,
mc.masking_function AS MaskingFunction
FROM sys.masked_columns mc
JOIN sys.tables t ON mc.object_id = t.object_id
JOIN sys.columns c ON mc.object_id = c.object_id AND mc.column_id = c.column_id
WHERE t.name = 'MaskedCustomers';
GO
-- 9. 删除屏蔽规则
ALTER TABLE dbo.MaskedCustomers ALTER COLUMN Email DROP MASKED;
-- 或删除整个表重建
GO
注释:
email():自动屏蔽邮箱(保留首字母和域名)。partial(prefix, padding, suffix):自定义屏蔽格式。default():根据数据类型默认屏蔽(字符串→XXXX,数字→0,日期→1900-01-01)。UNMASK权限控制谁能看到明文。- 无性能开销:屏蔽在结果返回前应用。
十一、综合性案例
综合案例1:构建完整的备份恢复策略
sql
-- 场景:为生产数据库 CompanyDB 设计完整备份策略(完整+差异+日志)
USE master;
GO
-- 步骤1:设置恢复模式为完整
ALTER DATABASE CompanyDB SET RECOVERY FULL;
GO
-- 步骤2:创建备份目录(确保路径存在)
-- EXEC xp_create_subdir 'D:\ProductionBackups\';
-- 步骤3:执行初始完整备份
BACKUP DATABASE CompanyDB
TO DISK = 'D:\ProductionBackups\CompanyDB_Full_Initial.bak'
WITH
INIT,
COMPRESSION,
NAME = 'Initial Full Backup for Production',
STATS = 10;
GO
-- 步骤4:创建自动备份作业(完整、差异、日志)
USE msdb;
GO
-- 4.1 每周日完整备份
EXEC sp_add_job @job_name = 'Weekly Full Backup - CompanyDB', @enabled = 1;
-- ... 添加步骤和调度(参考案例13,每周日执行)
-- 4.2 每天差异备份(除周日外)
EXEC sp_add_job @job_name = 'Daily Differential Backup - CompanyDB', @enabled = 1;
-- ... 添加步骤:BACKUP DATABASE ... WITH DIFFERENTIAL
-- ... 调度:每天,除周日
-- 4.3 每15分钟事务日志备份
EXEC sp_add_job @job_name = 'Every 15min Log Backup - CompanyDB', @enabled = 1;
-- ... 添加步骤:BACKUP LOG ...
-- ... 调度:每15分钟
-- 步骤5:模拟灾难恢复(还原到最新状态)
-- 假设数据库损坏,需还原
USE master;
GO
-- 5.1 尾日志备份(如果可能)
-- BACKUP LOG CompanyDB TO DISK = 'D:\ProductionBackups\tail.trn' WITH NORECOVERY;
-- 5.2 还原最新完整备份
ALTER DATABASE CompanyDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE CompanyDB
FROM DISK = 'D:\ProductionBackups\CompanyDB_Full_Initial.bak'
WITH REPLACE, NORECOVERY, STATS = 10;
-- 5.3 还原最新差异备份
RESTORE DATABASE CompanyDB
FROM DISK = 'D:\ProductionBackups\CompanyDB_Diff_Latest.bak'
WITH NORECOVERY, STATS = 10;
-- 5.4 按顺序还原所有日志备份(包括尾日志)
RESTORE LOG CompanyDB FROM DISK = 'D:\ProductionBackups\Log1.trn' WITH NORECOVERY;
RESTORE LOG CompanyDB FROM DISK = 'D:\ProductionBackups\Log2.trn' WITH NORECOVERY;
-- ... 继续还原所有日志
RESTORE LOG CompanyDB FROM DISK = 'D:\ProductionBackups\tail.trn' WITH RECOVERY;
-- 5.5 恢复多用户模式
ALTER DATABASE CompanyDB SET MULTI_USER;
GO
-- 步骤6:验证数据一致性
USE CompanyDB;
DBCC CHECKDB WITH NO_INFOMSGS;
GO
注释:
- 完整备份每周一次,减少I/O压力。
- 差异备份每天一次,减少备份大小。
- 日志备份每15分钟,确保最多丢失15分钟数据。
- 尾日志备份在灾难前尽可能捕获最后事务。
DBCC CHECKDB验证还原后数据库完整性。
综合案例2:安全备份与合规性加固
sql
-- 场景:结合加密、屏蔽、审计的备份恢复方案
USE master;
GO
-- 1. 启用透明数据加密(TDE) - 数据库级加密(需企业版)
-- CREATE DATABASE ENCRYPTION KEY ...
-- ALTER DATABASE ... SET ENCRYPTION ON;
-- 2. 对敏感列使用 Always Encrypted(如身份证号)
USE CompanyDB;
GO
-- ... 参考案例14配置AE
-- 3. 对非敏感但需保护的列使用动态数据屏蔽(如薪资)
-- ... 参考案例15配置DDM
-- 4. 创建审计跟踪备份操作
USE master;
GO
CREATE SERVER AUDIT BackupAudit
TO FILE (FILEPATH = 'D:\AuditLogs\', MAXSIZE = 100 MB)
WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE);
CREATE SERVER AUDIT SPECIFICATION BackupAuditSpec
FOR SERVER AUDIT BackupAudit
ADD (BACKUP_RESTORE_GROUP); -- 审计所有备份还原操作
ALTER SERVER AUDIT BackupAudit WITH (STATE = ON);
ALTER SERVER AUDIT SPECIFICATION BackupAuditSpec WITH (STATE = ON);
GO
-- 5. 创建安全的备份作业(备份到加密卷或网络共享)
USE msdb;
GO
-- 创建作业,备份到安全位置(如 \\SecureServer\Backups\)
-- 确保网络共享权限严格控制
-- 6. 验证审计记录
SELECT
event_time,
server_principal_name,
database_name,
statement
FROM sys.fn_get_audit_file('D:\AuditLogs\*.sqlaudit', DEFAULT, DEFAULT)
WHERE action_id IN ('BAC', 'REST'); -- BAC=Backup, REST=Restore
GO
-- 7. 清理(测试后)
-- ALTER SERVER AUDIT SPECIFICATION BackupAuditSpec WITH (STATE = OFF);
-- DROP SERVER AUDIT SPECIFICATION BackupAuditSpec;
-- ALTER SERVER AUDIT BackupAudit WITH (STATE = OFF);
-- DROP SERVER AUDIT BackupAudit;
GO
注释:
- TDE:加密整个数据库文件(静态加密)。
- Always Encrypted:保护特定列,即使DBA也无法查看。
- 动态数据屏蔽:控制应用层数据可见性。
- 审计:跟踪谁在何时执行了备份/还原操作。
- 备份文件应存储在访问受控的位置。
十二、最佳实践与注意事项
✅ 最佳实践:
-
3-2-1 备份规则:
- 至少 3 份数据副本。
- 存储在 2 种不同介质。
- 1 份异地存储(如云、磁带)。
-
定期测试恢复:备份无效,除非你测试过恢复!
-
监控备份作业:设置警报,确保备份成功。
-
压缩备份 :节省空间和网络带宽(
WITH COMPRESSION)。 -
验证备份 :使用
RESTORE VERIFYONLY验证备份完整性。 -
清理旧备份:使用维护计划或脚本删除过期备份。
-
文档化恢复步骤:确保团队知道如何执行灾难恢复。
-
结合多种技术:备份 + Always Encrypted + TDE + 审计。
⚠️ 注意事项:
- 日志链连续性:日志备份中断会导致无法恢复到最新状态。
- 空间管理:备份文件会占用大量空间,需定期清理。
- 权限最小化 :备份操作账户只需
db_backupoperator角色。 - 网络备份风险:备份到网络路径需确保网络稳定和权限安全。
- Always Encrypted 限制:不支持所有数据类型和操作(如范围查询)。
- 动态数据屏蔽不是安全机制:只是隐藏显示,数据仍存储明文。
- 恢复模式选择:根据业务需求选择,避免在简单模式下需要日志恢复。
✅ 本章全面覆盖 SQL Server 2019 备份与恢复的核心技术,从基础备份类型、恢复模式到高级的 Always Encrypted 和动态数据屏蔽,并提供实战案例。建立完善的备份恢复策略是保障业务连续性的基石!