随着企业数据量的爆炸式增长,如何在有限的维护窗口内完成高效的数据备份,成为DBA面临的严峻挑战。达梦数据库提供了基于 LSN 的增量备份机制,但在面对 TB 级大库时,传统的增量备份仍面临“全盘扫描”带来的 I/O 瓶颈。为此,DM 引入了 块改变跟踪(Block Change Tracking, BCT) 技术,旨在实现从“全量扫描”到“精准定位”的质变。
本文档将从底层原理出发,结合实战压测,全方位解析 DM 的备份还原机制与 BCT 技术的实际应用效果。
本文主要涵盖以下核心内容:
在数据库联机运行(Online)状态下进行物理备份,本质上是对一个不断变化的系统进行快照。这一过程面临两个核心矛盾:
达梦数据库(DM)通过 “磁盘快照 + 增量日志补偿” 的机制解决了上述问题。本文将深入存储引擎底层,解析这一机制的实现原理
与逻辑导出不同,DM 的物理备份(DMRMAN/SQL Backup)并不通过 SQL 接口读取数据,也不直接锁定内存中的 Buffer Pool。
由于备份过程跨越了一定的时间窗口,备份集内的物理数据页在逻辑时间上是乱序的:
BEGIN_LSN。END_LSN。结果:备份集中,P1 停留在 T2 时刻,P2 停留在 T4 时刻。整个数据集合处于“不一致”状态,如果直接使用这些数据启动数据库,将导致逻辑错误。
为了修复上述不一致,DM 在备份数据页的同时,必须捕获备份期间产生的所有变更。
BEGIN_LSN 到 END_LSN 之间的所有联机 REDO 日志。恢复过程分为两个严格的阶段:**还原(Restore)**负责物理还原,**恢复(Recover)**负责逻辑追平。
这是核心中的核心。DM 引擎启动重演进程,读取备份集中的 REDO 日志,按 LSN 顺序执行。
在此过程中,系统通过**幂等性检查 (Idempotency Check)**判断某条日志是否应该应用到某个数据页上。
假设系统正在重演一条日志记录:
系统执行严格的 LSN 大小比对逻辑:
情况 A:Log_LSN > Page_LSN (需要更新)
Page_LSN 更新为 Log_LSN。情况 B:Log_LSN <= Page_LSN (无需更新)
当重演进程执行完所有 BEGIN_LSN 到 END_LSN 之间的日志后:
END_LSN 的状态。END_LSN 或接近该状态)。END_LSN 这一时刻。此时,DM 执行最后一步操作:更新 DB_MAGIC。这标志着数据库已经从“不一致的备份集”转化为“一致的活动库”,可以正常对外提供服务。
达梦数据库的物理备份还原机制,并未试图在备份时刻暂停数据库DML活动,而是采用了“宽容捕获,严谨修正”的策略:
这种机制最大程度地减少了备份对业务系统的锁阻塞,实现了高性能的在线热备。
在理解了全量备份的基础上,我们需要深入探讨在生产环境中最常用的策略:差异增量备份 (Differential Incremental Backup)。
这种策略旨在减少存储占用,背后的使用的机制是“链式依赖”。
差异增量备份的核心逻辑是:只备份自“基备份(Base Backup)”以来发生过修改的数据页。
差异增量备份必须依赖一个已存在的备份集作为“基准”。
增备2 基于 增备1,增备1 基于 全备0。在不开启优化技术的情况下,RMAN 是如何知道哪些数据页发生了变化?答案是**“暴力比对”**。
BEGIN_LSN(基备份开始时的日志序列号)。Page_LSN > 基备份_BEGIN_LSN,说明该页在基备份之后被修改过。Page_LSN <= 基备份_BEGIN_LSN,说明该页自上次备份后未变动,直接丢弃,不写入备份集。结果:虽然备份集只包含变化的 1% 数据,但为了找到这 1%,系统不得不读取 100% 的物理文件。
增量还原不能单独进行,必须依赖完整的备份链条。
假设我们有备份链:全备 A -> 增备 B -> 增备 C。现在要还原到 增备 C 的状态。
物理还原完成后,数据页虽然是新的,但在时间上可能是不一致的(Fuzzy)。
虽然差异增量备份节省了存储空间,但存在两个显著的问题:
基备份 + 增备1 + ... + 增备N 链条。在第3节: 为了验证上述理论,我们将构建一个 5GB 的测试环境。演示在没有进行优化的情况下,即使只插入几条数据,增量备份的 I/O 读取量 和 耗时 依然会惊人地高
——这将为我们在第4节提到BCT 技术提供支持。
(1)数据脏页在内存中尚未刷盘,DM备份如何保存最新数据?
(2)为什么有时候增备特别慢,甚至比全备还慢?
此时INI中:ENABLE_BCT=0;默认状态下
首先,构造 5GB 初始数据:
-- 1. 创建表空间
-- 动作:创建一个名为 TEST 的表空间,包含 1 个数据文件
-- 大小:初始 5GB (5120M),且限制最大为 10GB (MAXSIZE 10240)
create tablespace "TEST"
datafile 'TEST.DBF' size 5120 autoextend on maxsize 10240
CACHE = NORMAL;
-- 2. 创建表
-- 动作:在 TEST 表空间上创建测试表
create table TEST(
ID VARCHAR2 PRIMARY KEY,
NAME VARCHAR2(100),
C_LOB CLOB,
C_LOB1 CLOB,
TTEXT TEXT
) storage(on "TEST");
-- 3. 插入数据
-- 动作:利用层次查询生成约 5GB 的随机数据
INSERT INTO TEST
SELECT
SYS_GUID(),
DBMS_RANDOM.STRING('U',100),
DBMS_RANDOM.STRING('U',40000),
DBMS_RANDOM.STRING('U',40000),
DBMS_RANDOM.STRING('U',40000)
CONNECT BY LEVEL <= 28000;
-- 4. 提交事务
COMMIT;
-- 4. 执行全量备份
BACKUP DATABASE FULL BACKUPSET 'db_test_full_01';
(1)插入耗时:
执行成功, 执行耗时1分 25秒 236毫秒. 执行号:1300 影响了28,000条记录 1条语句执行成功
(2)查看表空间使用情况:
SELECT
NAME AS SPACE_NAME,
-- 将页数转换为 GB: 页数 * 页大小 / 1024 / 1024 / 1024
-- 假设页大小为 8KB (8192)
(TOTAL_SIZE * 8192) / 1024 / 1024 / 1024 AS "总大小(G)",
((TOTAL_SIZE - USED_SIZE) * 8192) / 1024 / 1024 / 1024 AS "可使用(G)",
(USED_SIZE * 8192) / 1024 / 1024 / 1024 AS "已使用(G)",
-- 计算百分比
ROUND((USED_SIZE * 1.0 / TOTAL_SIZE) * 100, 2) AS "使用率(%)"
FROM
V$TABLESPACE;
**(3)全量备份耗时与读写比:**查询 V$BACKUP_HISTORY。
-- 备份完成后,执行此语句,截图记录
SELECT
BACKUP_NAME,
PATH AS BACKUP_PATH,
TYPE, -- 0:全量, 1:增量, 2:表备份, 3:归档备份
READ_SIZE/1024/1024 AS "实际读取(MB)",
WRITE_SIZE/1024/1024 AS "写入大小(MB)",
CASE WHEN WRITE_SIZE = 0 THEN 0
ELSE ROUND(READ_SIZE / WRITE_SIZE, 2)
END AS "读写比(倍数)", -- 除零保护和保留两位小数
DATEDIFF(SS, START_TIME, END_TIME) AS "耗时(秒)"
FROM V$BACKUP_HISTORY
ORDER BY START_TIME DESC
LIMIT 1;
(4)查看备份集大小:
[dmdba@localhost bak]$ cd /home/dmdba/dmdbms/data/DAMENG/bak [dmdba@localhost bak]$ # 查看大小 [dmdba@localhost bak]$ du -sh * 3.9G db_test_full_01 #全量备份集 [dmdba@localhost bak]$
-- 增量:在TEST表空间再插入3G数据
INSERT INTO TEST
SELECT
SYS_GUID(), -- 生成新的唯一 ID
DBMS_RANDOM.STRING('U', 100), -- 生成新的随机 Name
DBMS_RANDOM.STRING('U', 40000), -- 生成新的随机 C_LOB
DBMS_RANDOM.STRING('U', 40000), -- 生成新的随机 C_LOB1
DBMS_RANDOM.STRING('U', 40000) -- 生成新的随机 TTEXT
CONNECT BY LEVEL <= 16500; -- 控制生成 16500 行
-- 提交事务,确保数据落盘
COMMIT;
--执行第一次增量备份,DM 会自动在默认目录下寻找最近的基备份
BACKUP DATABASE INCREMENT BACKUPSET 'db_test_inc_01';
(1)插入耗时:
执行成功, 执行耗时33秒 599毫秒. 执行号:815
影响了16,500条记录
**(2)第一次增量备份耗时与读写比:**查询 V$BACKUP_HISTORY。
(3)查看备份集大小:
[dmdba@localhost bak]$ du -sh * 3.9G db_test_full_01 2.1G db_test_inc_01
-- 第二次增量:在新的表空间进行增量
-- 动作:创建一个名为 TEST_2 的表空间,用于存放本次测试数据
-- 文件:'TEST_2.DBF' (自动存放在数据库默认数据目录下)
-- 大小:初始 5120 MB (5GB),最大允许扩展到 10240 MB (10GB)
CREATE TABLESPACE "TEST_2"
DATAFILE 'TEST_2.DBF' SIZE 5120 AUTOEXTEND ON MAXSIZE 10240
CACHE = NORMAL;
create table TEST_2(
ID VARCHAR2 PRIMARY KEY,
NAME VARCHAR2(100),
C_LOB CLOB,
C_LOB1 CLOB,
TTEXT TEXT
) storage(on "TEST_2");
-- 动作:向 TEST_2表中增量插入约 3GB 的随机测试数据
INSERT INTO TEST_2
SELECT
SYS_GUID(),
DBMS_RANDOM.STRING('U', 100),
DBMS_RANDOM.STRING('U', 40000),
DBMS_RANDOM.STRING('U', 40000),
DBMS_RANDOM.STRING('U', 40000)
CONNECT BY LEVEL <= 16500;
-- 提交事务,确保数据落盘
COMMIT;
-- 3. 执行第二次差异增量备份
BACKUP DATABASE INCREMENT BACKUPSET 'db_test_inc_02';
(1)插入耗时:
执行成功, 执行耗时28秒 344毫秒. 执行号:810 影响了16,500条记录 1条语句执行成功
**(2)第二次增量备份耗时与读写比:**查询 V$BACKUP_HISTORY。
(3)查看备份集大小:
[dmdba@localhost bak]$ du -sh * 3.9G db_test_full_01 2.1G db_test_inc_01 3.5G db_test_inc_02
表 1:数据插入 (SQL执行) 性能统计表
| 阶段 | 插入数据量 | 目标表空间 | SQL执行耗时 | 备注 |
|---|---|---|---|---|
| 初始加载 | 约 5 GB | TEST | 1分 25秒 | 初始造数 |
| 增量一 | 约 3 GB | TEST | 33.6 秒 | 原表空间追加 |
| 增量二 | 约 3 GB | TEST_2 | 28.3 秒 | 新表空间纯写入 |
表 2:备份性能与 IO 效率统计表 (ENABLE_BCT=0)
| 测试阶段 | 动作描述 | 备份集大小 (Write) | 实际读取量 (Read) | 读写比 (IO膨胀率) | 备份耗时 | 现象解读 |
|---|---|---|---|---|---|---|
| 全量基准 | 初始 5GB 全备 | 3869 MB | 4027 MB | 1 : 1 | 18 秒 | 读写基本持平,这是正常的顺序读写特征。 |
| 增量一 | 原表空间 +3GB | 1147 MB | 6371 MB | 5 : 1 | 22 秒 | 效率降低。为了存 1G 数据,系统被迫读取了 6G 数据(包含之前的全量数据)。 |
| 增量二 | 新表空间 +3GB | 2507 MB | 8715 MB | 3 : 1 | 22 秒 | 全盘扫描。读取量 (8.7G) ≈ 全量(4G) + 增量一(2G) + 本次增量(2.5G)。系统把老数据又读了一遍。 |
现象记录:
AVG_READ 很高(全盘读),AVG_WRITE 很低(只写了 100MB)。面对“全盘扫描”的低效,达梦引入了 BCT 技术。
.bct 文件中。这些文件采用环形复用机制,循环记录最新的变化。.bct 文件,获取“脏页列表”,然后精准跳转到对应位置抓取数据。INI参数,可动态修改
SP_SET_PARA_VALUE(1, 'ENABLE_BCT', 1);
0:关闭(默认)。增量备份需要全盘扫描。1:开启。数据库会在后台维护位图(Bitmap),记录哪些数据页发生了修改。SELECT * FROM V$PARAMETER WHERE NAME = 'ENABLE_BCT';SELECT * FROM V$BCT_BITMAP;Sequential Read (全量顺序读) 变为 Random Read (精准随机读)。在第5节将开启BCT后,重复第3节的差异备份测试。
主要测试两个点:
和第3节完全相同操作。
注意清理数据时,在Drop或Truncat表的基础上,需要重建表空间或者Resize。
否则全量备份时将备份先前测试时留下的8G数据。
具体原因将在7.3节进行讲解。
数据清理:
-- 将先前的表drop,再重新插入,测试开启BCT后是否影响插入性能
DROP TABLE TEST;
DROP TABLE TEST_2;
-- 再检查表空间对象,drop表空间
SELECT * FROM DBA_SEGMENTS WHERE TABLESPACE_NAME = 'TEST';
DROP TABLESPACE TEST;
DROP TABLESPACE TEST_2;
表空间已被释放:
--1.重建前面的表空间和TEST表并插入数据
-- 表空间与表局具体SQL创建见3.1;
-- 2. 重新做一次全量备份
BACKUP DATABASE FULL BACKUPSET 'db2_full_bct';
(1)插入耗时:
执行成功, 执行耗时1分 1秒 563毫秒. 执行号:1000
影响了28,000条记录
**(2)全量备份耗时与读写比:**查询 V$BACKUP_HISTORY。
(4)查看备份集大小:
[dmdba@localhost bak]$ du -sh *
3.9G db_test_full_01
2.1G db_test_inc_01
3.5G db_test_inc_02
3.8G db2_full_bct #重建表空间后的bct全量备份
-- 4.1.1插入数据
......
-- 第一次增量备份
BACKUP DATABASE INCREMENT BACKUPSET 'db2_inc_01_bct';
(1)插入耗时:
执行成功, 执行耗时32秒 92毫秒. 执行号:610 影响了16,500条记录 1条语句执行成功
**(2)第一次增量备份耗时与读写比:**查询 V$BACKUP_HISTORY。
TYPEINTEGER
实际读取(MB)BIGINT
5817
写入大小(MB)BIGINT
2657
读写比(倍数)DECIMAL 2
耗时(秒)INTEGER
14
(3)查看备份集大小:
[dmdba@localhost bak]$ du -sh *
3.9G db_test_full_01
2.1G db_test_inc_01
3.5G db_test_inc_02
# 重建表空间后的BCT
4.0G db2_full_bct
3.0G db2_inc_01_bct
-- 同样新建TSET_2表空间和TEST_2表
-- 详情见4.2.2
-- 执行第二次差异增量备份
BACKUP DATABASE INCREMENT BACKUPSET 'db_inc_02_bct';
(1)插入耗时:
执行成功, 执行耗时37秒 990毫秒. 执行号:619
影响了16,500条记录
1条语句执行成功
**(2)第二次增量备份耗时与读写比:**查询 V$BACKUP_HISTORY。
TYPEINTEGER
实际读取(MB)BIGINT
4780
写入大小(MB)BIGINT
2193
读写比(倍数)DECIMAL 2
耗时(秒)INTEGER
12
(3)查看备份集大小:
[dmdba@localhost bak]$ du -sh *
3.9G db_test_full_01
2.1G db_test_inc_01
3.5G db_test_inc_02
#bct
4.0G db2_full_bct
2.0G db2_inc_01_bct
2.7G db2_inc_02_bct
(5)V$BCT_BITMAP;
目的:验证开启 BCT(后台维护位图)是否会拖慢业务系统的写入性能。
| 动作描述 | 场景 1: 无 BCT (Sec 3) | 场景 2: BCT + 重建表空间 (Sec 5) | 结论 |
|---|---|---|---|
| 初始加载 (5G) | 1分 25秒 | 1分 01秒 | 开启 BCT 未见性能下降,反而因环境波动略有提升 |
| 增量一 (+3G) | 33.6 秒 | 32.1 秒 | 持平 |
| 增量二 (+3G) | 28.3 秒 | 38.0 秒 | 略有增加,但在正常波动范围内 |
结论:开启 BCT 理论确实会引入微小的写入开销(因为需要同步更新内存位图),但在本次测试的批量高并发插入场景下,数据插入性能并不明显。对于读多写少的生产系统,影响更小。
| 测试阶段 | 指标 | 场景 1: 无 BCT (全盘扫描) | 场景 2: BCT (精准扫描) | 分析: |
|---|---|---|---|---|
| 全量备份 | 实际读取 | 4027 MB | 4027 MB | 全盘扫描一致 |
| 耗时 | 18 秒 | 17 秒 | 正常环境波动 | |
| 增量一 | 实际读取 | 6371 MB | 5817 MB | 减少无效读取。(注: BCT 读取量略高可能包含日志归档扫描,但已低于全盘) |
| (+3G 原表) | 读写比 | 5.5 : 1 | 2.2 : 1 | 效率倍增。无 BCT 需读 6G 写 1G;BCT 读写比更合理。 |
| 耗时 | 22 秒 | 14 秒 | BCT增备速度更快 | |
| 增量二 | 实际读取 | 8715 MB | 3340 MB | **BCT读取效率:**场景 3 仅读取了新增的 3.3GB,完全跳过了之前的 8GB 老数据。 |
| (+3G 新表) | 读写比 | 3.5 : 1 | 2.2 : 1 | 非 BCT 模式随着数据增长,无效读取越来越多。 |
| 耗时 | 22 秒 | 12 秒 | 速度提升 45%。随着数据量增大,BCT 的时间优势将呈指数级扩大。 |
| 备份集名称 | 场景 1: 无 BCT | 场景 2: 开启 BCT (新版本) | Bug 修复验证结论 |
|---|---|---|---|
| 全量备份 | 3.9 GB | 3.8 GB | 完全正常。与无 BCT 基准一致。 |
| 增量一 | 2.1 GB | 3.0 GB | 略有增加。包含必要的 BCT 元数据及日志,属正常范围 |
| 增量二 | 3.5 GB | 2.7 GB | 更优 |
通过上述一系列严谨的对照测试与数据分析,我们对达梦数据库(DM)的 BCT(块改变跟踪)技术在增量备份场景下的表现有了深入的理解。以下是核心结论与应用建议。
基于本次测试数据(变化率约 30% 时,备份性能提升约 45%),我们可以推演 BCT 的适用场景。
为什么在全量备份前,如果有大库/表的删除(确定不需要),建议重建或Resize表空间?
**原因:**会导致全量备份集变大。
测试验证如下:
在之前的 BCT 增量备份测试中,个人发现在相同的初始化数据下,全量备份集的大小存在异常波动。
核心问题: 当执行 DROP TABLE 操作后,虽然 V$TABLESPACE 显示空间已释放,但在初始化3G数据后进行全量备份时,全量备份集大小却为8G
为了探究数据库在 DROP TABLE(逻辑清理)与 DROP TABLESPACE(物理重建)两种不同场景下的存储残留与备份行为,我设计了以下四组对照实验。
测试目标:
我们将测试环境分为四种状态,核心变量为 “是否开启 BCT” 和 “表空间清理方式”。
| 组别 | 场景名称 | 关键动作 (清理方式) | BCT 状态 | 预期探究点 |
|---|---|---|---|---|
| A组 | 无 BCT-基准组 | 重建表空间 (DROP TS) | 关闭 | 净环境:基准线,纯净数据的备份大小。 |
| B组 | 无 BCT-对照组 | 仅删表 (DROP TABLE) | 关闭 | 脏环境:验证物理备份是否会备份已释放的数据页。 |
| C组 | BCT-基准组 | 重建表空间 (DROP TS) | 开启 | 净环境:额外验证BCT 是否会影响全量备份 |
| D组 | BCT-实验组 | 仅删表 (DROP TABLE) | 开启 | 脏环境:额外验证 BCT 是否会影响全量备份 |
所有组别均执行完全一致的数据操作流程,仅在**步骤 2(清理阶段)**有所不同:
DROP TABLE TEST; DROP TABLE TEST_2;。
DROP TABLESPACE 并重新 CREATE。此时物理文件重置为初始大小。(1) 无BCT初始状态:
尚未开始测试,此时尚未创建TEST和TEST_2表空间,相当于重建表空间的情况。
全量备份集大小:3.9G db_test_full
(2) 无BCT不删除表空间:
进行增备测试后,drop TEST和TEST_2表,但是不删除表空间。
等待一段时间后,表空间才会更新已使用空间,通过V$TABLESPACE查看表空间使用情况
全量备份集大小:8.1G db2_test_full
(3) 有BCT重建表空间:
在增备测试后,drop表的同时,drop对应的TEST和TEST_2表空间,之后再重新创建。
全量备份集大小:4.0G db_test_full_bct #重建表空间后的bct全量备份
(4)有BCT不删除表空间:
在增备测试后,只单独drop TEST和TEST_2表。
全量备份集大小:9.5G db2_test_full_bct
| 组别 | 场景描述 | 备份集名称 | 备份集大小 | 结果分析 |
|---|---|---|---|---|
| A | 无 BCT + 初始纯净 | db_test_full |
3.9 GB | 基准值。仅备份了有效数据。 |
| B | 无 BCT + 只Drop表 | db2_test_full |
8.1 GB | 异常膨胀。虽然表没了,但备份集大小接近物理文件大小 (8GB)。 |
| C | 有 BCT + 重建表空间 | db_test_full_bct |
4.0 GB | 回归正常。与基准值(3.9G)几乎持平,证明 BCT 自身并没有导致全量备份集膨胀。 |
| D | 有 BCT + 只Drop表 | db2_test_full_bct |
9.5 GB | 异常膨胀。在 8.1GB 的基础上,叠加了 BCT 的元数据/日志开销。 |
本次测试数据揭示了达梦数据库物理备份的一个核心底层机制:物理备份(Backup Database)是基于“数据文件高水位线(HWM)”和“数据页格式化状态”进行的,而非基于“逻辑数据行”的有效性。
以下是对该现象背后三个关键技术机制的深度解析:
(1)物理文件与逻辑数据的解耦机制
DROP TABLE 操作时,数据库内核仅在数据字典和段头中更新元数据,将对应的数据页标记为“空闲”。
Truncate同样是逻辑删除,全备依旧会备份被标记位空的内容。.DBF 数据文件物理大小保持不变,且存储在磁盘块中的二进制数据并未被物理擦除。V$TABLESPACE 视图显示表空间使用率下降(逻辑空间已释放),但操作系统命令(如 ls -lh)显示文件大小依然维持在历史峰值(如 8GB),证实了物理占用并未减少。(2)DM备份的“智能抽取”判定机制:
DM的物理备份虽然具备“智能抽取”特性(即只备份有效数据页),但其对“有效页”的判定标准遵循物理层面的严谨性:
TRUNCATE会重置高水位线 (HWM),释放表占用的区(Extents),将高水位线重置回初始状态。但这并不涉及物理文件的高水位线 。TRUNCATE重置的是表级高水位线(Segment HWM),并没有重置文件级高水位线 (Datafile HWM)—— TEST.DBF 文件里,数据曾经写到的最远位置。Truncate并不能影响全量备份的数据大小。DROP TABLE 无法缩减物理备份的数据量。
Truncate,结果和 DROP TABLE 相同。(3) BCT 环境下的元数据开销分析
在实验组 D(开启 BCT + 脏环境)中,全量备份集进一步膨胀至 9.5 GB(比无 BCT 组多出 1.4 GB),其可能原因分析如下:
若需彻底缩减全量备份集大小,建议执行 数据文件收缩(Resize) 或 表空间重建的方式物理释放空间。仅依靠逻辑删除无法达到缩减物理灾备容量的目的。
文章
阅读量
获赞
