尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

达梦数据库中的脏页与脏数据:从 WAL 到检查点的完整链路

达梦数据库中的脏页与脏数据:从 WAL 到检查点的完整链路
📅 发布时间:2026/8/2 10:38:48

探究 数据页从干净 → 变脏 → 再变干净 这一生命周期。

  1. 在达梦里有两层含义:

维度 脏页(Dirty Page) 脏数据 / 脏读(Dirty Data / Dirty Read)
层面 物理/缓冲层 事务/隔离层
定义 内存中的数据页已被修改,且与磁盘上对应页不一致 事务修改了数据但尚未提交;若其他事务读到这些未提交修改,即脏读
是否合法 正常运行时大量存在,是性能设计的一部分 是否允许取决于隔离级别(读未提交允许,读已提交及以上禁止)
落盘关系 由检查点 / 缓冲淘汰 / 关闭等机制刷回磁盘 与「数据页是否已刷盘」无直接对应:未提交事务也可能产生脏页

脏页回答的是:内存和磁盘是否一致
脏数据(脏读)回答的是:别的事务能不能看见我还没提交的修改?
两者可以同时存在,但绝不能互相替代。

  1. 一页数据的完整生命周期
    ┌─────────┐ DML 修改 ┌─────────┐ Checkpoint / 淘汰 ┌─────────┐
    │ 干净页 │ ──────────► │ 脏页 │ ──────────────────► │ 干净页 │
    │ (Clean) │ │ (Dirty) │ │ (Clean) │
    └─────────┘ └────┬────┘ └─────────┘
    │
    │ 同时写入 REDO 缓冲区
    ▼
    ┌─────────┐ 刷盘条件满足
    │ REDO缓冲 │ ──────────────────► REDO 文件落盘
    └─────────┘
    关键约束永远是:

日志先落盘,数据页后落盘(WAL, Write-Ahead Logging)

即便是未提交事务产生的脏页,在被刷入数据文件之前,系统也会强制检查并补刷相关 REDO,保证「页上已有的修改,日志里一定能重做出来」。

  1. 达梦缓冲池:脏页住在哪条链上?
    DM Server 启动时按 dm.ini 中的缓冲参数向 OS 申请连续内存,按页格式化后挂入管理链。数据缓冲区内有三条核心链表:

自由链(Free):尚未使用的内存页
LRU 链:已被使用的页(含干净页与脏页),按最近使用顺序排列
脏链(Dirty):已被修改、尚未写回磁盘的页
当自由链耗尽时,从 LRU 链尾淘汰最近较少使用的页;若被淘汰的是脏页,必须先(在满足 WAL 前提下)写回磁盘,再腾出缓冲槽位。

缓冲池类型(可通过 V$BUFFERPOOL 观察):

-- 查看各类型缓冲池及脏页数量SELECTNAME,PAGE_SIZE,N_PAGES,FREE,N_DIRTY,N_CLEAR,ROUND(RAT_HIT*100,2)ASHIT_RATIO_PCT,N_PHY_WRITEFROMV$BUFFERPOOLORDERBYN_DIRTYDESC;

常见类型:NORMAL(主缓冲)、KEEP(尽量常驻)、RECYCLE(临时表空间)、FAST、ROLL(回滚相关)等。日常脏页压力主要看 NORMAL 池的 N_DIRTY。

  1. WAL:为什么「COMMIT 不等于数据页落盘」
    4.1 COMMIT 真正保证的是什么
    达梦与大多数 RDBMS 一致:

COMMIT 的核心作用:确保该事务的 REDO(含提交标记)已落盘,从而保证持久性(Durability)。
COMMIT 不保证:该事务修改过的数据页已经写入 .dbf 数据文件。
因此会出现一种完全正常的状态:

事务 T1: UPDATE → 脏页在缓冲 → REDO 已落盘 → COMMIT 成功
此时:磁盘上的数据页可能仍是旧值;崩溃后靠 REDO 重做即可恢复到已提交状态。
4.2 日志刷盘的触发条件不止 COMMIT
COMMIT 是日志刷盘的重要触发点,但不是唯一触发点。常见还包括:

触发场景 说明
日志缓冲区满 / 达阈值 缓冲写满前必须刷出,否则无法继续写日志
后台日志 FLUSH 线程定期工作 dm_redolog_thd 负责合并缓冲并顺序写日志
检查点(Checkpoint) 刷脏页前必须先保证相关 REDO 已落盘
脏页即将被淘汰写盘 WAL 强制「先日志后数据」

伪代码刻画刷盘顺序:

procedure FlushDirtyPage(page): // 永远不允许:数据页已在磁盘上更新,而对应 REDO 还在内存ifpage.redo_lsn>rlog.file_lsnthenFlushRedoUpTo(page.redo_lsn)-- 先把日志补到至少覆盖该页修改 endifWriteDataPageToDisk(page)page.dirty :=false// 对应 REDO 之后可被检查点推进而回收 end procedure procedure Commit(trx): WriteCommitRecord(trx)-- 提交标记进入日志缓冲 EnsureRedoFlushed(trx.last_lsn)-- 若尚未落盘,同步刷一次 // 注意:此处不强制刷脏页returnSUCCESS end procedure

事务最终 COMMIT 时,系统会检查该事务相关 REDO 是否已在磁盘:

已在盘上:提交几乎瞬时完成,主要是写提交标记/确认;
尚未在盘上:此刻触发一次同步刷日志,确保落盘后再返回成功。

  1. 线程协作:谁写日志、谁写脏页
    结合达梦实例线程模型,可以把责任拆开:

工作线程 (Worker)
│ 修改缓冲页,生成 REDO 到日志缓冲
▼
日志 FLUSH 线程 (dm_redolog_thd)
│ 合并日志缓冲 → 顺序写 REDO 文件
│ (若配置实时归档,刷盘前可先发往备库)
▼
调度线程 (dm_sched_thd) ← 每秒轮询
│ 检查 CKPT_INTERVAL / 脏页数 / 日志量 → 触发检查点
▼
检查点线程 / 任务
│ 决定刷多少脏页(FLUSH_RATE / FLUSH_PAGES)
▼
I/O 线程 (dm_io_thd)
│ 真正把数据页写入磁盘
▼
数据文件 (.dbf)

要点:
日志顺序写,通常比数据页随机写更高效;
日志线程与 I/O 线程分离,避免互相阻塞;
脏页刷盘前,必须保证对应 REDO 已由 FLUSH 线程落盘。

  1. 检查点:让脏页「重新变干净」,并推进日志环
    6.1 在线 REDO 是一个环
    达梦在线 REDO 默认两个物理文件,可手工增加或扩容,系统不会自动扩展。可把它想象成一个环:

    ┌────── free space ──────┐

    TAIL ●────────────────────────● HEAD
    │ 有效 REDO(尚可能用于恢复)│
    └────────────────────────┘
    HEAD:新日志写入点
    TAIL:有效日志起点(检查点推进后右移)
    HEAD 与 TAIL 之间的空隙是可复用的空闲空间
    检查点的本质工作:

把缓冲中的部分或全部脏页写入数据文件;
这些页对应的 REDO 不再需要保留;
推进 TAIL(推进检查点),释放日志空间供循环复用。
副作用:需要重做的日志更少 → 故障恢复时间缩短。代价是额外的数据页 I/O。

6.2 触发检查点的条件
以下条件可同时生效,满足任一即可触发(参数为 0 表示关闭该路触发):

# dm.ini 片段(示例默认值,以实际版本为准)CKPT_INTERVAL=300# 定时触发,单位秒;0=关闭CKPT_RLOG_SIZE=100# 已占用 REDO 达到该值(MB)则触发;0=关闭CKPT_DIRTY_PAGES=10000# 缓冲脏页数超过该值则触发;0=关闭CKPT_FLUSH_RATE=5.00# 每次刷「待刷相关」的约 5%CKPT_FLUSH_PAGES=1000# 每次至少刷这么多页CKPT_WAIT_PAGES=128# 单批串行写入页数上限(分批推进 LSN)

另外还有「保底」路径:即便上述条件未满足,只要 REDO 可用空间不安全(与 RLOG_SAFE_SPACE 等相关),系统也会主动触发检查点,甚至进入日志预留/等待状态,避免日志环被写爆。

手工触发:

-- 触发一次刷盘比例约 20% 的检查点SELECTCHECKPOINT(20)FROMDUAL;-- 控制台启动时也可使用 CKPT 命令(视启动方式而定)

6.3 COMMIT vs Checkpoint 对照

-- 概念对照(注释说明,非可执行断言)-- COMMIT:-- 保证:本事务 REDO(含提交标记)落盘-- 不保证:脏数据页写入 .dbf---- CHECKPOINT:-- 保证:按策略刷脏页 + 推进检查点 LSN / 日志 TAIL-- 同时:刷脏页前仍会遵守 WAL,必要时先刷 REDO

操作 REDO 落盘 脏页落盘 主要目的
COMMIT 是(该事务相关) 否(不强制) 事务持久性
Checkpoint 视需要补刷 是(按比例/页数) 回收日志、缩短恢复、腾缓冲

  1. 用 SQL「看见」脏页与日志进度
    7.1 缓冲脏页与命中率
SELECTNAME,N_DIRTY,N_PAGES,FREE,ROUND(N_DIRTY*100.0/NULLIF(N_PAGES,0),2)ASDIRTY_PCT,ROUND(RAT_HIT*100,2)ASHIT_RATIO_PCT,N_PHY_READS,N_PHY_WRITEFROMV$BUFFERPOOLWHERENAMEIN('NORMAL','KEEP','RECYCLE','FAST','ROLL')ORDERBYN_DIRTYDESC;

经验观察(非绝对阈值):

N_DIRTY 长期接近池容量,且伴随检查点频繁、写 I/O 飙高 → 考虑加大 BUFFER、优化大批量 DML 批次,或审视 CKPT_* 是否过激/过缓;
命中率持续偏低 → 优先查 SQL 与缓冲大小,而非一味加快刷脏。

7.2 在线 REDO 空间与刷盘进度

-- 日志空间概览SELECTCAST(SYSDATEASVARCHAR(20))ASCHECK_TIME,ROUND(TOTAL_SPACE/1024/1024,2)ASTOTAL_MB,ROUND(FREE_SPACE/1024/1024,2)ASFREE_MB,ROUND((TOTAL_SPACE-FREE_SPACE)/1024/1024,2)ASUSED_MBFROMV$RLOG;-- 更细的 LSN / 刷盘状态(字段以实际版本为准)SELECTCUR_LSN,FILE_LSN,FLUSHING_PAGES,ROUND(TOTAL_SPACE/1024/1024,2)ASTOTAL_MB,ROUND(FREE_SPACE/1024/1024,2)ASFREE_MBFROMV$RLOG;-- 日志文件清单SELECTCLIENT_PATHASLOG_NAME,PATH,ROUND(RLOG_SIZE/1024/1024,2)ASSIZE_MB,CREATE_TIMEFROMV$RLOGFILE;

关注点:
FREE_MB 持续偏低 → 检查点跟不上写入,或在线日志总容量偏小;
CUR_LSN 与 FILE_LSN 差距拉大 → 日志刷盘压力大,COMMIT 可能变慢。

7.3 检查点相关参数一览

SELECTPARA_NAME,PARA_VALUEFROMV$DM_INIWHEREPARA_NAMEIN('CKPT_INTERVAL','CKPT_RLOG_SIZE','CKPT_DIRTY_PAGES','CKPT_FLUSH_RATE','CKPT_FLUSH_PAGES','CKPT_WAIT_PAGES','RLOG_SAFE_SPACE','RLOG_BUF_SIZE','RLOG_POOL_SIZE','BUFFER')ORDERBYPARA_NAME;

部分环境也可对照检查点历史视图(若版本支持):

-- 若存在该视图,可观察检查点频率与耗时趋势SELECT*FROMV$CKPT_HISTORYORDERBY1DESC;
  1. 事务层的「脏」:脏读与隔离级别
    物理脏页与逻辑脏读要分开做实验认知。

8.1 演示:读未提交下的脏读
会话 A:

-- 会话 ACREATETABLET_DIRTY_DEMO(IDINTPRIMARYKEY,VALVARCHAR(32));INSERTINTOT_DIRTY_DEMOVALUES(1,'INIT');COMMIT;SETTRANSACTIONISOLATIONLEVELREADUNCOMMITTED;UPDATET_DIRTY_DEMOSETVAL='DIRTY_UNCOMMITTED'WHEREID=1;-- 注意:此处故意不 COMMIT```会话 B(同为READUNCOMMITTED):```sql -- 会话 B SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT ID, VAL FROM T_DIRTY_DEMO WHERE ID = 1; -- 可能读到 'DIRTY_UNCOMMITTED' —— 这就是脏读 -- 若会话 A 随后 ROLLBACK,会话 B 刚才读到的值在业务上是「从未真正存在」的```默认隔离级别(通常为读已提交)下,会话 B 不应看到 A 未提交的修改;但 A 修改产生的脏页仍然可能存在于缓冲池中,只是对其他会话不可见(靠锁/MVCC/回滚段读一致性视图等机制实现)。8.2对照实验:COMMIT后脏页是否立刻消失?```sql -- 会话:观察 COMMIT 前后脏页数变化(示意) SELECT SUM(N_DIRTY) AS DIRTY_BEFORE FROM V$BUFFERPOOL; UPDATE T_DIRTY_DEMO SET VAL = 'AFTER_COMMIT_TEST' WHERE ID = 1; COMMIT; SELECT SUM(N_DIRTY) AS DIRTY_AFTER_COMMIT FROM V$BUFFERPOOL; -- 通常 DIRTY_AFTER_COMMIT 不会因 COMMIT 而骤降到 0 -- 说明:提交保证的是日志,不是立刻把所有相关脏页刷盘 SELECT CHECKPOINT(100) FROM DUAL; -- 加大刷盘力度 SELECT SUM(N_DIRTY) AS DIRTY_AFTER_CKPT FROM V$BUFFERPOOL; -- 检查点后,脏页数一般会明显下降(不一定为 0,取决于刷盘比例与并发写入)```这组对比能把笔记里那句「COMMIT确保日志落盘,但不是日志刷盘的唯一条件,也不是脏页刷盘条件」钉死在实验事实上。9.故障恢复视角:脏页为什么「敢于」滞后落盘 假设时间线: t1Checkpoint完成,ckpt_lsn=L1,此前脏页已落盘 t2 事务 T 修改页 P,REDO 写入并在COMMIT时落盘,LSN=L2 t3 页 P 仍是脏页,尚未写入数据文件 t4 实例崩溃 恢复时: 从最近检查点(约 L1)之后的 REDO 开始重做; 重做包含 T 的修改与提交记录; 页 P 在内存重建后写回,数据库回到崩溃前已提交状态。 若 t3 之前没有把 REDO 落到盘上就刷了页 P,崩溃后可能出现「磁盘上有新页、却无法用日志证明/回滚」的不一致——这正是 WAL 禁止的情况。 因此恢复公式可以记为: 持久性=REDO 已落盘(相对COMMIT) 一致性窗口缩短=检查点推进(减少需重做的日志量) 可恢复性=WAL 顺序(永远日志先于数据页)10.调优与运维建议(面向生产)10.1参数取舍 目标 倾向 风险 缩短宕机恢复时间 更积极的检查点(更小INTERVAL/更小 RLOG_SIZE/更敏感 DIRTY_PAGES) 写 I/O 升高,高峰期抖动 压榨峰值吞吐 略放宽检查点,保证在线日志容量充足 恢复时间变长;日志空间吃紧时仍会被迫猛刷 避免日志等待 扩大在线 REDO、合理 RLOG_SAFE_SPACE、保证检查点跟得上写入 磁盘占用增加 经验原则: 先保证日志环不「卡死」,再谈吞吐; CKPT_FLUSH_RATE 与 CKPT_FLUSH_PAGES 控制的是「每次刷多少」,过小会「检查点很勤但清不干净」,过大则单次 I/O 风暴; OLTP 高峰与大批量装载场景,参数往往需要两套策略,而不是一套吃遍天下。10.2巡检脚本骨架```sql-- 一页纸巡检:缓冲 + 日志 + 检查点参数SELECT'BUFFER_DIRTY'ASITEM,NAME||': dirty='||N_DIRTY||', hit='||ROUND(RAT_HIT*100,2)||'%'ASINFOFROMV$BUFFERPOOLWHEREN_DIRTY>0UNIONALLSELECT'RLOG_SPACE','used_mb='||ROUND((TOTAL_SPACE-FREE_SPACE)/1024/1024,2)||', free_mb='||ROUND(FREE_SPACE/1024/1024,2)FROMV$RLOGUNIONALLSELECT'CKPT_PARA',PARA_NAME||'='||PARA_VALUEFROMV$DM_INIWHEREPARA_NAMELIKE'CKPT_%'ORDERBY1,2;

同时建议结合实例日志中检查点刷盘记录,判断业务写压力节奏,作为调整 BUFFER 与 CKPT_* 的依据。

  1. 回到最初那张周期图
    把全文收束成一条因果链:

DML
└─► 缓冲页变脏(进入脏链)
└─► 生成 REDO(进日志缓冲)
│
├─► 多种条件触发 REDO 刷盘(缓冲满 / 后台线程 / Checkpoint / COMMIT 等)
│
└─► COMMIT:确保本事务 REDO 落盘(脏页仍可滞后)
│
▼
Checkpoint / 淘汰 / 关闭
│ (刷页前再次确认 WAL)
▼
脏页写回数据文件 → 页变干净 → 推进日志 TAIL

再记三句不易错的结论:
COMMIT ≠ 数据页落盘;COMMIT ≈ 「该事务的 REDO 已安全」。
永远是日志先落盘,数据页后落盘;未提交事务的脏页也不例外。
脏页是性能机制,脏读是隔离问题;运维盯 N_DIRTY 与 REDO 空间,开发盯隔离级别与事务边界。

相关新闻

  • 2026 年郎溪有实力的不锈钢水箱生产厂家源头厂家选哪家,这款帮企业省了三成运维成本的“储水神器”,到底是谁家的? - 行业推荐官[官方】--
  • VMware虚拟机连接手机热点上网的三种解决方案与原理详解
  • LRCGET 终极指南:批量歌词下载与音乐歌词同步完整解决方案

最新新闻

  • 2026沈阳闲置旧金变现实录:易奢福从验金到打款仅用10分钟 - 易奢福
  • 走A机制深度解析:从游戏底层原理到实战收益验证
  • Altium Designer钢网文件生成全攻略:从原理到实战
  • 2026 丽水办公家具企业采购好口碑发布:六家服务商适配生态产业园、竹木文创采购需求 - 东科家具
  • WinForm自定义控件开发:构建带状态指示灯的按钮控件
  • 工业编码RSBL85-12解读与元器件替代实战指南

日新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

周新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号