做国产数据库相关项目,最常被翻牌子的就是达梦数据库。这几年国内企业信息化改造、系统信创化升级,几乎绕不开它。日常接手最多的问题,不是复杂的架构设计,而是那些看起来很小、却很磨人的使用问题:连不上库、迁移报错、备份失败、SQL突然变慢。这篇文章不打算讲高深原理,就把我在实际项目里反复踩过的、帮别人排查过的高频问题,按场景分类整理出来,每个问题都给出排查思路和可落地的解决办法。适合刚接触达梦的开发、运维、DBA同学,也适合正在做Oracle或MySQL向达梦迁移的朋友直接“抄作业”。
1. 部署与初始化阶段的高频问题
1.1 服务起不来、连接被拒,先按这个顺序排查
接触达梦的第一道坎,往往不是SQL,是“连不上去”。我在现场排查过太多这种案例:应用、中间件、数据库都在同一台服务器上,但是disql一敲就报网络通信异常。这时别急着怀疑数据库配置,按下面这个顺序来,基本能解决90%的连接类问题。
第一步是确认服务进程是否存在。达梦安装完成后,通常会注册一个服务,常见形式是DmServiceDMSERVER这样的系统服务。在Linux下可以直接执行:
systemctl status DmServiceDMSERVER如果服务处于active状态,进程一般没问题。如果服务没起来,先看日志,达梦的日志目录通常位于安装路径下的log子目录,实例日志文件命名类似dm_DMSERVER_年月.log。查看日志里有没有磁盘空间不足、内存分配失败这类明确原因,修复后重新启动服务。
第二步是确认端口。达梦默认端口是5236,但很多人初始化实例时改了端口,或者同一个服务器部署了多个达梦实例,这个最容易踩坑。可以用下面命令确认端口是否在监听:
netstat -an | grep 5236 ss -lnt | grep 5236如果端口没监听,说明实例可能没起来,或者监听地址写的是某个特定IP。查一下dm.ini里的PORT_NUM参数和LISTEN_IP参数。
第三步是确认客户端连接的地址。很多人是在本地连接没问题,跨服务器连接就报错。这时先检查服务器的防火墙,把5236端口放通。再看客户端是否配置了正确的IP和端口。连接测试建议直接用达梦自带的命令行工具:
./disql SYSDBA/SYSDBA@192.168.x.x:5236注意用户名和密码之间是斜杠,地址和端口之间是冒号,不要写反。通过这条命令能快速把问题范围缩小到“网络”还是“数据库”层面。
提示:如果数据库部署在云服务器或虚拟化环境,还要确认安全组、虚拟网络ACL是否放行了对应端口。这些问题往往比数据库本身更隐蔽。
1.2 初始化实例时,这几个参数务必一次想清楚
达梦创建实例用的是dminit工具,很多人急着初始化,把参数随便一填就往下走,后面迁移数据时才发现问题。dminit有几个参数是初始化以后基本改不了的,必须在执行之前想清楚。
第一个是PAGE_SIZE,页大小。达梦支持4K、8K、16K、32K等选项,默认是8K。这个参数直接影响单行数据最大长度、索引的性能表现。如果从Oracle迁移过来,建议根据表结构最大行长来选择,比如有长字段的静态表,页大小选16K或32K更稳。一旦库建好,页大小改不了,只能重建实例,代价非常大。
第二个是CASE_SENSITIVE,标识符大小写敏感。这是迁移项目里翻车率最高的参数。Oracle默认不带双引号的标识符会转成大写存储,很多从Oracle迁到达梦的项目,应用代码里大量使用双引号包含小写表名、小写字段名,如果初始化时CASE_SENSITIVE选错,后面SQL全部报对象不存在。从Oracle迁移的场景,我的建议是保持默认的Y,也就是大小写敏感,同时要求应用侧SQL统一大小写规范。如果前期评估发现应用代码里小写对象名太多,改动量过大,可以考虑选N,但一定要全项目统一约定,否则更容易乱。
第三个是CHARSET,字符集。达梦支持GB18030和UTF-8两个选项。现在的业务系统基本都推荐UTF-8,一方面兼容各种语言字符,另一方面和Oracle的AL32UTF8迁移时更贴合。选了GB18030后在Oracle迁移场景会遇到编码转换的坑。第四个是LENGTH_IN_CHAR,这个参数控制VARCHAR类型的长度单位是按字节还是按字符。从Oracle迁移过来的项目,建议设为1,也就是按字符计算,这样VARCHAR2(100)迁移到达梦VARCHAR(100)后,行为基本一致,避免中文占字节导致字符长度不够报错。
一个比较稳妥的初始化命令参考:
./dminit PATH=/data/dmdata DB_NAME=DAMENG PAGE_SIZE=16 CASE_SENSITIVE=Y CHARSET=1 LENGTH_IN_CHAR=1 PORT_NUM=5236提示:
dminit执行完成以后,会输出实例的详细信息,包括控制文件路径、数据文件路径、数据库名、端口号。这些信息建议截图保存,后续运维会频繁用到。
2. 数据迁移与SQL兼容性适配
2.1 数据类型映射没弄对,导入一半就报错
达梦兼容Oracle做得不错,但“兼容”不等于“完全一样”。最常见的数据导入失败,都是卡在数据类型转换上。从Oracle迁移到达梦,我建议先按下面这张对照表理一遍所有字段类型。
| Oracle类型 | 达梦推荐类型 | 注意事项 |
|---|---|---|
| VARCHAR2(n) | VARCHAR(n) | 建议确认达梦的LENGTH_IN_CHAR初始化参数,按字符计算时长度语义一致 |
| NUMBER(p, s) | NUMERIC(p, s) 或 DECIMAL(p, s) | 注意精度和标度,金额字段建议保留两位小数 |
| DATE | DATE或TIMESTAMP | 达梦DATE支持时分秒,但如果原库有带毫秒的时间,用TIMESTAMP |
| TIMESTAMP | TIMESTAMP | 注意时区语义差异 |
| CLOB | TEXT或CLOB | 大字段迁移时注意备份文件大小和导入缓冲区设置 |
| BLOB | BLOB | 二进制大对象,导入导出时建议单独处理 |
这里特别说下NUMBER类型。Oracle的NUMBER可以不带精度,比如NUMBER能存任意精度数字,但达梦的NUMERIC如果没指定精度,默认行为和Oracle略有差异。我遇到过一张表有几十个NUMBER字段,迁移后插入超过默认精度的小数时直接报值超出范围。稳妥的做法是在建表脚本里显式指定NUMERIC(p,s),别偷懒依赖默认值。
还有一个小坑:Oracle的EMPTY_STRING等价于NULL,但达梦里VARCHAR空字符串和NULL是区分开的。从Oracle导出的数据里如果存在空字符串,迁移后查询条件WHERE col = ''的行为会跟原库不一致,应用层需要仔细核对这类SQL。
2.2 SQL写法上的不兼容点,迁移时代码为什么要大改
数据类型是表结构层面的问题,SQL语法则是代码层面的问题。达梦对Oracle语法的兼容度在国产数据库里算相当高的,但实际测试中还是能发现不少差异点。
函数兼容性。大部分Oracle函数如NVL、DECODE、TO_DATE、TO_CHAR、SYSDATE在达梦里都支持,但是参数格式可能有细微差别。比如TO_DATE的格式模板,个别场景下对字符串的解析要求更严格。我遇到过Oracle里正常执行的TO_DATE('20240101', 'YYYYMMDD')在达梦上报格式不匹配,换成TO_DATE('2024-01-01', 'YYYY-MM-DD')就没问题。迁移时如果存储过程、视图多,建议做一个函数扫描,把不常见格式模板统一排查一遍。
字符串拼接。Oracle里字符串 || 字段这个写法达梦支持,但是如果有人用了CONCAT(a, b),达梦的CONCAT只支持两个参数,三个以上参数会报错。项目里如果大量使用嵌套CONCAT,建议批量改成||拼接。另外,||两侧如有NULL,结果会是NULL,和Oracle行为一致,这个不用额外处理。
分页查询。Oracle经典写法WHERE ROWNUM <= 20在达梦里能用,但达梦也支持LIMIT语法。从MySQL迁过来的系统建议保留LIMIT写法,从Oracle迁过来的系统ROWNUM写法则要注意排序和分页组合时的结果差异,ORDER BY子句的执行顺序问题能引发分页数据错乱,需要实测确认。
序列和触发器。这个是最容易忽略的。Oracle的序列和触发器配合生成主键,达梦也支持,但迁移后有个常见问题:数据导入了,序列的当前值没有同步更新,新插入数据时主键冲突。解决办法是迁移完数据后,手动把每个序列的当前值改成表里最大ID加1。达梦提供了修改序列起始值的方法,也可以重建序列。
存储过程和包。达梦支持存储过程、函数、包,但Oracle的一些特性如自治事务、动态SQL的某些写法、集合类型的嵌套使用,在达梦里的表现不完全一致。这类复杂对象建议在迁移前做一轮静态审查,把不兼容的语法标出来。不要指望迁移工具一次搞定,工具通常只处理常规对象,复杂逻辑往往需要人工介入。
3. 备份恢复是运维底线,这些细节决定成败
3.1 备份失败的常见原因与对策
数据库管理员的底线工作就是备份。达梦的备份方式分为物理备份和逻辑备份,物理备份又分为联机备份和脱机备份。生产环境最常用的是联机备份,也就是数据库处于运行状态时执行备份操作。
联机备份最典型的失败原因有三个。
第一个是磁盘空间不足。备份文件需要写磁盘,如果备份目录所在分区空间不够,备份会直接失败。这个看似简单,实则在生产环境里经常发生,因为备份目录和数据目录往往在同一个分区,数据增长会压缩备份空间。我建议用布控任务定期检查分区使用率,备份目录空间至少留一个完整备份文件大小的余量。
第二个是归档模式未开启。联机备份依赖归档日志,如果数据库没开归档,执行联机备份时会提示无法备份或备份不完整。确认归档是否开启,可以查询:
SELECT * FROM V$ARCHIVE_DEST;如果归档信息为空,参考以下方式开启归档并重启数据库生效:
ALTER DATABASE ADD ARCHIVELOG 'DEST=/data/dmarch, TYPE=LOCAL, FILE_SIZE=256, SPACE_LIMIT=2048'; ALTER DATABASE SET ARCHIVELOG;注意:不同版本达梦的归档配置语法略有差异,执行前最好确认版本对应的官方手册。修改归档配置后必须重启数据库服务才能生效。
第三个是权限问题。备份目录需要数据库进程用户有写权限,尤其是备份目录由DBA手工创建的场景,经常遇到root创建目录后数据库进程无法写入。解决方式很简单,把目录属主改成数据库安装用户,或者通过chmod授权。
联机备份的完整命令大概长这样:
BACKUP DATABASE FULL BACKUPSET '/data/dmbackup/backup_20250101';备份完成后,备份集目录下会生成一系列文件,包括主备份文件.meta和相应的数据备份文件。恢复时使用dmrman工具或图形化管理工具,指定对应的备份集路径即可。
3.2 归档日志增长失控,怎么防“磁盘爆满”
开了归档以后,一个新的麻烦自然出现:归档日志增长过快,磁盘被慢慢撑满。这个问题我在现场碰到好几次,特点是有很强隐蔽性——平时没感觉,等到磁盘满的时候数据库直接hang住,如果此时恰好需要做恢复,就会发现日志不完整。
控制归档日志增长有几个常用手段。
第一,设置归档空间上限。在上面的ADD ARCHIVELOG语句里,SPACE_LIMIT参数就是归档空间限制,单位为MB。生产环境根据业务量合理设置,我一般会给一个“足够支撑最近一次全备到下一次全备之间日志量”的值。第二,合理规划全备周期。全量备份会截断归档日志,所以全备周期不能拉太长。联机备份时达梦会自动清理已经包含在备份中的归档日志,前提是正确配置了归档日志的删除策略。第三,设置定时清理任务。清理历史归档时用达梦提供的SF_ARCHIVELOG_DELETE_BEFORE_TIME之类的函数,按时间段删除指定时间之前的归档文件。
逻辑备份的dexp和dimp使用时同样注意导出文件大小。dexp导出大库时,建议按用户或按表分批导出,避免单文件过大导致导出中途失败。导出和导入前最好先确认目标库的兼容性参数,特别是字符集和页大小,这两个不一致时,导入过程经常报错。
4. 性能诊断与锁等待问题排查实录
4.1 慢SQL定位,用达梦自带的ET工具就够了
SQL性能问题在达梦里的排查思路跟Oracle类似,从动态视图入手,抓SQL、看执行计划、定位瓶颈。团队里很多人一遇到慢SQL就问“有没有类似的AWR报告”,实际上达梦也提供了配套的性能视图和工具。
定位慢SQL的第一步是找到具体是哪些SQL占用了大量资源。可以查询会话视图来观察当前正在执行的SQL:
SELECT * FROM V$SESSIONS;V$SESSIONS视图里包含会话ID、用户、状态、正在执行的SQL等信息。如果看到某个会话长期处于ACTIVE状态且SQL_TEXT有内容,那基本就是它在慢。
确认了目标SQL之后,使用达梦特色工具ET来分析。ET工具在disql里按以下方式开启:
SET ET ON;然后正常执行那条需要分析的SQL。执行结束后,达梦会输出一份执行计划树,展示每一步操作的行数、耗时、缓存命中率等指标。这个ET输出的信息量非常大,能直接看出哪一步慢。比如某个大表走了全表扫描、某个哈希连接消耗了大量时间,一目了然。
分析完才能动手优化。常见的优化手段包括:检查是否有合适索引,确认谓词条件是否被索引覆盖;调整SQL写法避免隐式类型转换,比如字段是VARCHAR类型,条件却传了数字,导致索引失效;考虑更新统计信息让优化器选出更好的执行计划。
提示:达梦里统计信息更新可以用
DBMS_STATS.GATHER_TABLE_STATS来操作。部分场景下新导入大量数据后没收集统计信息,SQL跑得慢,收集之后立马改善。这一点经常被忽略。
4.2 锁等待和死锁,怎么快速定位和终止会话
锁问题是运维人员最头疼的问题之一。表现往往就是某个业务卡住不动,界面长时间无响应。在达梦里处理锁问题,我的思路分三步:查锁、定位、终止。
查锁时先看会话和事务视图,找到持有锁的会话。常见查询思路是通过V$LOCK查看锁对象,关联V$TRX查看事务信息,再关联V$SESSIONS拿到会话详情。锁冲突的本质是事务A持有了某行或某页的锁,事务B申请同一资源的锁时被阻塞。这时候要判断的不是“哪个会话在等”,而是“哪个事务还没提交”。
定位到持有锁的会话后,根据业务情况做决策。如果这个会话是僵尸会话或者事务回滚可以接受,直接终止会话:
ALTER SYSTEM KILL SESSION '会话ID';如果会话对应的事务还在执行重要操作,不能随便杀,需要和业务方确认是否可以回滚。生产环境操作前务必确认会话身份,不要误杀正常业务。
预防锁问题比解决锁问题更重要。应用层的事务设计要遵循“短事务”原则,尽早提交或回滚,不要在一个事务里做太重的批处理操作。批量更新尽量避开业务高峰。确认数据库层面没有太多“先查后写”“先写后查”的闭环式交叉访问,这类场景最容易引发死锁。死锁发生后达梦会自动检测并回滚其中一个事务,但是产生死锁的业务报文告警仍然需要人来处理。
5. 运维中容易被忽略的“小问题”
5.1 密码策略与账号锁定的管理
达梦和其他数据库一样,默认会有一套密码安全策略。如果你用默认密码比如SYSDBA的初始密码,第一次登录后通常会被提示修改。如果某天突然发现某个应用账号连不上库,报密码错误或账号锁定,多半是密码策略在起作用。
处理账号锁定问题的步骤是:先用DBA权限账号登录,查询账号状态和锁定时间,然后解锁或重置密码:
ALTER USER USER_TEST LIMIT FAILED_LOGIN_ATTEMPTS 5, PASSWORD_LOCK_TIME 1;上面的语句限制登录失败5次后锁定1天。生产环境要根据运维规范合理设置,太严会导致应用反复锁定账号,太松有安全隐患。我一般建议应用账号允许10次以内失败尝试,锁定时间不超过1天,并且定期在低峰期轮换密码。
另外建议办法认真管理DBA账号,避免多个开发人员共用一个管理账号操作生产库。改密时要注意应用中配置的数据库连接字符串同步更新,否则应用会因为密码过期或修改而连不上库。
5.2 实例参数、服务自启动、日志规范
运维层面的另一个高频问题,是实例参数设置不匹配实际负载。最典型的是MAX_SESSIONS连接数不够,业务量稍大就报“超出最大会话数限制”。这个参数可以在数据库运行状态查询当前值:
SELECT * FROM V$PARAMETER WHERE NAME = 'MAX_SESSIONS';如果发现连接数上限不足,可以动态调整。部分参数在达梦里支持动态修改,比如ALTER SYSTEM SET 'MAX_SESSIONS'=500;即可生效,或者调用系统函数修改后重启。修改参数前注意确认参数类型是静态还是动态,静态参数修改后必须重启实例才能生效,否则改了等于没改。
服务自启动也是个需要提前处理的细节。服务器重启后,达梦服务如果没配置开机自启动,数据库会“消失”,应用侧随之报错。Linux下注册并配置开机启动的方式,正规做法是通过达梦安装包自带的脚本工具注册服务,然后执行systemctl enable加以设置。不要用简单粗暴的把启动命令塞进rc.local的做法,进程监控和自动拉起都没有保障。
日志管理方面,达梦的日志包括实例日志、SQL日志、备份日志等。SQL日志功能默认可能是关闭的,需要排查问题时临时开启,通过参数设置日志输出目录。长期开启SQL日志会带来磁盘IO开销和日志增长,不建议生产环境常开,排查问题时开一阵子,排查完及时关闭。
最后再分享一点个人体会
跟达梦数据库打交道这几年,我一直有一个感受:大部分“难搞”的问题都不是真的技术难题,而是因为对产品体系不够熟悉、踩坑经验不够多的缘故。像连接失败这类问题,五分钟能定位到原因的,往往能把半天时间耗在错误的方向上。如果你正在引入达梦数据库,或者正在把老系统迁过来,建议从第一天就把这几个习惯立起来:一是把初始化参数、实例配置、端口、备份策略这些基础信息做成运维文档,团队内共享;二是迁移前做一轮完整的SQL兼容性评估,宁可多花时间在早期,也不要等上线后才发现批量SQL跑不动;三是备份恢复的演练一定要做,不要只把备份命令挂着定时任务里就觉得万事大吉。最后想说,踩坑不可怕,怕的是踩过坑以后没有沉淀出一份自己的排查清单。这套经验能让你在下一次遇到同类问题时,直接跳过试错阶段。