做数据库运维和开发这些年,我见过太多人把国内数据库当成“带中文手册的Oracle”或“国产MySQL”来用,出了问题就本能地往连接配置、权限上猜。其实不管哪家关系型数据库,只要把检查点、redo/undo、MVCC、内存缓存刷盘这条底层链路想清楚了,很多故障不用猜都能直接看到根因。今天我就拿达梦数据库(DM)来把这个话题掰开揉碎讲一遍,内容偏底层,但保证每一节最后都能落到你实际能操作的参数和命令上。如果你是刚接触达梦,或者已经在生产环境里跑了一段时间但总觉得心里没底,这篇值得你静下心看完。
1. 达梦数据库的整体架构:先认清这几个核心机制在哪一层
1.1 一句话理解达梦的体系结构
达梦的总体结构和主流关系型数据库非常接近:一个实例(Instance)管理一组内存结构和后台进程/线程,数据最终落在数据文件(DBF)里;实例启动时通过控制文件(dm.ctl)找到并加载数据库;数据修改先写内存缓冲区,再由后台机制落盘。这个“先改内存、再刷磁盘”的模型,是所有事务型数据库能保持高性能的基础。
这里要特别强调一个容易混乱的点:达梦里“数据库”和“实例”是两个不同概念。实例对应着dm.ini里配置的INSTANCE_NAME,而数据库对应一组物理文件。很多新手用disql登录后看到select name from v$database和select instance_name from v$instance结果是不同信息,就以为出问题了,其实完全正常。这套结构本质上和 Oracle 的 instance/database 分离模型同源,理解了这一点,后面谈检查点、redo、undo 时就不会绕晕。
1.2 为什么这四个机制必须放在一起理解
一句话总结它们的关系:内存缓存是“舞台”,redo 是“保险丝”,undo 是“后悔药”,检查点是“收银员”。
- 所有事务性修改先进内存缓冲区,这是性能的关键;
- redo 日志在事务提交时把修改记录落盘,用来保证崩溃后已经提交的事务不丢;
- undo 记录保存修改前的镜像,用来支持回滚和一致性读;
- 检查点负责在合适时机把内存中的脏页真正刷到数据文件,避免日志无限增长、缩短崩溃恢复时间。
实际运维中,你会遇到的所有诡异现象——数据库突然连不上、重启后恢复很长时间、磁盘空间被日志塞满、查询看到不一致的数据——基本都能从这条链路里找到答案。所以这篇文章不按功能手册的模式罗列,而是按“数据从内存到磁盘”的完整流转过程讲。
2. 检查点机制:内存脏页刷盘的核心枢纽
2.1 检查点到底在做什么
检查点的本质,是把数据缓冲区里的脏页(内容比数据文件新、尚未写回磁盘的页)刷到数据文件,同时更新控制文件中记录的检查点位置。为什么要做这件事?因为数据库不能每次提交事务都立刻把每个数据页写盘,那样性能会慢到没法用。但也不能无限期不写盘,否则:
- 联机日志会持续膨胀,因为恢复时需要从检查点位置重放日志,检查点越靠后,需要保留的日志越多;
- 崩溃恢复时间会无限拉长,实例崩溃后数据库要从检查点位置开始读日志做前滚,检查点太旧意味着要重放所有日志;
- 数据文件本身长时间不更新,页的“年龄”差异过大,刷盘压力会集中爆发。
打个比方:检查点像一个“阶段性结账”机制。你不能每卖一瓶水就把账本和现金对一遍,那太慢了;但也不能一直不收银,否则账本越堆越厚,哪天机器一断电,要重新点验的流水就多到崩溃。检查点的间隔,就是在“性能”和“恢复成本”之间找一个平衡。
2.2 达梦的检查点分类与触发条件
达梦里的检查点可以分成几类,很多老运维也只关注“自动还是手动”,其实类别分清楚对定位问题很有帮助:
- 完全检查点(Normal Checkpoint):把缓冲区里所有脏页都刷盘,通常在正常关闭库、执行
CHECKPOINT命令、在线日志切换达到一定条件时触发。 - 增量检查点(Incremental Checkpoint):不一次性刷所有脏页,而是按LRU顺序、分批、持续地刷。达梦会周期性把脏页按缓冲区内脏页的先后顺序写出。这能避免刷盘波峰,对生产环境更友好。
- 强制检查点(Forced Checkpoint):当运行日志切换或某些需要数据文件与日志严格一致的场景(比如备份、某些DDL),系统强制把所有脏页刷出。
- 手动检查点:DBA执行
CHECKPOINT;主动触发。
触发条件上,达梦主要看两个阈值:
- 日志量阈值:当 redo 日志量达到某个比例或页数,系统自动发起检查点;
- 时间阈值:后台进程周期性检查,到时间就刷一批脏页。
这里特别说一下:别把“检查点”理解成“一次把所有脏页刷完”。增量检查点机制下,一个检查点动作可能持续几个周期,每次只刷一批页。生产环境中如果观察到 I/O 有规律的小波纹,而不是定时大波峰,往往说明检查点刷盘策略在正常起作用。
2.3 检查点相关的关键参数配置
达梦的检查点参数主要配置在dm.ini中,以下是生产环境里最常调整的几个:
| 参数名 | 默认值 | 作用说明 | 调整建议 |
|---|---|---|---|
CHECKPOINT_EVERY_PAGES | 300 | 每产生多少页在线日志后触发检查点 | 值越小检查点越频繁,故障恢复越快,但刷盘压力越大;值越大性能更好,但日志积压多 |
CKPT_RLOG_PERCENT | 16 | 联机日志空间使用达到该百分比时触发检查点 | 一般保持默认,日志紧张时适当调低 |
CKPT_FLUSH_INTERVAL | 3 | 后台检查点线程刷脏页的时间间隔(秒) | 高并发写入场景可调小到1~2秒,减少每次刷盘量 |
CKPT_FLUSH_PAGES | 100 | 每个刷盘周期最多刷出的脏页数 | 磁盘I/O能力强的环境可以调大 |
实操中说三个经验:
第一,别盲目调大CHECKPOINT_EVERY_PAGES。有人觉得检查点刷盘影响性能,就把这个值调到几千,结果日志文件暴涨,实例崩溃后恢复要几分钟甚至几十分钟。生产环境我一般保持默认或微调,优先观察日志切换频率。
第二,刷盘线程间隔CKPT_FLUSH_INTERVAL不是越小越好。它控制的是检查点线程主动刷脏页的周期,如果设成0,表示完全靠日志触发,刷盘会比较集中,容易在日志量达到阈值时出现I/O尖峰。保持默认3秒,让脏页分批匀速落盘,通常更平滑。
第三,用CHECKPOINT;手动刷盘前要评估业务影响。手动检查点会把大量脏页一次性写出,如果脏页量大,瞬间I/O压力会非常夸张。我曾经在一个核心交易系统上执行手动检查点,结果磁盘利用率直接冲到95%,业务SQL全部变慢。所以手动检查点适合在业务低峰或停机维护时用。
3. Redo日志:崩溃恢复的底层保障
3.1 redo日志记录了什么
redo 日志(达梦里也叫联机日志、Redo Log)记录的是数据页的物理修改信息——也就是说,它不记录“张三把余额从100改成200”这种业务语义,而是记录“数据文件第X号文件的第Y号页,偏移Z处的数据被改成了什么”。这种物理日志的优势是恢复时不需要重新执行SQL,直接把对应页的修改重放一遍即可,速度极快。
达梦的联机日志采用循环写方式:日志文件至少两个,当前组写满后切换到下一组,切回第一组时如果检查点还没把对应位置的数据刷盘,数据库会先触发一次检查点,确保被覆盖的日志对应的脏页已经落盘。这个设计非常关键——它保证了 redo 日志可以被安全覆盖,不会因为循环覆盖导致恢复链路断裂。
理解这一点后,你就明白一个常见问题了:为什么日志文件不能随便减少或删除。如果你手动删了联机日志文件,或者把日志参数改小导致覆盖过快,一旦实例崩溃,重放日志时发现需要的日志已经被覆盖,数据库轻则恢复失败,重则损坏页。达梦会尽量避免这种情况,但如果物理日志文件缺失,后果不会比Oracle好到哪里去。
3.2 redo日志与检查点如何配合
redo 和检查点的配合逻辑是:检查点位置决定了日志重放的起点。
- 事务提交时,redo 必须落盘(至少写到日志文件中),否则提交不生效;
- 检查点负责在合适时机,把日志对应的脏页刷到数据文件,同时把检查点位置往前推进;
- 崩溃恢复时,从控制文件记录的检查点位置开始,读取 redo 日志并重放,把数据文件恢复到崩溃前的状态。
所以整个链路里,redo 是记录的“源头”,检查点是记录的“终点标记”。联机日志空间的循环复用,必须以检查点位置为界:日志写指针可以追着检查点位置跑,但永远不能覆盖检查点位置之前的日志,因为那段日志还有可能在恢复时用到。
在实际运维中,你可以通过查询来确认这个“安全边界”:
-- 查看当前日志情况(视版本不同,视图名可能有差异) SELECT * FROM V$RLOG; -- 查看检查点信息 SELECT * FROM V$CKPT;如果你发现日志使用率长期卡在某个百分比附近降不下来,大概率是检查点刷盘速度跟不上日志产生速度。这时候要排查的就不是日志问题,而是脏页刷不下去的原因——比如磁盘I/O瓶颈、CKPT_FLUSH_INTERVAL设得太大、或者DATA_BUFFER太小导致频繁换页。
3.3 联机日志与归档日志的运维要点
达梦有两种日志形态:联机日志和归档日志。
- 联机日志是必须的,数据库实例在线运行时持续向它写入,循环覆盖;
- 归档日志是把写满的联机日志保存下来的备份文件,只有在
ARCH_MODE开启时才会产生。
开启归档是达梦生产环境的基本要求,原因很简单:联机日志循环覆盖,如果不做归档,历史日志会丢失,备份只能做差异性备份或全量备份,时间点恢复完全无从谈起。达梦的dm.arch.ini配置归档目标、归档类型和空间上限。
这里给一个非常实际的警告:归档日志目录写满,是达梦数据库“突然连不上”最常见的原因之一。数据库进程一旦无法写入归档日志,整个实例会进入挂起状态,新的连接请求可能排队等待甚至直接超时。排查时第一步往往不是看监听,而是看$DM_HOME/log下的日志,或者直接看磁盘空间。
我也踩过一次坑:某客户生产库归档目录放在/dm/arch,和系统根目录共用一块盘,结果根目录爆掉,数据库直接挂了。后来我把归档目录挪到单独的数据盘,并设置了ARCH_SPACE_LIMIT(归档空间上限),再配置定时清理策略,问题才算根治。
3.4 日志相关参数与监控
与 redo/日志空间直接相关的参数主要有:
RLOG_RAW_SIZE:单个联机日志文件的大小(单位MB),比如 2048 表示每个联机日志文件2GB;LOG_NUM:联机日志文件组数;ARCH_MODE:是否开启归档(1开启,0关闭);ARCH_SPACE_LIMIT:归档空间上限(单位MB,0表示不限制);ARCH_FLUSH_INTERVAL:归档刷新间隔(秒)。
达梦在生产环境推荐至少4个联机日志文件、每个1GB以上。日志组不是越多越好,但太少了会导致日志切换过于频繁,频繁切换时会有短暂的写停顿。如果你观察日志切换频率超过每分钟一次,就该考虑加大RLOG_RAW_SIZE或增加日志组数了。
日常监控我习惯用下面两条SQL:
-- 在线日志使用情况 SELECT * FROM V$RLOG; -- 归档日志信息 SELECT * FROM V$ARCHIVED_LOG ORDER BY SEQUENCE# DESC;结合实际经验,归档日志的SEQUENCE#跳跃出现大空洞,往往意味着某个时间段归档中断过,这是排查备份链路是否可靠的重要线索。
4. Undo机制与MVCC并发控制
4.1 undo的本质:数据修改前的镜像
undo 段(达梦里放在 ROLL 表空间)保存的是事务修改数据之前的旧版本。当一个事务执行 UPDATE 或 DELETE 时,达梦不会立刻覆盖旧数据,而是把旧数据版本的地址记录到 undo 段中,然后用新值修改数据页。这样:
- 事务回滚时,根据 undo 记录把数据页恢复为修改前的样子;
- 其他事务做一致性读时,可以通过 undo 链回溯到旧版本,看到“这个修改发生之前”的数据;
- 数据库崩溃恢复时,redo 负责“前滚”已提交事务,undo 负责“回滚”未提交事务的修改。
很多初学者搞不清 redo 和 undo 的区别,我用一句话总结:redo 保证你做过的修改能重做,undo 保证你做错的事情能撤销;redo 是向前看的保险,undo 是向后看的后悔药。两者不是互相替代的关系,而是配合工作的。
4.2 MVCC的实现原理:版本链与快照
达梦的 MVCC(多版本并发控制)机制,核心就是undo 版本链 + 读取快照。
当一个事务读取数据时,如果某一行正在被其他事务修改,它不会阻塞等待锁释放,而是沿着 undo 版本链查找该行在“当前读时刻”之前的版本,直接读取旧版本数据。这就实现了读不阻塞写、写不阻塞读。举例来说:
- 事务 A 修改了某行的余额,从 100 改成 200,但还没提交;
- 事务 B 在这个时刻执行 SELECT,它不会等 A 提交,而是读取 undo 链上余额为 100 的旧版本;
- 只有已经提交且提交时间早于 B 快照时刻的修改,B 才能看到。
这套机制的前提是 undo 记录不能太早被清理。如果 undo 保存时间不够长,事务 B 构造快照时发现需要的旧版本已经被清掉了,就会报snapshot too old(快照过旧)之类的错误。这在长事务 + 高并发更新场景下尤其容易发生。
4.3 快照读与当前读:很多问题的根源
MVCC 下,“读”有两类:
- 一致性读(快照读):普通的
SELECT,读的是执行时刻的快照,不加锁、不阻塞; - 当前读(锁定读):
SELECT ... FOR UPDATE、UPDATE、DELETE等操作,读的是数据的最新版本,并且要加锁防止并发修改。
这就是搜索热词里有人问“SELECT * FROM ... FOR UPDATE读了 MVCC 吗”的原因。答案是:FOR UPDATE不走 MVCC 快照读,它走的是当前读,必须加锁。如果同一行数据被其他事务更新且未提交,FOR UPDATE会等待锁;而普通SELECT则不会等,直接读快照。
实操里最常见的坑是:开发人员以为所有SELECT都“读不到未提交数据”,就放心地在应用里先SELECT判断、再UPDATE修改。结果在高并发下出现竞态条件——两个事务都SELECT到了旧值,都认为自己有权修改,后提交的人把先提交的人覆盖了。解决方式就是业务上必须用SELECT ... FOR UPDATE或UPDATE本身来加锁,依赖 MVCC 并不能避免更新丢失。
4.4 undo相关参数和常见坑
达梦中和 undo 相关的核心参数主要是:
UNDO_RETAIN:undo 记录保留时间(秒),控制太久版本的清理策略;UNDO_MODE:undo 模式;- ROLL 表空间的大小:undo 段所在的表空间,空间不足会导致事务执行失败。
生产环境中,我遇到过最典型的两个问题:
一是ROLL 表空间膨胀收不住。根因往往是某个应用在做大批量 UPDATE 或 DELETE,一个事务内修改了几百万行,所有旧版本都堆在 undo 里。此时即使其他事务早都结束了,只要这个大事务还没提交,undo 就不能乱清,表空间就会被涨满。解决思路是拆小事务,每批几千行提交一次。
二是ORA-01555 风格的快照过旧问题。达梦里常见的报错是“记录已被其他事务更新”或“undo 信息不足”。这种场景几乎都发生在“长查询 + 高并发更新”时。解决办法不是无脑调大UNDO_RETAIN,而是要评估业务长事务,尽量把长报表、数据抽取放到业务低峰,或者对相关表做更合理的更新批量控制。
5. 内存缓存架构与刷盘策略
5.1 达梦的内存结构概览
达梦实例的内存主要分几个区:数据缓冲区(DATA_BUFFER)、日志缓冲区(LOG_BUFFER)、字典缓冲区、SQL缓冲区、排序区等。其中最核心、对性能影响最大的是前两个。
可以把数据库内存想象成一个厨房的操作台:厨师(事务处理线程)在台面上切菜(修改数据页),台面就是数据缓冲区;切完的菜先放在一个小托盘(日志缓冲区)里记录;后台清洁工(检查点线程)定期把垃圾和成品端出厨房(刷盘)。操作台大小决定了你能同时处理多少菜;如果台面太小,厨师就得频繁往冰箱(磁盘)里存取,效率自然低。
5.2 数据缓冲区 DATA_BUFFER
DATA_BUFFER是达梦最大的内存池,存放数据文件的页副本。所有数据读写都要经过这里:读数据时如果页不在缓冲区,就从磁盘读取到缓冲区;写数据时先修改缓冲区中的副本,把该页标记为脏页,等后续刷盘。
DATA_BUFFER太小会导致buffer miss 率上升,SQL执行时频繁产生物理读,TPS下降明显;太大则占用大量内存,可能引发操作系统内存紧张、交换。对于OLTP系统,我一般先把DATA_BUFFER设置为物理内存的50%~60%,再根据命中率微调。
查看命中率的方法:
SELECT * FROM V$BUFFERPOOL; -- 关注 RATE 字段,表示缓冲命中率,正常应在95%以上如果命中率低于90%,优先加大DATA_BUFFER。但注意:加大数据缓冲区不能替代SQL优化。遇到大量全表扫描的慢SQL,缓冲区再大也迟早被打穿,最重要的还是用索引把逻辑读降下来。
5.3 日志缓冲区 LOG_BUFFER
LOG_BUFFER是日志写入的中转区。事务提交时,不会直接写磁盘上的日志文件,而是先把 redo 记录写入日志缓冲区,再由后台进程批量刷新到日志文件。这个批量化设计极大减少了I/O次数。
提升性能的关键在于减少日志写等待。如果LOG_BUFFER太小,事务提交时可能因为日志缓冲区满而等待;而如果过大,又会浪费内存。达梦默认值一般是几百MB量级,对于高并发短小事务的OLTP系统,可以适当调大,但没必要盲目设几GB——日志写瓶颈往往在磁盘I/O能力,不在缓冲区大小。
这里说一个经验:达梦对日志刷盘提供了**组提交(group commit)**能力,多个事务的日志可以合并成一次刷盘。在实际压测中,适当调大LOG_BUFFER并配合较快的存储(比如SSD),日志提交等待时间能明显下降。但如果你的存储本身很慢,加大缓冲区只能缓解一时,最终还是要解决磁盘能力问题。
5.4 刷盘策略与脏页管理
刷盘是内存和磁盘之间的“最后一公里”。达梦的脏页刷盘大致遵循以下策略:
- 延迟写:脏页不是事务提交时就立刻写数据文件,而是攒到检查点或缓冲区压力大时才写。这保证了高频小事务场景下的性能。
- LRU淘汰写:缓冲区空间不足时,按LRU算法淘汰页,如果被淘汰页是脏页,就先写盘再淘汰。
- 检查点批量写:后台检查点线程定期把脏页按序刷盘,每次刷固定页数(
CKPT_FLUSH_PAGES)。
这种机制下,一个高并发的OLTP数据库,脏页比例会动态波动。如果脏页长期占比过高,说明写压力大而刷盘能力不足。此时首先要看磁盘I/O,其次看CKPT_FLUSH_INTERVAL和CKPT_FLUSH_PAGES是否需要调整。
提醒一句:很多人以为把DATA_BUFFER调大就能解决所有写入性能问题,但实际上缓冲区越大,意味着脏页可能积累越多,一旦日志空间紧张或检查点触发,刷盘量反而更集中,I/O尖峰更明显。合理控制缓冲区大小,让脏页保持在一个相对稳定、可预期的水位,往往比一味贪大更健康。
6. 日常运维中的常见问题与排查实录
6.1 数据库突然连不上怎么办
这应该是达梦运维里最让人发慌的报障。根据我的经验,按下面的顺序排查,大多数情况下10分钟内能找到方向:
- 先看进程和监听:执行
ps -ef | grep dmserver确认实例进程是否在,再用disql登录确认监听端口。进程没了先看$DM_HOME/log下的.log文件尾部;进程还在但连不上,重点看端口有没有被防火墙挡住。 - 看磁盘空间:
df -h确认归档目录、数据目录所在盘是否满了。归档写满导致实例挂起,在达梦里太常见了。 - 看连接数:执行
SELECT COUNT(*) FROM V$SESSIONS;对比MAX_SESSIONS参数。连接数打满时新连接会报资源不足。 - 看日志文件:达梦日志目录一般在
$DM_HOME/log,文件名类似dm_实例名_日期.log,里面会明确记录“归档日志写满”“表空间不足”“连接数已满”等明确信息。
有一次客户反馈“数据库连不上了”,我上去一看,归档目录使用率100%,实例已经挂起。清理掉过期归档后实例立即恢复。这个场景每次复盘都提醒我:监控磁盘空间,尤其是归档目录空间,比监控数据库本身的指标更前置。
6.2 备份还原与DMP导入导出的注意事项
达梦的备份恢复有两个层面:
- 物理备份:使用
DMAP或dmrman工具备份数据文件、控制文件、归档日志,支持全量备份和增量备份; - 逻辑备份导出:使用
dexp导出 DMP 文件,使用dimp导入 DMP 文件。
很多人拿 Navicat 连达梦数据库或使用达梦管理工具(Manager)还原 DMP 文件,经常遇到“字符集不匹配”“对象已存在”报错。给几个实用建议:
- 导出导入时,源端和目标端的字符集尽量保持一致,否则中文数据容易出现乱码;
- 导入前先清理掉目标库中要导入的schema下的对象,避免
CREATE TABLE冲突; - 大数据量导入时,先导入表结构,再导入数据,最后重建索引和约束,速度会快很多;
- DMP 文件是逻辑级导出,不包含数据文件的物理状态,不能替代物理备份。生产环境必须同时有物理备份和逻辑导出备胎。
6.3 与nacos等中间件适配时的参数选择
达梦在国产化替代项目中经常要适配 Nacos 等中间件。Nacos 作为注册中心和配置中心,依赖数据库存储配置、服务、历史信息,SQL以简单的增删改查为主。适配达梦时,主要注意几个点:
- 使用达梦官方提供的 JDBC 驱动(
DmJdbcDriver18.jar),JDBC URL 格式是jdbc:dm://IP:PORT; - 达梦对标准SQL的兼容性总体不错,但个别语法(如分页、某些函数)有差异,Nacos 自带的建表脚本如果是按 MySQL 语法写的,可能需要进行少量调整;
- 连接池建议使用合适的连接数,Nacos 对数据库连接的管理并不复杂,核心是确保连接池不长期闲置又不过度占用达梦
MAX_SESSIONS。
一个实际项目里,我们遇到 Nacos 连接达梦时报“无效的列名”错,最后定位是驱动版本和达梦服务端小版本不匹配,换官方指定版本驱动后问题消失。所以遇到中间件适配问题,第一反应应该是检查JDBC驱动版本和服务端版本是否配套,而不是怀疑数据库配置。
6.4 检查点状态与日志状态的快速检查SQL
日常巡检时,我习惯把下面几条SQL整理成一个脚本,定期跑:
-- 1. 检查点信息 SELECT * FROM V$CKPT; -- 2. 联机日志状态 SELECT * FROM V$RLOG; -- 3. 归档日志序列 SELECT MAX(SEQUENCE#) AS MAX_SEQ, COUNT(*) AS CNT FROM V$ARCHIVED_LOG; -- 4. 数据缓冲区命中率 SELECT * FROM V$BUFFERPOOL; -- 5. 活动会话数 SELECT COUNT(*) FROM V$SESSIONS WHERE STATE = 'ACTIVE'; -- 6. 表空间使用率 SELECT TABLESPACE_NAME, FILE_SIZE_MB, FREE_SIZE_MB, (FILE_SIZE_MB - FREE_SIZE_MB) / FILE_SIZE_MB * 100 AS USED_PERCENT FROM V$TABLESPACE;这条脚本输出的信息,能让你在几分钟内判断一个达梦实例的“健康水位”:检查点位置是否滞后、日志是否接近切换峰值、缓冲区命中率是否下降、活动会话是否过高、表空间是否紧张。我把这些指标称为“达梦五大黄金指标”,每次排查都从这五条SQL开始。
6.5 一个真实的故障案例:检查点滞后导致日志暴涨
最后分享一个比较典型的案例。有一套跑批系统,每天凌晨执行大批量 UPDATE,持续半小时。某天早上监控报警:联机日志使用率长时间超过90%,数据库响应变慢。我看了V$CKPT发现检查点位置远远落后于日志写入位置,脏页刷不出去,日志自然不敢覆盖。
排查结果是:该库DATA_BUFFER被调到32GB,但CKPT_FLUSH_INTERVAL还保持默认3秒,每次刷盘页数在几十万行级别的大批改面前杯水车薪。刷盘线程每次只能刷100页(默认值),而日志产生的速度远快于刷盘速度,导致检查点一直追不上日志。
解决方式:把CKPT_FLUSH_PAGES适当调大到400,CKPT_FLUSH_INTERVAL从3秒调整为1秒;同时对跑批SQL进行分批提交(每5万条一次),大幅降低单事务内产生的日志量。调整后日志使用率稳定回落到40%~60%,故障解除。
这件事给我的教训很深刻:数据库参数都是联动的。缓冲区大了,脏页能力变强,但刷盘能力如果不跟上,日志空间和恢复时间都会成为新的瓶颈。调优不是“把某个值调大”这么简单,而是要把内存、日志、检查点、I/O看作一个整体来平衡。
根据我个人经验,达梦数据库的这些核心机制,和你在Oracle、PostgreSQL、甚至MySQL里学到的底层思路是高度相通的。把检查点、redo、undo、MVCC和刷盘策略理解透了,不只能解决达梦的问题,再看任何数据库都会觉得顺手很多。最后再分享一个小技巧:新接手一套达梦环境,先别急着调参数,把上面那几条巡检SQL的结果留存几份,连续观察一周,很多潜在问题会在那一周里自己现出原形。