本文记录一次达梦数据库 Redo 文件损坏修复实验。这里说的“修复”,不是把原来的 .log 文件修好后继续使用,而是在联机 Redo 已经丢失或损坏、又没有备份归档可以正常恢复时,尝试让故障库临时打开,尽快把还能读取的数据抢救出来。
需要先明确:这种无备份、无归档条件下的应急处理,无法保证全部数据恢复。故障发生附近,已经提交但尚未写入数据文件、仅存在于原 Redo 中的修改可能丢失。
先把结论放在前面:
有备份和归档
→ 优先走正规恢复
没有备份归档,Redo 又不可用
→ 才考虑替换 Redo 临时启动
数据库能启动
→ 不等于修复完成
→ 目标应是导出数据、重建新库、导入校验
本文实验只适用于测试环境。生产环境必须长期保留可用的物理备份和归档日志,并定期验证备份可恢复性。生产库遇到 Redo 损坏时,应先保护故障现场,优先评估备份、归档、主备副本和厂商支持,不要直接照搬替换 Redo、修改文件头或调整抢救参数。
Redo 是数据库故障恢复的重要依据。执行 DDL、DML 时,数据库会生成 Redo 记录,保存数据库物理更改信息。数据页不一定在事务提交时立刻全部写入数据文件,但恢复所需的 Redo 必须被保护下来。
这条链路可以简化成:
用户修改数据
↓
内存中的数据页发生变化
↓
生成 Redo
↓
Redo 写入联机日志
↓
数据页随后通过检查点等机制落盘
异常停库后,只要数据文件、控制文件、联机 Redo 都还完整,数据库重启时就可以从检查点附近读取 Redo,重做尚未完整落盘的修改,再处理未提交事务。
Redo 丢失或损坏时,问题就变了:
数据库不是“不知道怎么启动”
而是“正常恢复所需的依据坏了”
这时所谓“替换 Redo”,本质不是恢复原来的 Redo 历史。新的 Redo 是从新实例里拿来的,它没有原库故障前那段真实修改记录。替换的目标只是让数据库获得一组结构合法、身份匹配的联机日志文件,尝试跨过启动阶段。
因此,替换 Redo 更像一次数据抢救:
放弃已经不可用的原 Redo 历史,尝试接受当前数据文件状态,争取一次导出数据的机会。
Redo 损坏时,首先要判断有没有正规恢复条件。
如果有备份和归档,优先选择:
还原备份
↓
应用归档日志
↓
恢复到故障前尽可能新的时间点
这条路线的目标是恢复一个一致、可以继续使用的数据库。生产环境应把可用备份和连续归档作为前提,而不是把替换 Redo 当成常规恢复手段。
如果没有备份、没有归档,也没有可用副本,才进入应急抢救。Redo 不可用时,故障库本身已经无法正常提供业务读写;临时拉起后也不应恢复应用接入或继续承载生产交易。这个阶段的主线应当是:
临时打开故障库
↓
确认核心对象是否还能读取
↓
尽快导出能读取的数据
↓
初始化全新数据库
↓
导入并校验
这里不是“把原库恢复到故障前的完整状态”。原 Redo 中尚未落盘的修改已经无法通过新 Redo 重演,因此故障附近的数据可能丢失,最终能够抢救出的数据范围要以实际查询、导出和业务校验结果为准。
因此,启动日志中看到:
SYSTEM IS READY
只能说明数据库进程打开了。它不代表所有事务都完整,也不代表所有表、索引和空间结构都健康。
联机 Redo 文件不是普通文本日志,也不是只要文件名存在就可以。
例如直接执行:
touch DAMENG01.log
touch DAMENG02.log
这种文件没有 Redo 内部结构,没有日志控制信息,也没有数据库身份信息,数据库不会把它当成合法联机日志。
比较稳妥的应急思路是:
查看原库初始化参数
↓
使用一致的关键参数初始化新实例
↓
启动并正常停止新实例
↓
从新实例取得结构合法的 Redo
↓
读取原库 SYSTEM.DBF 的身份信息
↓
修改新 Redo 的 db_magic 和 pemnt_magic
↓
替换故障库对应 Redo
↓
尝试启动故障库
这条流程有两个关键点。
第一,新实例不是为了替代原库,而是为了生成一组结构正常的 Redo 文件。初始化参数要尽量和原库一致,尤其是页大小、簇大小、大小写敏感、字符集、日志大小等关键参数。
第二,新 Redo 原本属于新实例。要让原库识别它,需要把其中的 db_magic 和 pemnt_magic 调整为原库的值。原库身份信息可以从 SYSTEM.DBF 读取:
dmmdf TYPE=1 FILE=/dmdata/DAMENG/SYSTEM.DBF
再修改新 Redo:
dmmdf TYPE=2 FILE=/dmdata/DAMENG/DAMENG01.log
dmmdf TYPE=2 FILE=/dmdata/DAMENG/DAMENG02.log
db_magic 和 pemnt_magic 可以理解为数据库文件之间的身份标识。数据文件属于原库,而新生成的 Redo 属于新库,如果身份不一致,启动时就可能报 magic 不匹配。
实际处理时不能机械理解成“必须替换两个日志”。前台启动和实例日志通常会指出 DAMENG01.log 或 DAMENG02.log 中哪个文件不存在、读取失败或校验异常;如果只有一个 Redo 文件有问题,正常只处理该文件,保留另一个完好的 Redo。本文实验同时移走并处理两个文件,只是为了完整演示流程。完成处理后,仍然是使用放回原故障库目录、已经调整过身份信息的 Redo,前台启动原故障库进行验证。
本次实验涉及的工具可以先这样理解:
| 工具 | 作用 | 本文中的定位 |
|---|---|---|
dminit |
初始化一个新实例 | 用相同关键参数生成结构合法的新 Redo |
dmserver |
启动数据库实例 | 用前台启动观察真实报错和 SYSTEM IS READY |
dmmdf |
查看或修改达梦数据文件、Redo 文件的部分文件头属性 | 读取 SYSTEM.DBF 的 db_magic、pemnt_magic,再写入新 Redo |
dmhcr |
部分环境或案例中可能出现的辅助工具 | 本次实验未使用,本文不把它写成必需步骤;如果现场版本提供该工具,应以对应版本说明和厂商建议为准 |
dmdbchk |
检查数据库物理结构 | 数据抢救后可用于辅助校验,但不能替代逻辑导出和业务校验 |
其中 dmmdf 是本次实验最关键的工具。它不是普通文本编辑器,而是按文件类型读取和修改达梦文件头信息。本文只使用它处理 db_magic、pemnt_magic 这两个身份字段,不把 clsn、next_seq 等日志位置字段作为初学实验的修改项。
实验放在单机测试库中进行,目标是模拟“联机 Redo 丢失后,通过新实例 Redo 临时拉起故障库”。主备、DSC、ASM、控制文件损坏、Undo 损坏等场景暂时不混入,先把 Redo 替换这一条线看清楚。
实验数据库信息如下:
端口:15236
原实例目录:/dmdata/DAMENG
备份目录:/dmdata/bak_DAMENG
新实例目录:/dmdata/NEW_DAMENG/DAMENG
测试用户:TEST
测试表:TEST.T_REDO
实验主线如下:
先准备一个可恢复的测试现场
↓
正常停库并备份数据目录
↓
移走联机 Redo,模拟 Redo 丢失
↓
确认原库无法启动
↓
新建同参数实例,取得合法 Redo
↓
修改新 Redo 的 magic 信息
↓
替换原库 Redo 并尝试启动
↓
启动后只做查询、导出、迁移验证
下面进入实操。
CREATE USER TEST IDENTIFIED BY "Test@123456";
CREATE TABLE TEST.T_REDO
(
ID INT,
NAME VARCHAR(50)
);
INSERT INTO TEST.T_REDO VALUES (1, 'REDO_TEST_1');
INSERT INTO TEST.T_REDO VALUES (2, 'REDO_TEST_2');
COMMIT;
2. 正常停止测试库,完整复制数据目录,并单独备份原 Redo 文件。
/dmdbms/bin/DmServiceDMSERVER stop
cp -a /dmdata/DAMENG /dmdata/bak_DAMENG
cp /dmdata/DAMENG/DAMENG01.log /dmdata/DAMENG/DAMENG01.log.bak
cp /dmdata/DAMENG/DAMENG02.log /dmdata/DAMENG/DAMENG02.log.bak
这一步必须先停库再复制。/dmdata/bak_DAMENG 是完整现场备份,单独备份 .log 文件是为了实验结束后还能还原 Redo。
3. 移走联机 Redo,模拟 Redo 丢失。
本实验为了展示完整流程,同时移走了两个 Redo。真实故障中应先根据前台启动信息和实例日志判断具体是 DAMENG01.log 还是 DAMENG02.log 有问题;如果只损坏一个,就只替换对应文件。
cd /dmdata/DAMENG
mv DAMENG01.log DAMENG01.log.lost
mv DAMENG02.log DAMENG02.log.lost
4. 启动原库,保存启动报错和实例日志,用来确认故障现象确实来自联机 Redo 不可用。
5. 查看原实例的 dminit*.log,按关键初始化参数创建新实例。
需要记录的内容包括:
PAGE_SIZE
EXTENT_SIZE
CASE_SENSITIVE
CHARSET 或 UNICODE_FLAG
LOG_SIZE
DB_NAME
INSTANCE_NAME
PORT_NUM
如果 dminit*.log 中字段显示的是描述性名称,例如 page size: 8192,需要换算成 dminit 参数:8192 字节对应 PAGE_SIZE=8,32768 字节对应 PAGE_SIZE=32。
随后在新目录初始化一个临时实例,用来生成结构合法的 Redo。示例:
mkdir -p /dmdata/NEW_DAMENG
/dmdbms/bin/dminit PATH=/dmdata/NEW_DAMENG PORT_NUM=15237 PAGE_SIZE=32 EXTENT_SIZE=32 LOG_SIZE=2048 CHARSET=0 CASE_SENSITIVE=Y SYSDBA_PWD=Dameng@1234 SYSAUDITOR_PWD=Dameng@1234
这些参数要按原库实际情况填写,PORT_NUM 可以使用新端口,避免和原实例冲突。新实例初始化完成后,启动一次再正常关闭:
./dmserver /dmdata/NEW_DAMENG/DAMENG/dm.ini
看到 SYSTEM IS READY 后,正常停止新实例。这样可以取得一组由达梦正常生成并处理过的 Redo 文件,后面再复制到故障库目录中处理 magic 信息。
6. 从原库 SYSTEM.DBF 读取 db_magic、pemnt_magic,再用 dmmdf 修改新 Redo 中对应的 magic 信息。
先读取原库 SYSTEM.DBF。这里读取的是故障库自己的系统数据文件,不是新实例的 SYSTEM.DBF:
cd /dmdbms/bin
./dmmdf TYPE=1 FILE=/dmdata/DAMENG/SYSTEM.DBF
进入 dmmdf 后,重点记录这两个值:
db_magic=502961763
pemnt_magic=1019282663
如果输出里还能看到 enable_page_check,也一并记下来,后面排查 magic 不匹配或页校验问题时可能用到。这里只读取原库身份信息,不修改 SYSTEM.DBF。记录完成后输入:
q
然后把新实例生成的 Redo 复制到原库目录。当前新实例目录是 /dmdata/NEW_DAMENG/DAMENG,原实例目录是 /dmdata/DAMENG:
cp /dmdata/NEW_DAMENG/DAMENG/DAMENG01.log /dmdata/DAMENG/DAMENG01.log
cp /dmdata/NEW_DAMENG/DAMENG/DAMENG02.log /dmdata/DAMENG/DAMENG02.log
接着修改复制回来的 Redo。先处理第一个:
./dmmdf TYPE=2 FILE=/dmdata/DAMENG/DAMENG01.log
进入交互界面后,按菜单编号修改:
输入 6
将 db_magic 修改为原库 SYSTEM.DBF 中记录的 db_magic
按提示输入 y
保存修改
输入 12
将 pemnt_magic 修改为原库 SYSTEM.DBF 中记录的 pemnt_magic
按提示输入 y
保存修改
第二个 Redo 同样处理:
./dmmdf TYPE=2 FILE=/dmdata/DAMENG/DAMENG02.log
同样修改 6 db_magic 和 12 pemnt_magic,保存退出。
/dmdbms/bin/dmserver /dmdata/DAMENG/dm.ini
第一次使用修改过 magic 的新 Redo 前台启动原故障库后,数据库已经不再报 DAMENG01.log not exist,也没有停在 magic 不匹配上,说明文件存在性和身份检查已经通过。
前台最终显示:
非法指令 (核心已转储)
这只是进程异常停止时的外部现象,不能单独作为根因。应继续回到实例日志,查找它之前最靠近故障时间的 ERROR 或 FATAL 信息。本次实例日志中的关键报错是:
rlog4_adjust_clsn_off_4k rpkg decode failed
rlog4_adjust_clsn_off_4k failed
code = -4602, dm_sys_halt now!!!
这说明数据库已经能够读取新 Redo 的文件头,但在继续解析或调整 Redo 日志包位置时失败。db_magic、pemnt_magic 只解决“新 Redo 是否属于原库”的身份问题,不能把原库故障前真实的日志内容、日志位置和日志包进度恢复出来。
达梦官方 FAQ 中也有“前台启动报非法指令(核心已转储)”的案例。该案例真正的实例日志是 Redo log try flush over space,官方解释为日志环被冲破,并针对这一特定错误给出了 RLOG_CHECK_SPACE=0 的处理建议。由此可以看出,同样的前台“非法指令”可能对应不同的实例日志根因,不能只按前台一句话套用参数;本次仍应以实际出现的 rlog4_adjust_clsn_off_4k 和 -4602 为分析依据。
这个结果把替换 Redo 的边界暴露得很清楚:
文件存在
≠ Redo 内容可用于原库恢复
magic 匹配
≠ 数据库一定能启动
基础替换流程完成
≠ 抢救一定成功
因此,第一次失败并不表示前面的步骤毫无作用,而是说明基础替换只完成了第一段:让 Redo 文件结构和身份信息通过检查。继续启动失败时,必须回到实例日志,判断后续是否涉及日志包解析、日志空间检查、页校验或事务恢复阶段。
PSEG_RECV=0 不应该放在 Redo 替换流程最前面。Redo 文件不存在、损坏或身份不匹配时,数据库连基本的 Redo 检查都无法通过,这时修改 PSEG_RECV 不能把 Redo 修好。
只有当 Redo 文件已经放回原库、magic 等基础检查通过,数据库启动流程继续向后推进时,事务恢复参数才可能开始产生影响。
先区分两个参数:
PSEG_RECV=0
→ 跳过活动事务回滚和已提交事务 PURGE
→ 只影响后续事务恢复阶段
→ 不修复 Redo,也不恢复丢失数据
RLOG_CHECK_SPACE
→ 与 Redo 日志空间检查有关
→ 应结合具体实例日志、数据库版本和官方说明使用
PSEG_RECV=0 风险很高。数据库异常关闭时,未提交事务正常应被回滚;跳过回滚后,事务可能只留下部分修改,破坏原子性。因此它只用于无正规恢复条件时争取临时打开和导出数据的机会,不能作为长期运行配置。
对于 RLOG_CHECK_SPACE,达梦官方 FAQ 在 Redo log try flush over space、日志环被冲破的案例中明确给出的处理值是:
RLOG_CHECK_SPACE=0
官方对该值的说明是:日志刷盘时不检查日志空间是否溢出。这个说明对应的是“日志环被冲破”这一特定报错。
本次实验参考社区案例,实际使用的是:
RLOG_CHECK_SPACE=2
并在当前测试版本中成功启动。但公开官方 FAQ 没有给出值 2 的通用释义,因此本文不把 RLOG_CHECK_SPACE=2 写成所有 Redo 损坏场景的标准处理,也不把官方对值 0 的解释直接套到值 2 上。生产环境应以对应版本参数说明、实际实例日志和厂商建议为准。
本文涉及的主要字段和参数可以这样区分:
| 名称 | 所在位置 | 作用 |
|---|---|---|
db_magic |
SYSTEM.DBF、Redo 文件头 |
数据库文件身份标识,新 Redo 要改成原库值 |
pemnt_magic |
SYSTEM.DBF、Redo 文件头 |
数据库文件身份标识,通常和 db_magic 一起核对 |
enable_page_check |
SYSTEM.DBF,部分版本 Redo 文件头也可能出现 |
页校验相关字段;本次 dmmdf TYPE=2 未显示可修改项,所以没有处理 |
PSEG_RECV=0 |
dm.ini |
跳过活动事务回滚和已提交事务 PURGE,是高风险抢救参数 |
RLOG_CHECK_SPACE=0 |
dm.ini |
官方 FAQ 在“日志环被冲破”案例中的处理值,含义为日志刷盘时不检查日志空间是否溢出 |
RLOG_CHECK_SPACE=2 |
dm.ini |
本次社区案例和测试环境采用的值;公开 FAQ 未给出通用释义,不应泛化 |
clsn、clsn_off、next_seq |
Redo 文件头 | 与日志位置、日志包解析有关,本文只观察不修改 |
第一轮失败后,没有继续修改 Redo 头里的 clsn、next_seq 这类位置字段。它们和日志包位置直接相关,改错以后很难判断新的错误来自哪里。
结合社区案例,本次在 dm.ini 文件末尾追加两个参数。同一个参数如果在文件中出现多次,读取时以最后一次配置为准,因此直接在末尾追加即可,避免前面已有同名配置影响本次实验:
PSEG_RECV = 0
RLOG_CHECK_SPACE = 2
这两个参数的定位并不相同:
PSEG_RECV=0
→ Redo 基础检查通过后,跳过活动事务回滚和已提交事务 PURGE
→ 不负责修复缺失或损坏的 Redo
RLOG_CHECK_SPACE=2
→ 本次参考社区案例采用的实验值
→ 当前测试版本能够识别,并在本次现场中继续推进启动
→ 不代表所有版本和所有 Redo 故障都应设置为 2
需要再次区分:官方 FAQ 针对 Redo log try flush over space 给出的值是 RLOG_CHECK_SPACE=0,其含义为日志刷盘时不检查日志空间是否溢出;本次使用的值 2 来自社区案例和实际测试,公开 FAQ 没有给出值 2 的通用解释。
至于 enable_page_check,本次没有修改。虽然 dmmdf TYPE=1 查看 SYSTEM.DBF 时能看到:
enable_page_check=3
但当前版本执行:
./dmmdf TYPE=2 FILE=/dmdata/DAMENG/DAMENG01.log
Redo 文件头输出中没有 enable_page_check 字段,可修改列表里也没有对应编号:
You can only reset sta(4) or db_magic (6) or clsn (9) or clsn_fil(10)
or clsn_off(11) or pemnt_magic(12) or fil_id(13) or next_seq(15)
or g_next_seq(16) or p_db_magic(22) or n_apply_ep(23).
所以这一步不强行处理。第二次实验相对第一次,实际变化只有两处:
Redo 文件:
仍然只修改 db_magic、pemnt_magic
dm.ini:
新增 PSEG_RECV=0
新增 RLOG_CHECK_SPACE=2
再次前台启动:
/dmdbms/bin/dmserver /dmdata/DAMENG/dm.ini
这次实例成功进入:
SYSTEM IS READY
说明第一轮的失败点并不是 magic 不匹配。只改 Redo 身份信息时,数据库仍会在后续 Redo 检查或恢复阶段失败;本次测试环境在文件末尾补充 PSEG_RECV=0 和 RLOG_CHECK_SPACE=2 后,实例获得了临时打开的机会。这个结果只能描述本次版本和本次现场,不能直接推广成通用固定组合。
但这仍然不能理解成“数据库已经修好”。本次实验的结论应当写得更克制:
替换 Redo + 修改 magic
→ 通过 Redo 文件身份检查
再配合 PSEG_RECV=0、RLOG_CHECK_SPACE=2
→ 本次测试库可以临时启动
临时启动成功
→ 只用于查询、导出、迁移和校验
启动成功后,应立即验证测试表:
SELECT * FROM TEST.T_REDO;
如果测试数据可读,下一步就是逻辑导出、重建新库、导入校验。走到这一步,这次 Redo 抢救实验才算闭环。
另外,PSEG_RECV=0 和 RLOG_CHECK_SPACE=2 是为了抢救数据临时加入的参数,不适合作为正常运行配置长期保留。完成导出迁移后,应以新库校验结果为准,而不是继续扩大故障库的使用范围。
Redo 文件损坏修复最容易误解的地方,是把“启动成功”当成“数据库修好了”。
更稳妥的认识是:
db_magic、pemnt_magic 匹配只是第一层检查通过,不代表 Redo 包一定能解析;DAMENG01.log 或 DAMENG02.log,单个文件有问题时只处理对应文件;PSEG_RECV=0 不是修复 Redo,而是在 Redo 基础检查通过后高风险跳过事务恢复步骤;RLOG_CHECK_SPACE=0 的解释适用于“日志环被冲破”案例;本次使用的值 2 只记录为当前测试现象,不作通用参数解释;enable_page_check 是否需要处理取决于版本和 dmmdf 输出,本次没有修改;Redo 修复的目标不是让两个 .log 文件看起来正常,而是尽可能把数据从不完整的故障现场里带出来。能启动,只是拿到了抢救窗口;能迁移并校验,才是这类应急处理真正结束。
RLOG_CHECK_SPACE=0 的用途。RLOG_CHECK_SPACE=2、enable_page_check 等方式尝试启动的过程。文章
阅读量
获赞
