达梦数据库关键字屏蔽:EXCLUDE_RESERVED_WORDS 实战
发布时间:2026/9/17 13:06:17来源:尧图网络
上个月帮朋友看一个数据库迁移项目他们的订单表里有两个字段一个叫comment备注一个叫level等级。这两个字段在原来的 MySQL 里跑得好好的存量 SQL 加个反引号就能用迁到达梦之后select id, comment, level from t_order直接报语法错误应用起不来报表全挂。翻了一圈才发现达梦的 SQL 解析器把 COMMENT 当成了建表注释相关的关键字把 LEVEL 当成了层次查询的伪列关键字字段名和系统关键字撞了车。这个坑就是“达梦数据库关键字屏蔽”要解决的核心问题通过实例级参数把一部分系统保留关键字从解析器的保留清单里挪出去让这些词可以像普通标识符一样当表名、列名、变量名用从而避免大面积改表结构、改存量 SQL。它不是什么炫技功能而是一味典型的“迁移期止血药”——用最小的改造成本让存量应用先在达梦上跑起来。这篇内容我准备按实操顺序讲透关键字为什么会在达梦里撞车、达梦的保留字体系长什么样、EXCLUDE_RESERVED_WORDS怎么配怎么验证、DW 和 DSC 集群环境下要不要一起改、Nacos 适配达梦时怎么配合使用、以及参数改了不生效时从哪儿开始查。适合正在做数据库替换的 DBA、后端开发和中间件适配的同学读不需要你已经是达梦专家但至少得能看懂建表语句和 JDBC 配置。1. 关键字屏蔽到底在解决什么问题1.1 一个真实的迁移现场列名是怎么和关键字“撞车”的数据库在跑一条 SQL 之前第一步是词法分析把一串字符切成一个个 token然后判断每个 token 属于哪一类是关键字、函数名、还是普通标识符。判断依据就是一份内置的关键字表。如果切出来的单词命中了关键字表里“保留”的那一档解析器就会按关键字的语义去处理它而不是当成你表里的列名。MySQL 和 Oracle 在这个问题上给了用户一根救命稻草反引号或者双引号。MySQL 里写commentOracle 里写COMMENT解析器就知道“这不是关键字这是标识符”。达梦同样支持双引号包裹标识符但它默认是大小写敏感的comment和COMMENT是两个不同的东西建表时用哪种写法查询时就必须用哪种写法匹配一字不差。问题就出在存量 SQL 上。一个跑了七八年的系统SQL 散落在 MyBatis 映射文件、存储过程、报表模板、ETL 脚本、定时任务里可能有几千处引用。SQL 里没有加引号的习惯迁移到达梦之后只要列名撞上保留字就会在解析阶段直接失败。这类失败往往不是“跑得慢”而是“根本跑不起来”排查起来还特别分散——今天修完订单模块明天库存模块又冒出来一个。注意报错信息里如果出现“语法错误”“无效的列名”“非法的标识符”这类描述先别急着怀疑驱动和连接串把报错那行 SQL 里的列名拿去和保留字清单比对一下命中率相当高。1.2 三条技术路线为什么我把屏蔽排在最后面对关键字冲突常规做法有三种屏蔽参数只是其中一种而且是最“重”的一种。方案改动点生效范围优点风险改表名或列名表结构、应用代码、报表、ETL局部长期最干净无副作用改造量大回归测试成本高SQL 统一加双引号SQL 层批量改写局部不动表结构大小写严格匹配全量改写风险高配置EXCLUDE_RESERVED_WORDSdm.ini 一个参数实例全局成本最低一次配置全局生效关键字语法功能丢失影响面大打开兼容模式建库参数COMPATIBLE_MODE实例全局顺带解决一批语法差异不能绕过所有关键字冲突我的排序逻辑是这样的新系统、新表一律走改列名这是最干净的路。老系统、表结构动不了的先评估能不能只在 SQL 层用拦截器统一加引号——比如 MyBatis 插件或者数据库中间件里做改写。这两条都走不通或者时间窗口根本不允许做全量回归的时候才动用参数屏蔽。为什么把屏蔽放最后因为它是一把“敌我不分”的刀。它不是在 SQL 层面告诉解析器“这个词这次是标识符”而是在实例层面告诉解析器“这个词以后永远不是关键字了”。你的应用可能只是想让它当列名但实例上其他任何一个脚本、任何一个工具只要还想用它当关键字就全都失效了。所以在生产库上开这个参数一定得想清楚谁在用它。1.3 屏蔽的代价它改的是解析器的“字典”理解这一点特别重要EXCLUDE_RESERVED_WORDS不是给某张表、某个模式开的例外它改的是整个实例的解析规则。举个具体的例子。假设你把 COMMENT 加进屏蔽清单那么CREATE TABLE T (ID INT, COMMENT VARCHAR(100))能建成功了SELECT COMMENT FROM T也能跑了。但与此同时COMMENT ON TABLE T IS 订单表这条注释语法就废了因为 COMMENT 已经不再是关键字解析器不认它了。同理如果屏蔽掉 LEVELSELECT LEVEL FROM T CONNECT BY PRIOR ID PID这种层次查询也会报错——LEVEL 作为伪列的功能没了。所以屏蔽清单不是越长越好。每加一个词你就等于砍掉了一项 SQL 语法能力。加之前必须回答两个问题第一实例上有没有业务真的在用这个关键字的语法功能第二如果哪天要用回来回退的成本是多少。这两个问题在迁移项目里经常被忽略等到上线后某个报表突然报错才回头找原因就很被动了。我在实际操作里养成了一个习惯屏蔽清单里的每一个词都在变更单上写清楚“为什么必须屏蔽、对应的业务表是哪张、有没有使用该语法的历史记录”。清单超过十个词就要拉上业务方一起过一遍宁可多花半天评审也不要在生产上反复重启。2. 达梦保留字机制拆解先把家底摸清楚2.1 用V$RESERVED_WORDS看清哪些关键字动不得动手之前必须先查清楚家底达梦提供了一个系统视图V$RESERVED_WORDS专门用来列关键字。-- 先看看这个视图长什么样不同版本列名略有差异 SELECT * FROM V$RESERVED_WORDS WHERE ROWNUM 5; -- 统计一下保留字总量 SELECT COUNT(*) AS TOTAL FROM V$RESERVED_WORDS WHERE RESERVED 1; -- 看看哪些是可以被屏蔽掉的 SELECT KEYWORD FROM V$RESERVED_WORDS WHERE RESERVED 1 AND RESERVED_FIXED 0;视图里几个关键列的含义KEYWORD是关键字本身LENGTH是长度RESERVED标记它是不是保留字1 是保留0 不是RESERVED_FIXED标记它是不是“固定保留字”——这个标记是整套机制里最关键的一列下面单独说。有一点要提醒不同小版本的视图列名和数量会有细微差别所以别照抄网上别人的结果一定在你自己的实例上先SELECT *看一眼结构再写查询。我见过有人拿着 A 环境的清单去 B 环境配参数结果版本不同清单里有两个词在 B 环境根本不存在配上去虽然不报错但也没意义纯粹增加维护负担。2.2RESERVED_FIXED这个标记决定了你的屏蔽清单上限RESERVED_FIXED 1的关键字是固定保留字配到EXCLUDE_RESERVED_WORDS里也不会生效。这类词通常是语法骨架级别的比如各类查询语句、DDL 语句的核心动词以及那些一旦去掉就会让解析器彻底失去判断依据的词。达梦这么设计很合理要是允许你把这类词屏蔽掉整个 SQL 语法体系就崩了。所以完整的筛选流程应该是两步先筛RESERVED 1拿到全部保留字再筛RESERVED_FIXED 0拿到可屏蔽的那一批最后拿你的业务列名去和这批可屏蔽的词做交集交集才是最终要配的清单。实践中的做法是这样的把SELECT KEYWORD FROM V$RESERVED_WORDS WHERE RESERVED 1 AND RESERVED_FIXED 0的结果导出成一个文本文件再把业务系统的建表语句里所有列名导出来两个集合做一次交集。这个交集通常比你想象的小得多——大部分业务字段名其实不撞车真正中招的往往就是那么几个通用词比如备注、等级、类型、状态之类。提示如果业务列名和固定保留字真的撞了那没有捷径只能改列名或者加双引号参数这条路走不通。这种情况越早发现越好别等到配置完参数重启完才发现白干一场。2.3 大小写敏感与兼容模式两个会和你抢戏的参数在达梦里“关键字冲突”从来不是孤立事件它往往和两个参数纠缠在一起排查时必须一起看。第一个是CASE_SENSITIVE也就是大小写敏感性默认是敏感。这意味着字符串比较、标识符匹配都严格区分大小写。影响是什么你不加双引号写的列名达梦会统一转成大写去匹配你加了双引号写的列名原样保留。如果建表时用了comment小写形式查询时写COMMENT是找不到这一列的。很多“明明配了屏蔽还是不认”的情况根源其实是大小写没对齐不是参数没生效。第二个是COMPATIBLE_MODE兼容模式。它决定实例按哪种数据库的行为习惯去解析 SQL常见取值里有 Oracle 兼容模式和 MySQL 兼容模式。开启 MySQL 兼容模式后一部分语法差异会被抹平比如分页写法、部分函数行为。但它和关键字屏蔽是两码事兼容模式解决的是“语法怎么写”关键字屏蔽解决的是“这个词还能不能当关键字”。两者可以叠加使用但不要指望打开兼容模式就能绕过关键字冲突我实测过该撞的车还是会撞。判断顺序建议是先确认实例的兼容模式和大小写敏感设置再去看关键字清单最后才决定屏蔽清单。顺序反了容易做无用功。3. 动手实操从冲突扫描到参数生效3.1 第一步把存量 SQL 里的“嫌疑词”扫出来这一步千万别省。凭印象列清单是最容易漏的。我的做法是把实例里的可屏蔽关键字导出来再拿业务侧的表结构定义去比对。先把可屏蔽关键字导出-- 导出可屏蔽关键字用 Navicat 或 disql 导出成文本都行 SELECT KEYWORD FROM V$RESERVED_WORDS WHERE RESERVED 1 AND RESERVED_FIXED 0 ORDER BY KEYWORD;然后用一段简单的脚本扫建表语句。下面这个 Python 版本是简化过的够用import re def load_reserved(path): 从导出的关键字文件里读可屏蔽关键字集合 words set() with open(path, encodingutf-8) as f: for line in f: w line.strip().upper() if w: words.add(w) return words def scan_ddl(sql_text, reserved): 扫描建表语句里的列名返回命中关键字的列 hits {} pattern re.compile(rcreate\stable\s[^(]\((.*?)\)\s*;, re.S | re.I) for m in pattern.finditer(sql_text): body m.group(1) for line in body.split(,): tokens line.strip().split() if not tokens: continue col tokens[0].strip([]).upper() # 跳过约束定义行 if col in (PRIMARY, UNIQUE, KEY, INDEX, CONSTRAINT, FOREIGN): continue if col in reserved: hits[col] hits.get(col, 0) 1 return hits if __name__ __main__: reserved load_reserved(reserved.txt) ddl open(schema.sql, encodingutf-8).read() result scan_ddl(ddl, reserved) for k, v in sorted(result.items(), keylambda x: -x[1]): print(f{k}\t出现 {v} 次)输出结果按出现次数排序出现次数多的优先处理通常意味着影响面大。如果建表语句是从 Navicat 里导出的格式比较统一这个脚本基本能覆盖。要是你的 DDL 格式特别乱那就退而求其次用 grep 把列名抓出来做交集也够用。3.2 第二步改 dm.ini写法比你想的讲究拿到清单之后找到实例的配置文件。Linux 环境下通常在数据目录里比如/dm/data/DAMENG/dm.ini具体路径跟你的安装方式有关可以用find定位。参数名是EXCLUDE_RESERVED_WORDS值是用逗号分隔的关键字列表。写法上注意几点多个关键字之间用英文逗号不要用中文逗号不要在逗号两边塞多余空格关键字建议用大写虽然达梦可能做了大小写处理但大写最不容易出问题。# dm.ini 片段 EXCLUDE_RESERVED_WORDS COMMENT,LEVEL,TYPE,SIZE除了手改文件也可以试着用存储过程改。达梦对字符串类型的参数提供了对应的设置接口-- scope 为 2 表示同时修改内存和配置文件静态参数需要重启后生效 SP_SET_PARA_STRING_VALUE(2, EXCLUDE_RESERVED_WORDS, COMMENT,LEVEL,TYPE,SIZE);我的经验是能用存储过程改就用存储过程它会帮你把格式校验一遍改不动或者报权限相关的错再直接编辑 dm.ini最直接。但无论哪种方式改完都要重启实例这个参数属于静态参数不重启不生效别指望改完立刻见效。注意改文件前先备份一份。cp dm.ini dm.ini.bak.20240601这条命令花不了两秒但省下的可能是几个小时的排查时间。生产环境尤其要养成这个习惯。3.3 第三步重启与三重验证重启这一步不同部署方式命令不一样。单机环境如果是用服务脚本管理的通常是# 先看服务名一般和实例名有关 ls $DM_HOME/bin | grep DmService # 重启实例 systemctl restart DmServiceDMSERVER # 或者用脚本方式 cd $DM_HOME/bin ./DmServiceDMSERVER restart重启完别急着通知业务方联调先自己做三重验证。第一重看参数是不是真的写进去了SELECT PARA_NAME, PARA_VALUE, FILE_VALUE FROM V$DM_INI WHERE PARA_NAME EXCLUDE_RESERVED_WORDS;FILE_VALUE是配置文件里的值PARA_VALUE是当前内存里的值。重启之后这两者应该一致如果不一致说明参数没被正确加载得回去看 dm.ini 的写法。第二重直接在数据库里造一张测试表CREATE TABLE T_KW_TEST ( ID INT, COMMENT VARCHAR(100), LEVEL INT ); INSERT INTO T_KW_TEST(ID, COMMENT, LEVEL) VALUES(1, 测试备注, 3); SELECT ID, COMMENT, LEVEL FROM T_KW_TEST;能建、能插、能查说明屏蔽生效了。这里特别注意建表时千万不要用COMMENT xxx这种列注释语法因为 COMMENT 已经被屏蔽成标识符了这个语法此时用不了。第三重用应用的真实连接串跑一遍。参数是实例级的但不代表驱动、连接池这一层就没有别的问题用应用侧的连接实测一遍才能确认端到端没问题。我一般会挑三个最有代表性的接口一个纯查询、一个带分页的列表查询、一个写入。这三个过了基本可以放心。3.4 第四步DW/DSC 集群环境下的配置一致性单机环境相对简单集群环境就得多想一层。DW 是数据守护主备架构。主库改了什么参数备库也要同步改否则一旦发生切换备库接手后的解析行为跟主库不一致业务侧就会出现“平时没问题一切换就报错”的诡异现象。所以 DW 环境下的操作顺序建议是先改备库再改主库两边确认一致后再按主备切换的规范流程做一次验证切换确保两边行为对齐。DSC 是数据共享集群多节点共享存储每个节点都有自己的 dm.ini。这意味着每一个节点的配置文件都要改一个都不能漏。而且节点是逐个重启的要注意重启顺序和集群状态别一次性全停那样业务就断了。稳妥的做法是滚动重启摘掉一个节点改了配置重启确认这个节点正常后再处理下一个。提示集群环境的参数一致性建议做成检查项写进变更流程里。我见过最典型的翻车场景就是三个节点里改了两个剩一个忘了结果连接池轮询到那个节点就报错稳定性排查了半天才找到原因。4. 组合场景Nacos 适配达梦时怎么用关键字屏蔽4.1 数据源改造与建表脚本的适配Nacos 适配达梦这个场景是这两年被问得最多的组合之一。Nacos 默认走内嵌数据库或者 MySQL换到达梦需要三件事引入达梦的 JDBC 驱动改数据源配置把建表脚本换成达梦能执行的版本。驱动这块把达梦的 JDBC 驱动包放进 Nacos 的依赖目录然后在application.properties里改数据源配置大致是这样一个形态spring.datasource.platformdm db.num1 db.url.0jdbc:dm://127.0.0.1:5236?schemaNACOS db.user.0NACOS db.password.0你的密码建表脚本是重头戏。MySQL 的建表语句直接拿到达梦上是跑不通的自增主键、unsigned、datetime、text这些写法都要调整。这时候如果你实例开了 MySQL 兼容模式能省掉一部分改造工作量但不能全指望它。至于关键字冲突Nacos 自己的表结构整体还算规整但不代表一定不撞车。我的做法是把 Nacos 的建表脚本丢进 3.1 节的扫描脚本里跑一遍用结果说话不要凭感觉猜测哪些字段有问题。跑出来有命中就按前面的方法处理能改列名的优先改列名改不动的把词加到屏蔽清单里。4.2 应用侧 ORM 与 SQL 的配套调整中间件适配完之后业务应用这边还有一堆配套工作要做关键字屏蔽只是其中一环。ORM 层面MyBatis 的映射文件里如果写了带反引号的 SQL迁到达梦要统一处理反引号在达梦上是无效的。可以写一个 MyBatis 拦截器统一替换也可以在建 SQL 的时候就去掉。分页这块要特别注意原来用 MySQL 的limit到达梦上要么依赖兼容模式要么换成达梦的分页写法这个坑在列表页特别容易暴露。还有一类容易被忽略的是数据库端的自定义函数。比如很多系统会用数据库函数生成中文首拼码来做编码规则这类函数在迁移时基本都要用 DMSQL 重写一遍。写的时候注意字符串处理函数的差异别直接把源库的函数体照搬过来语法能过不代表结果对一定要拿真实数据做一轮比对。首拼码这种东西一旦生成错了影响的是编码规则回补数据非常麻烦。4.3 备份、回滚与上线节奏改参数这件事一定要有回滚预案。我的做法是这样第一改 dm.ini 之前先做文件级备份这是最快最直接的还原手段。第二做一次逻辑备份dexp导出关键模式至少保证数据这一层有兜底。第三如果时间允许做一次物理备份dmrman全库备份。物理备份里包含了配置文件恢复的时候要注意别把已经调好参数的 dm.ini 覆盖回旧版本恢复前先确认清楚。上线节奏上我建议把关键字屏蔽这类实例级参数的变更安排在业务低峰期并且和业务方约定一个明确的观察窗口。观察期内重点看三类日志应用侧的 SQL 异常报错、数据库端的错误日志、以及慢 SQL 的变化。参数屏蔽理论上不会让 SQL 变慢但如果屏蔽掉了某个关键字导致原本的 SQL 走了不同的执行路径也是有可能出现性能波动的观察一下没坏处。5. 常见问题与排查实录5.1 参数改了没反应先看这张表这是被问得最多的问题我把遇到过的原因整理成了一张速查表。现象可能原因排查与解决参数值查询为空dm.ini 路径找错了改了别的实例的配置用V$DM_INI确认当前实例的配置文件路径参数值有但行为没变没重启实例静态参数未加载重启后重新查PARA_VALUE与FILE_VALUE部分关键字生效部分不生效其中某个词是固定保留字查RESERVED_FIXED值为 1 的必须移出清单建表能过查询报列不存在大小写没对齐检查CASE_SENSITIVE核对建表时的引号写法单机正常集群切换后异常备库或某个节点没改配置逐节点核对EXCLUDE_RESERVED_WORDS的值应用报错但数据库直连正常连接池里有旧连接未释放重启应用或让连接池回收长连接改了参数后别的功能报错屏蔽掉了仍在使用的语法关键字评估该语法是否在用必要时从清单移除这张表的用法是自上而下逐行排除不要跳步。大部分“不生效”的问题前两行就能解决。5.2 参数值写法上的几个小坑格式问题看着不起眼但在生产上折腾人的次数相当多。第一个坑是中文逗号。从文档里复制参数值的时候很容易把英文逗号复制成中文逗号肉眼几乎看不出来但达梦解析这个列表的时候就会出问题。改完参数之后用V$DM_INI查一下实际值仔细看每个分隔符。第二个坑是多余空格。EXCLUDE_RESERVED_WORDS COMMENT, LEVEL这种写法空格可能导致某个词被当成带空格的关键字进而匹配不上。统一写成COMMENT,LEVEL最保险。第三个坑是重复配置。如果你在 dm.ini 里搜EXCLUDE_RESERVED_WORDS发现有两行说明之前有人改过但你不知道。这时候要看清楚哪一行生效达梦对重复参数的处理方式跟你想象的未必一样最稳妥的是把重复行清掉只留一行。第四个坑是清单里塞了不存在的词。有些词你以为是关键字其实在可屏蔽清单里根本没有配上去虽然不报错但会让其他人产生误解以为已经处理过了。每次改完清单都拿V$RESERVED_WORDS复核一遍。5.3 屏蔽过头业务 SQL 报错怎么退回来如果上线后发现某个业务功能挂了而且确认是关键字屏蔽引起的处理思路要分两步走。第一步是快速止血。从清单里去掉引起问题的那个词重启实例。这里要注意重启是有业务中断代价的所以别一个词一个词地试先把怀疑范围缩小到具体是哪个词引起的再一次性改完重启。怎么定位是哪个词把报错的 SQL 拿到数据库上单独跑逐个把清单里的词从 SQL 里替换掉再试基本能锁定。第二步是长期解决。从清单里去掉这个词之后原来的冲突还在得换方案。这时候回到 1.2 节的路线图能不能只改那一张表的列名能不能在应用层给这几个引用点加上双引号如果这个列的引用点很少改列名加改代码可能是最省事的。我遇到过一个案例屏蔽 LEVEL 之后层次查询报表挂了最后是改了报表 SQL用递归 CTE 替代了CONNECT BY的写法比把 LEVEL 留在清单里更稳。注意回退之前想清楚去掉一个词可能让另一个业务功能重新报错。所以回退和重新屏蔽一样都要走一遍范围评估别在故障处理的时候慌手慌脚地改配置。6. 上线前的检查清单与个人体会6.1 上线前的检查清单把下面这些项过一遍基本可以避免掉绝大多数翻车场景。关键字清单来自V$RESERVED_WORDS的实际查询结果不是网上抄的也不是凭印象列的清单里每个词都确认过RESERVED_FIXED 0每个词都做过语法功能影响评估确认实例上没有人还在用它当关键字dm.ini 已经备份备份文件路径记录在变更单里逻辑备份或物理备份已完成恢复步骤写过一遍单机环境确认重启成功集群环境确认每个节点的配置一致应用侧用真实连接串做过端到端验证覆盖查询、分页、写入三类操作回滚方案明确回滚后需要重启的步骤和影响面已经和业务方沟通观察窗口和责任人明确观察期内有人盯着应用日志和数据库错误日志这份清单本身不复杂难点在执行的时候不走捷径。我见过太多“就改一个参数不用这么麻烦吧”的情况最后都是在某个没想到的角落出问题。6.2 我个人在实际操作中的体会最后说几句掏心的体会。关键字屏蔽这个功能最大的诱惑是“成本低”最大的陷阱也是“成本低”。它让你觉得不用改代码、不用改表结构改一个参数就搞定了于是很容易产生一种错觉这是个零风险操作。但实际上它改的是整个实例的解析规则影响的是所有跑在这个实例上的东西包括你自己都不知道存在的那几个定时任务和报表脚本。我现在的习惯是能改列名就改列名参数屏蔽只在“存量改造量实在太大、且明确评估过语法影响”的情况下用而且清单尽量短能只屏蔽一个词就绝不放两个能用半年就做一次清理。它是一副对症的止痛药但止完痛之后该做的手术还是得找时间做掉。再补一句容易混淆的地方如果你说的“关键字屏蔽”指的其实是日志里不落敏感 SQL、查询时对身份证手机号做动态脱敏那是另一个话题属于数据脱敏和审计的范畴和保留字屏蔽完全是两回事方案和参数都不通用别在排查的时候串了线。
网站建设高端定制企业官网