注册
DM增量备份机制与BCT优化:
专栏/技术分享/ 文章详情 /

DM增量备份机制与BCT优化:

悬铃木 2025/12/05 15 0 0
摘要

DM增量备份机制与BCT优化:

介绍:

随着企业数据量的爆炸式增长,如何在有限的维护窗口内完成高效的数据备份,成为DBA面临的严峻挑战。达梦数据库提供了基于 LSN 的增量备份机制,但在面对 TB 级大库时,传统的增量备份仍面临“全盘扫描”带来的 I/O 瓶颈。为此,DM 引入了 块改变跟踪(Block Change Tracking, BCT) 技术,旨在实现从“全量扫描”到“精准定位”的质变。

本文档将从底层原理出发,结合实战压测,全方位解析 DM 的备份还原机制与 BCT 技术的实际应用效果。

本文主要涵盖以下核心内容:

  1. DM备份还原底层机制:解析 DM 备份还原的底层机制,包括检查点(Checkpoint)、LSN 对齐、Redo 日志补偿以及差异增量备份的“链式依赖”原理。
  2. 实战对比:构建标准化的 5GB+ 数据模型,通过 “无 BCT(默认模式)”“开启 BCT” 的全流程对照测试,量化分析 BCT 对业务写入性能的影响及对备份 I/O 效率的提升。
  3. 异常探究:在测试过程中,我们捕捉到了一个反直觉的存储现象——逻辑清理(DROP/TRUNCATE)后的全量备份集巨大。本文将解析全量备份底层的“高水位线(HWM)”判定机制,探讨“逻辑删除”与“物理释放”在备份场景下的区别,并给出针对性的运维建议。

1. DM备份还原原理

1.1 联机热备问题:

在数据库联机运行(Online)状态下进行物理备份,本质上是对一个不断变化的系统进行快照。这一过程面临两个核心矛盾:

  1. 脏页滞后性:数据库的高性能依赖于内存缓冲区(Buffer Pool)。大量最新的数据修改停留在内存中的“脏页”上,尚未刷入磁盘。
  2. 时间切片的不一致性:备份是一个持续的过程(可能持续数小时)。备份开始时读取的数据页 A,与备份结束时读取的数据页 B,处于不同的时间点(LSN)。

达梦数据库(DM)通过 “磁盘快照 + 增量日志补偿” 的机制解决了上述问题。本文将深入存储引擎底层,解析这一机制的实现原理

1.2 备份机制

1.2.1 数据页的捕获机制

与逻辑导出不同,DM 的物理备份(DMRMAN/SQL Backup)并不通过 SQL 接口读取数据,也不直接锁定内存中的 Buffer Pool。

  • I/O 源头:物理备份进程直接对磁盘上的**数据文件(.DBF)**进行扫描读取。
  • 内存脏页的处理:备份进程忽略内存中尚未刷盘的脏页。这意味着,备份集中的数据页是“陈旧”的。例如,某数据页在内存中已被修改(LSN=200),但磁盘上仍是旧版本(LSN=100),备份集将记录 LSN=100 的旧数据。

1.2.2 模糊备份的形成

由于备份过程跨越了一定的时间窗口,备份集内的物理数据页在逻辑时间上是乱序的:

  • T1 时刻(备份开始):系统记录当前的检查点 LSN,记为 BEGIN_LSN
  • T2 时刻:备份进程读取了 数据页 P1(LSN=1000)。
  • T3 时刻:业务事务提交,修改了 数据页 P2,将其 LSN 推至 1500,并触发 Checkpoint 刷盘。
  • T4 时刻(备份结束):备份进程读取了 数据页 P2(LSN=1500)。系统记录此时的 LSN,记为 END_LSN

结果:备份集中,P1 停留在 T2 时刻,P2 停留在 T4 时刻。整个数据集合处于“不一致”状态,如果直接使用这些数据启动数据库,将导致逻辑错误。

1.2.3 REDO 日志

为了修复上述不一致,DM 在备份数据页的同时,必须捕获备份期间产生的所有变更。

  • 捕获范围:从 BEGIN_LSNEND_LSN 之间的所有联机 REDO 日志。
  • 存储方式:这部分日志被提取并封装在备份集(BackupSet)中,通常作为独立的日志备份片存储。它们是后续恢复操作的“脚本”。

1.3 还原与恢复阶段

恢复过程分为两个严格的阶段:**还原(Restore)**负责物理还原,**恢复(Recover)**负责逻辑追平。

1.3.1 物理还原 (Restore)

  • 动作:将备份集中的数据页写回目标磁盘。
  • 状态:此时磁盘上的数据文件完全复刻了备份时的“模糊”状态。数据页的时间戳(Page LSN)参差不齐,数据库处于不可启动状态。

1.3.2 逻辑恢复

这是核心中的核心。DM 引擎启动重演进程,读取备份集中的 REDO 日志,按 LSN 顺序执行。

在此过程中,系统通过**幂等性检查 (Idempotency Check)**判断某条日志是否应该应用到某个数据页上。

假设系统正在重演一条日志记录:

  • Log_LSN:该日志记录对应的序列号。
  • Page_LSN:磁盘上目标数据页头部记录的序列号。

系统执行严格的 LSN 大小比对逻辑

情况 A:Log_LSN > Page_LSN (需要更新)

  • 场景分析:备份进程读取该页时,该页处于较旧的状态。随后业务产生了修改(即当前的 Log),但备份没来得及抓取到修改后的页,或者抓取的是修改前的版本。
  • 系统动作应用 (Redo)。系统执行日志中的操作(如修改 Tuple 内容),并将数据页的 Page_LSN 更新为 Log_LSN

情况 B:Log_LSN <= Page_LSN (无需更新)

  • 场景分析:备份进程在读取该页时,该页已经是一个“较新”的版本(可能是在备份的后期读取的)。这意味着该页在备份时已经包含了这条日志所描述的修改。
  • 系统动作跳过 (Skip)。避免重复修改导致数据损坏。

1.3.3 最终一致性状态

当重演进程执行完所有 BEGIN_LSNEND_LSN 之间的日志后:

  1. 所有“旧页”都被推进到了 END_LSN 的状态。
  2. 所有“新页”保持不变(已经是 END_LSN 或接近该状态)。
  3. 结果:所有数据页在逻辑上对齐到了 END_LSN 这一时刻。

此时,DM 执行最后一步操作:更新 DB_MAGIC。这标志着数据库已经从“不一致的备份集”转化为“一致的活动库”,可以正常对外提供服务。

1.4 总结

达梦数据库的物理备份还原机制,并未试图在备份时刻暂停数据库DML活动,而是采用了“宽容捕获,严谨修正”的策略:

  1. 备份时:允许数据页处于时间上的混乱状态(只读磁盘,忽略内存脏页),但完整记录这段时间的操作流水(Redo Log)
  2. 恢复时:利用数据页头部的 LSN 作为版本标识,通过 日志重演 将所有数据页精准地推演到备份结束的那一刻。

这种机制最大程度地减少了备份对业务系统的锁阻塞,实现了高性能的在线热备。

2. DM 差异增量备份与还原机制

在理解了全量备份的基础上,我们需要深入探讨在生产环境中最常用的策略:差异增量备份 (Differential Incremental Backup)

这种策略旨在减少存储占用,背后的使用的机制是“链式依赖”。

2.1 差异增量备份

差异增量备份的核心逻辑是:只备份自“基备份(Base Backup)”以来发生过修改的数据页。

2.1.1 基备份的定义

差异增量备份必须依赖一个已存在的备份集作为“基准”。

  • 基备份的选择:在差异增量模式下,基备份既可以是完全备份,也可以是上一次的增量备份
  • 多级增量:这就形成了一个层层递进的关系。例如:增备2 基于 增备1增备1 基于 全备0

2.1.2 变化数据的捕获机制

在不开启优化技术的情况下,RMAN 是如何知道哪些数据页发生了变化?答案是**“暴力比对”**。

  1. 确定时间锚点: 系统首先读取基备份的元数据,获取其 BEGIN_LSN(基备份开始时的日志序列号)。
  2. 全文件扫描 (Full Scan): 备份进程必须从头到尾读取源数据库的所有数据文件(.DBF)。
  3. LSN 阈值过滤: 系统检查每一个读取到的数据页头部(Page Header)的 LSN。
    • 判定规则:如果 Page_LSN > 基备份_BEGIN_LSN,说明该页在基备份之后被修改过。
    • 动作:将该页拷贝到当前的增量备份集中。
    • 忽略:如果 Page_LSN <= 基备份_BEGIN_LSN,说明该页自上次备份后未变动,直接丢弃,不写入备份集。

结果:虽然备份集只包含变化的 1% 数据,但为了找到这 1%,系统不得不读取 100% 的物理文件。


2.2 差异增量还原

增量还原不能单独进行,必须依赖完整的备份链条。

2.2.1 还原过程 (Restore)

假设我们有备份链:全备 A -> 增备 B -> 增备 C。现在要还原到 增备 C 的状态。

  1. 基石重置: 系统首先使用 全备 A 进行还原,在磁盘上重建数据文件,恢复所有基础数据页。此时数据处于 T1 时间点。
  2. 链式回放(隐含的完全还原): 系统必须搜集完整的备份集链表,并严格按照时间顺序(从前到后)处理。
    • 读取 增备 B:将 B 中的新数据页,覆盖磁盘上对应的旧页。
    • 读取 增备 C:将 C 中的新数据页,再次覆盖对应的位置。
  3. 物理终态: 经过多次覆盖后,磁盘上的物理文件已经拥有了所有最新的数据页(截止到 C 备份结束时)。

2.2.2 恢复过程 (Recover)

物理还原完成后,数据页虽然是新的,但在时间上可能是不一致的(Fuzzy)。

  • 日志重演:系统利用备份集 C 中的 Redo 日志(或外部归档),执行标准的 RECOVER 操作,将所有数据推进到一致性状态。
  • 注意:中间备份集(如 A 和 B)中的日志通常不需要重演,因为它们对应的物理修改已经被后续的增量备份(C)中的数据页直接覆盖了。

2.3 性能与维护弊端

虽然差异增量备份节省了存储空间,但存在两个显著的问题:

  1. 备份性能瓶颈(无效 I/O)
    • 由于缺乏“变化追踪”机制,每次增量备份——哪怕只修改了 1 个数据页——都必须全盘扫描整个数据库的所有文件来寻找这 1 个页。
    • 后果:对于 TB 级大库,增量备份的读 I/O 开销与全量备份完全一样,导致备份窗口极长,严重占用生产资源。
  2. 还原链路风险(链条脆弱性)
    • 差异增量还原依赖完整的 基备份 + 增备1 + ... + 增备N 链条。
    • 后果:链条越长,还原所需的时间越久(需要多次物理覆盖)。更致命的是,一旦中间任何一个增量备份集损坏或丢失,整个后续链条全部失效,导致数据无法恢复到最新状态。

在第3节: 为了验证上述理论,我们将构建一个 5GB 的测试环境。演示在没有进行优化的情况下,即使只插入几条数据,增量备份的 I/O 读取量耗时 依然会惊人地高

——这将为我们在第4节提到BCT 技术提供支持。

2.4 相关问题解答:

(1)数据脏页在内存中尚未刷盘,DM备份如何保存最新数据?

  • 通过保存备份期间发生的REDO日志,对比进行重演,将数据库恢复到最新状态

(2)为什么有时候增备特别慢,甚至比全备还慢?

  • 根据上述关于增量备份原理的讲解,可知增量备份的读 I/O 开销与全量备份相差不大,都需要扫描整个数据库的数据文件。但是一般情况下,增量备份写I/O开销会比全量备份少很多,因此增备一般比全备速度更快。
  • 例外情况:当数据库大部分数据文件都被修改,增量备份需要大量对修改的数据页进行备份,此时写I/O开销和全量备份接近。同时,因为增备在读I/O时需要额外比对LSN,因此读I/O开销实际超过全备。综合下来,增备反而有可能比全备还慢。

3. 普通差异增量备份

3.1 环境准备与全量备份

此时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;
  • image20251121135130266.png

**(3)全量备份耗时与读写比:**查询 V$BACKUP_HISTORY

  • image20251121135302021.png

  • -- 备份完成后,执行此语句,截图记录 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]$

3.2 模拟增量变化

3.2.1 第一次增量:3G(原表空间)

-- 增量:在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

  • image20251121140116545.png

(3)查看备份集大小:

  • [dmdba@localhost bak]$ du -sh * 3.9G db_test_full_01 2.1G db_test_inc_01

3.2.2 第二次增量:3G(新表空间)

-- 第二次增量:在新的表空间进行增量 -- 动作:创建一个名为 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

  • image20251121142903068.png

(3)查看备份集大小:

  • [dmdba@localhost bak]$ du -sh * 3.9G db_test_full_01 2.1G db_test_inc_01 3.5G db_test_inc_02

3.3 数据统计:

表 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)。系统把老数据又读了一遍。

现象记录:

  • 备份耗时:较长(因为需要扫描整个 5GB+ 的文件)。
  • 读写比AVG_READ 很高(全盘读),AVG_WRITE 很低(只写了 100MB)。

4. BCT (块改变跟踪)

面对“全盘扫描”的低效,达梦引入了 BCT 技术。

4.1 BCT 的定义与原理

  • 定义Block Change Tracking。它是一种在数据库运行期间,实时跟踪并记录数据页物理修改的技术。
  • 原理
    • 写入端(捕获):当事务修改数据导致“脏页”产生时,后台线程会同步在内存的 BCT Buffer 中,将对应的位图(Bitmap)标记为 1。
    • 存储端(落盘):这些位图信息会异步写入磁盘上的 .bct 文件中。这些文件采用环形复用机制,循环记录最新的变化。
    • 读取端(备份):RMAN 不再扫描数据文件,而是直接读取 .bct 文件,获取“脏页列表”,然后精准跳转到对应位置抓取数据。
  • 开启:INI参数,可动态修改
    • SP_SET_PARA_VALUE(1, 'ENABLE_BCT', 1);
      • 注:此参数较新,尚未更新在官网DBA手册中
    • 取值
      • 0:关闭(默认)。增量备份需要全盘扫描。
      • 1:开启。数据库会在后台维护位图(Bitmap),记录哪些数据页发生了修改。
  • 查看:
    • 查看值:SELECT * FROM V$PARAMETER WHERE NAME = 'ENABLE_BCT';
    • 查看位图:SELECT * FROM V$BCT_BITMAP;

4.2 BCT 的核心优势

  1. IO 模型的质变:从 Sequential Read (全量顺序读) 变为 Random Read (精准随机读)。
  2. 时间缩短:备份时间不再取决于数据库总大小,而是取决于增量变化的大小。对于 10TB 级数据库,性能提升可达 50 倍以上。

4.3 BCT增备测试方法

在第5节将开启BCT后,重复第3节的差异备份测试。

主要测试两个点:

  • 一是批量插入速度是否有所影响
  • 二是备份速度改变

5. BCT差异增量备份

5.1 初始化与全量备份

和第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;

表空间已被释放:

  • 截图时间在本次初始化数据后,所以已使用3G
  • 相比第4节:总大小从6G变为5G。(6G是第4节测试时导致的自动扩充)
  • image20251121160008883.png
--1.重建前面的表空间和TEST表并插入数据 -- 表空间与表局具体SQL创建见3.1; -- 2. 重新做一次全量备份 BACKUP DATABASE FULL BACKUPSET 'db2_full_bct';

(1)插入耗时:

  • 执行成功, 执行耗时1分 1秒 563毫秒. 执行号:1000
    影响了28,000条记录
    

**(2)全量备份耗时与读写比:**查询 V$BACKUP_HISTORY

  • image20251121155332432.png

(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全量备份

5.2 模拟同样的增量变化

5.2.1 第一次增量

-- 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

  • image20251125093244443.png

(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

5.2.2 第二次增量

-- 同样新建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

  • image20251125093554099.png

(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;

  • image20251125093626039.png

6.综合数据对比

表 1:数据插入性能对比 (SQL 执行耗时)

目的:验证开启 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 理论确实会引入微小的写入开销(因为需要同步更新内存位图),但在本次测试的批量高并发插入场景下,数据插入性能并不明显。对于读多写少的生产系统,影响更小。

表 2:备份 IO 效率与读写比对比

测试阶段 指标 场景 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 的时间优势将呈指数级扩大。

表 3:备份集空间占用对比

备份集名称 场景 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 更优

7. 总结:

通过上述一系列严谨的对照测试与数据分析,我们对达梦数据库(DM)的 BCT(块改变跟踪)技术在增量备份场景下的表现有了深入的理解。以下是核心结论与应用建议。

7.1 核心结论:BCT 实现增量备份的性能优化

  1. IO 模型的根本性优化: BCT 成功将增量备份的 IO 读取模式从传统的 “全盘扫描” 转变为 “精准定位”。在测试中,面对仅有 3GB 数据变更的场景,开启 BCT 后系统成功跳过了未修改的 8GB 存量数据,实现了 “只读有效数据” 的理想状态。
  2. 读写比回归健康状态: 非 BCT 模式下,随着数据库总容量的增长,增量备份的无效读取呈线性上升趋势,读写比严重失衡(最高达 5.5:1)。而开启 BCT 后,增量备份的读写比稳定维持在 2:1 左右,极大地节省了宝贵的 IO 资源。
  3. 对业务性能影响微乎其微: 尽管 BCT 需要在后台维护位图信息,但在高并发数据插入场景下的实测表明,其对业务写入性能的影响在 误差范围内,对于生产环境通常是完全可接受的。

7.2 场景适用性分析

基于本次测试数据(变化率约 30% 时,备份性能提升约 45%),我们可以推演 BCT 的适用场景。

  • 收益来源:减少对未修改数据页的读取(Read Skip)。数据量越大,性能收益越高。
  • 成本来源
    • 联机日志写入时的位图维护(极小,测试中表现不明显,但是理论上会有,如果在CPU资源紧张的情况下,影响可能会比较明显,毕竟有计算成本)
    • 高并发下的位图锁争用:位图是全局共享的内存结构。为了防止数据错乱,数据库必须对位图加。每一个事务在修改数据页之前,都需要去更新内存中的 BCT 位图,这就可能出现锁争用,导致 CPU 使用率瞬间飙升。
    • BCT初次备份时额外的元数据开销(3G,对比普通增量备份的2.1G)
  • 决策建议
    • 推荐开启
      • 大型 OLTP 系统(库容量 > 500GB,日增量变化 < 10%)。此时 BCT 带来的 I/O 节省将是数量级的(从小时级缩短为分钟级)。
      • 中型标准库:CPU资源不紧张情况下,推荐开启。
      • 增量追加型的 OLAP 系统。
    • 不推荐开启
      • 全量清洗的 ETL 中间库、当绝大部分数据都发生变化时,位图扫描优势消失。且批量加载(Bulk Load)可能引发位图争用。
      • CPU 负载长期处于红线(>80%)的数据库系统中需谨慎开启。

7.3 运维注意事项

为什么在全量备份前,如果有大库/表的删除(确定不需要),建议重建或Resize表空间?

**原因:**会导致全量备份集变大。

测试验证如下:

7.3.1 测试背景与目的

在之前的 BCT 增量备份测试中,个人发现在相同的初始化数据下,全量备份集的大小存在异常波动。

核心问题: 当执行 DROP TABLE 操作后,虽然 V$TABLESPACE 显示空间已释放,但在初始化3G数据后进行全量备份时,全量备份集大小却为8G

为了探究数据库在 DROP TABLE(逻辑清理)与 DROP TABLESPACE(物理重建)两种不同场景下的存储残留与备份行为,我设计了以下四组对照实验。

测试目标

  1. 验证 “逻辑删除”“物理重建” 对全量备份集大小的差异化影响。
  2. 揭示达梦物理备份(Backup Database)底层的数据页抽取机制

我们将测试环境分为四种状态,核心变量为 “是否开启 BCT”“表空间清理方式”

组别 场景名称 关键动作 (清理方式) BCT 状态 预期探究点
A组 无 BCT-基准组 重建表空间 (DROP TS) 关闭 净环境:基准线,纯净数据的备份大小。
B组 无 BCT-对照组 仅删表 (DROP TABLE) 关闭 脏环境:验证物理备份是否会备份已释放的数据页。
C组 BCT-基准组 重建表空间 (DROP TS) 开启 净环境:额外验证BCT 是否会影响全量备份
D组 BCT-实验组 仅删表 (DROP TABLE) 开启 脏环境:额外验证 BCT 是否会影响全量备份

7.3.2标准化测试流程

所有组别均执行完全一致的数据操作流程,仅在**步骤 2(清理阶段)**有所不同:

  1. 脏环境构造
    • 初始化 TEST 表空间,插入 3G 基础数据。
    • 追加插入 2G 业务数据(此时表空间物理文件自动扩容至约 5~6GB)。
    • 再次插入 2G 基础数据。
    • 合计约 8GB 的物理占用。
  2. 清理环境(变量控制点)
    • 脏环境组(B/D):执行 DROP TABLE TEST; DROP TABLE TEST_2;
      • 注:此时从 SQL 逻辑层面看,数据库是空的;但从操作系统层面看,数据文件依然有 8GB。
    • 净环境组(A/C):执行 DROP TABLESPACE 并重新 CREATE。此时物理文件重置为初始大小。
  3. 初始化数据与全量备份
    • 初始化 TEST 表空间,插入 3G 基础数据。
    • 执行全量备份
    • 预测:合理的全量备份集大小应为3~4G
  4. 执行观测
    • 记录备份集大小、耗时及 IO 数据。7

7.3.3测试过程:

(1) 无BCT初始状态:

尚未开始测试,此时尚未创建TEST和TEST_2表空间,相当于重建表空间的情况。

全量备份集大小:3.9G db_test_full

(2) 无BCT不删除表空间:

进行增备测试后,drop TEST和TEST_2表,但是不删除表空间。

等待一段时间后,表空间才会更新已使用空间,通过V$TABLESPACE查看表空间使用情况

  • 发现表空间可使用大小已恢复。扩容后的总大小显示为6G(自动扩容)。

全量备份集大小: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

7.3.4 测试数据汇总

组别 场景描述 备份集名称 备份集大小 结果分析
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 的元数据/日志开销。

7.3.5 根因分析

本次测试数据揭示了达梦数据库物理备份的一个核心底层机制:物理备份(Backup Database)是基于“数据文件高水位线(HWM)”和“数据页格式化状态”进行的,而非基于“逻辑数据行”的有效性。

以下是对该现象背后三个关键技术机制的深度解析:

(1)物理文件与逻辑数据的解耦机制

  • 逻辑删除的本质:执行 DROP TABLE 操作时,数据库内核仅在数据字典段头中更新元数据,将对应的数据页标记为“空闲”。
    • 补充:Truncate同样是逻辑删除,全备依旧会备份被标记位空的内容。
  • 此时数据的物理存储状态:该操作并不会触发操作系统层面的文件截断(Truncate),磁盘上的 .DBF 数据文件物理大小保持不变,且存储在磁盘块中的二进制数据并未被物理擦除。
  • 现象验证:虽然查询 V$TABLESPACE 视图显示表空间使用率下降(逻辑空间已释放),但操作系统命令(如 ls -lh)显示文件大小依然维持在历史峰值(如 8GB),证实了物理占用并未减少。

(2)DM备份的“智能抽取”判定机制:

DM的物理备份虽然具备“智能抽取”特性(即只备份有效数据页),但其对“有效页”的判定标准遵循物理层面的严谨性:

  • 未初始化页:指位于文件尾部、从未被写入过的页面。备份程序会自动跳过此类页面(解释为何基准测试中的全量备份仅为 3.9GB,远小于预分配文件5G的大小)。
  • 备份逻辑:为了确保还原后物理文件结构的完整性,物理备份程序必须扫描并备份 “高水位线(HWM) ”以下的所有“已格式化页”,即便这些页面在逻辑上是空闲的。
  • 已格式化页:指位于**高水位线(HWM)**之下、曾经被写入过数据,当前即使被标记为 Free 的页面。
    • 补充说明:在DM中,TRUNCATE会重置高水位线 (HWM),释放表占用的区(Extents),将高水位线重置回初始状态。但这并不涉及物理文件的高水位线 。
    • 严格来说,TRUNCATE重置的是表级高水位线(Segment HWM),并没有重置文件级高水位线 (Datafile HWM)—— TEST.DBF 文件里,数据曾经写到的最远位置。
    • 全量备份进程在读取 TEST.DBF 时,扫描的截止点是 文件级 HWM。 因此Truncate并不能影响全量备份的数据大小。
  • 现象验证:实验组 B(无 BCT + 脏环境)的备份集大小为 8.1 GB。这一数值与此前插入数据撑大的物理文件总大小(TEST 5GB + TEST_2 3GB)完全吻合,证实了 DROP TABLE 无法缩减物理备份的数据量。
    • 后续补充测试Truncate,结果和 DROP TABLE 相同。

(3) BCT 环境下的元数据开销分析

在实验组 D(开启 BCT + 脏环境)中,全量备份集进一步膨胀至 9.5 GB(比无 BCT 组多出 1.4 GB),其可能原因分析如下:

  • 全量扫描机制:执行全量备份时,无论 BCT 是否开启,系统均需进行全盘扫描。BCT 的“精准定位”优势仅在增量备份中生效。
  • 元数据与日志开销:在开启 BCT 的环境下,数据库需维护块改变跟踪文件。面对大规模的逻辑删除与空间重用,系统可能产生了额外的 BCT 元数据记录或 Redo 日志开销,导致最终备份集大小在物理数据量的基础上出现了额外增长。

7.3.6 运维建议:

若需彻底缩减全量备份集大小,建议执行 数据文件收缩(Resize)表空间重建的方式物理释放空间。仅依靠逻辑删除无法达到缩减物理灾备容量的目的。

  • 后续补充测试:Truncate 表并Resize表空间。结果:全量备份集恢复正常大小(3.4G)
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服