1. 为什么这套组合值得搭:场景、收益与方案取舍
1.1 什么场景下需要 OpenEuler + Docker + Mycat + 达梦DM
最近一个内部项目要把原来的数据库层整体切换到国产化底座:操作系统被限定为OpenEuler,底层数据库换成了达梦DM8,但应用层一时半会儿改不动,而且业务方明确说后面会有分库分表和读写分离的诉求。这种局面很容易让人头疼——历史SQL体量不小,换数据库本身就够折腾,再去每个服务里单独改数据源和路由逻辑,工作量根本不可控。
这时候把Mycat放在应用和达梦之间,是最省事的解法。Mycat对外暴露标准MySQL协议端口,应用层不用关心背后是哪台达梦、有没有分片,只当它是一个数据库连接入口。达梦DM本身有Oracle兼容模式,一批从Oracle迁过来的SQL改动非常小。于是OpenEuler作为操作系统底座,Docker承载Mycat运行环境,Mycat负责路由和连接池,达梦DM8作为最终存储——这四样东西拼在一起,正好解决项目里最迫切的问题。
这篇内容适合下面几类人参考:
- 正在做国产化数据库替换,底层用了达梦DM,又想保留分库分表能力
- 对Mycat有基本概念,但没在非MySQL数据库上真正跑通过
- 需要一套可以复用的容器化Mycat部署模板,而不是照着文档从头猜
有一点要提前说清楚:Mycat的分片和读写分离设计初衷是面向MySQL体系,连达梦属于“适配”而非“原生”。最务实的打通方式是让Mycat走JDBC连接达梦,同时把方言层配置成Oracle兼容模式。这要求业务SQL里别出现太MySQL化的写法,反引号、LIMIT这类语法要尽量避免。这个限制最好一开始就跟应用团队讲明白,不然后面排查问题会非常被动。
1.2 物理机、K8s与Docker Compose的取舍
方案选择上,我最早也考虑过直接在OpenEuler物理机上装Mycat。表面上看只是解压一个安装包、配个JDK环境的事,实际上坑很多:OpenEuler的软件仓库和CentOS不同,JDK版本要自己处理;Mycat是Java进程,环境变量、服务托管、日志切割、多实例隔离全得自己来;更麻烦的是,在这台机器上好不容易折腾完,换一台机器又要重复一遍,可复制性几乎为零。
K8s方案在大型生产环境确实有价值,但为了一个Mycat单实例去部署一套K8s,控制面节点、网络插件、存储编排、Ingress全都要维护,对只有两三台机器的小团队来说,复杂度带来的负担远大于收益,属于典型的用大炮打蚊子。
最终我选的是Docker单容器方案,配合宿主机目录挂载。好处很直接:镜像构建一次,到哪都能启动;server.xml、schema.xml这些配置放在宿主机,改完restart容器就能生效;容器由守护进程托管,配合--restart=always,进程意外退出后能自动拉起。资源占用也可控,单容器内存上限压到1G左右,跑中小业务流量完全没有压力。后续所有步骤都按这个拓扑来:OpenEuler宿主机 + Docker容器 + 单台达梦DM8实例。
2. 开工前的环境准备:OpenEuler系统与达梦数据库基线
2.1 OpenEuler版本确认与系统基线检查
动手配置之前,先把系统看清楚,免得后面因为架构不匹配白折腾。我习惯先执行三条命令:
cat /etc/openEuler-release uname -m cat /etc/os-release我演示用的环境是OpenEuler 22.03 LTS,x86_64架构。如果是鲲鹏、飞腾这类arm64机器,流程整体一样,但后面选镜像、下载JDK和达梦驱动时必须对应aarch64版本,否则启动容器时会直接报exec format error,这是很多人第一个遇到的坑。
硬件方面,至少保证2核4G。Mycat启动时默认JVM参数分配的内存比较大,资源太紧张很容易触发OOM。如果机器规格确实有限,后面可以通过wrapper.conf把堆内存压到512M,这个在第6章会详细说。
然后是系统更新和基础工具:
dnf -y update dnf -y install tar gzip vim net-toolsOpenEuler不像CentOS那样预装了一堆调试工具,net-tools、telnet这些都要自己装。后面排查达梦端口连通性时,telnet几乎是必备工具,所以这句安装命令别省。
2.2 安装Docker并验证容器网络
OpenEuler自带仓库里有Docker包,安装方式很简单:
dnf install -y docker systemctl enable --now docker docker version如果仓库里搜不到docker,先确认是否启用了container相关仓库:
dnf repolist | grep -E "container|os|everything"安装完成后,我建议重点验证两件事,而不是急着去拉Mycat镜像。
第一,容器能不能正常跑起来。执行docker run --rm hello-world,如果报权限或SELinux问题,先解决环境问题,不然后面所有操作都会被干扰。OpenEuler默认可能开启SELinux,Docker在普通用户或bind mount场景下受到的限制比较多,排查时要有这个意识。
第二,宿主机到达梦数据库的网络链路是否通。因为容器默认走bridge网络,容器里访问127.0.0.1看见的是容器自己,不是宿主机。如果达梦就装在宿主机本机,容器里要访问达梦端口,要么填桥接网关IP(一般是172.17.0.1,也可能因配置不同),要么直接用host网络模式启动容器。从省事角度,前期起步阶段用host网络模式最直接,少一层网络排查;等完全跑通了,再根据安全要求决定要不要切回bridge。
如果倾向保留bridge网络,我会提前创建一个自定义网络:
docker network create --driver bridge mycat-net后续启动Mycat容器时加入这个网络,网络IP规划会更清楚。
2.3 达梦DM8实例与JDBC驱动准备
达梦不是这篇文章的主角,但它的状态直接决定Mycat能不能连上。我假设你已经有一个能用的DM8实例,至少需要确认三件事:
- 达梦服务是否启动:
systemctl status DmServiceDMSERVER,服务名会随实例名变化 - 监听端口是否还是默认的5236,有没有被防火墙或安全策略挡掉
- 用disql能不能正常登录:
/opt/dmdbms/bin/disql dmuser/Dm@123456@192.168.10.10:5236这里强调一个原则:disql能进,才轮到Mycat去连。很多人在达梦本身没通的情况下直接配置Mycat,最后日志里报一堆连接错误,还以为是自己配置写错了,实际上第一步就偏了。
达梦JDBC驱动是最容易忽略的环节。驱动文件通常在达梦安装目录的drivers/jdbc路径下,文件名类似DmJdbcDriver18.jar,对应的就是JDK8版本。Mycat跑在JDK8上,所以用这个版本最稳。不要随便拿一个JDK11或17的驱动塞进去,某些版本会直接报Class版本号不兼容。
驱动准备建议放到独立目录,比如/opt/mycat-dm/driver/,后面构建镜像或挂载时直接引用,思路会清晰很多。
另外,达梦的密码策略默认偏严。如果建库时开启了密码复杂度校验,Mycat里配置的密码必须满足达梦要求,否则启动后连接会被拒,报错信息往往只是一个笼统的认证失败,容易让人误判成驱动问题。
3. Mycat镜像选型与扩展:没有官方完美镜像怎么办
3.1 官方镜像的局限
Docker Hub上能找到Mycat相关镜像,我实际用过的是1.6.7.6版本。里面已经带了JDK8和Mycat标准目录结构,这点是省心的。但问题也很明显:lib目录下只有MySQL、Oracle、PostgreSQL等常见驱动,以及Mycat自身依赖,没有达梦驱动。
这意味着不能拿默认镜像直接跑。否则Mycat启动后日志会持续报ClassNotFoundException,指向dm.jdbc.driver.DmDriver。我第一次搭的时候就被这个误导了,以为是Mycat版本对达梦支持有问题,实际上就是缺一个jar包。
另外,官方镜像把conf目录放在镜像内部,直接启动后,宿主机上怎么改配置都不会影响容器。所以我的思路调整为:镜像只负责提供Mycat运行时和JDK环境,配置文件全部通过挂载方式从宿主机注入,达梦驱动直接打进镜像。这样既保证镜像可复用,又保证配置可修改。
拉取镜像时如果遇到超时,先检查宿主机DNS和镜像源配置,把镜像拉取问题和Mycat配置问题分开排查,别混在一起。
3.2 自建镜像与驱动注入
我最终采用的是自建镜像思路,Dockerfile非常简洁:
FROM mycat/mycat:1.6.7.6 COPY DmJdbcDriver18.jar /usr/local/mycat/lib/ ENV TZ=Asia/Shanghai这个方案的关键点在于:构建时把达梦驱动COPY进镜像的lib目录,Mycat启动时会扫描加载lib下所有jar,驱动天然可用。以后想改Mycat的server.xml、schema.xml,不需要重新构建镜像,只需要在docker run时用-v挂载覆盖对应文件即可。
如果官方镜像拉取不稳定,还可以退一步自己组装一个基础镜像:
FROM openjdk:8-jdk-slim ENV MYCAT_HOME=/usr/local/mycat # 把Mycat安装包解压到 /usr/local/mycat 目录 COPY DmJdbcDriver18.jar $MYCAT_HOME/lib/ WORKDIR $MYCAT_HOME EXPOSE 8066 9066 CMD ["./bin/mycat", "console"]用openjdk:8为底座的方案更干净,但要额外处理Mycat安装包的拷贝,比官方镜像方案多几步。如果只是想快速跑通,优先用官方镜像加COPY驱动的方案,少踩生态依赖的坑。
构建命令:
docker build -t mycat-dm:v1 .构建完成后,建议先验证驱动确实在镜像里:
docker run --rm mycat-dm:v1 ls -l /usr/local/mycat/lib | grep dm看不到文件就直接检查Dockerfile里的路径,别等启动报错再回头查。
3.3 配置目录规划与挂载要点
容器跑起来后,配置文件必须能随时改。我的目录规划是这样的:
/opt/mycat-dm/ ├── conf/ │ ├── server.xml │ ├── schema.xml │ └── rule.xml ├── logs/ └── driver/启动时把conf下这几个文件和logs目录挂载到容器内。这里有个细节:Mycat的conf目录里除了这三个文件还有其它配置,如果直接把整个conf目录挂载出去,宿主机端必须补全全部文件,否则Mycat启动会缺配置。所以我推荐单文件挂载,只覆盖自己关心的三个文件,其余保持镜像内原样。
单文件挂载有一个隐性问题:宿主机上的目标文件必须存在。如果文件不存在,Docker会直接创建一个目录,导致容器内路径被目录占住,Mycat读取时行为很怪。所以启动容器前先touch好宿主机文件,再确认路径正确。
另一个坑是SELinux。OpenEuler默认enforcing状态下,bind mount文件进容器后,容器内进程可能没有读权限。表现是Mycat启动时日志里报读不到schema.xml,或者加载后内容为空。急于验证时可以先setenforce 0临时关闭,生产上更稳妥的是给挂载目录打SELinux标签,或者在运行时加:Z标记。这一步不做深究,但排查方向一定要记着。
4. 配置核心:server.xml、schema.xml、rule.xml怎么改
4.1 server.xml:Mycat逻辑账号与权限
server.xml管的是Mycat自身的用户、端口和系统参数。先把用户说清楚。
Mycat对外有两个端口:8066是数据端口,应用连接用它;9066是管理端口,运维监控用它。这两个端口建议使用不同账号,不要把数据账号和管理账号混在一起。例如:
<user name="mycat" defaultAccount="true"> <property name="password">Mycat@123</property> <property name="schemas">dmtest</property> </user> <user name="monitor" defaultAccount="false"> <property name="password">Monitor@123</property> <property name="schemas">dmtest</property> <property name="readOnly">true</property> </user>如果混用,对9066执行show @@database这类命令时,返回的权限提示容易让人误读。数据账号只负责业务SQL,管理账号只做只读监控,权限边界清晰,后面审计也方便。
system标签下,我建议一开始就关注这几个参数:
<system> <property name="charset">utf8</property> <property name="processors">4</property> <property name="frontSocketSoTimeout">300000</property> <property name="backSocketSoTimeout">300000</property> <property name="maxStringLiteralLength">65535</property> </system>frontSocketSoTimeout是前端客户端连接超时,backSocketSoTimeout是Mycat到达梦的socket超时。达梦处理某些复杂查询如果耗时偏长,默认超时值可能需要调大,否则查询还没返回,连接就被掐断,应用层会看到socket read timeout。这类问题在压测阶段最容易暴露,提前把超时放开一些,能少很多半夜告警。
4.2 schema.xml:达梦数据源的正确写法
schema.xml是整个配置里最需要小心的文件。先用一个逻辑库对应一个达梦节点的基础写法:
<mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="dmtest" checkSQLschema="false" sqlMaxLimit="-1" dataNode="dn1"> </schema> <dataNode name="dn1" dataHost="dmhost1" database="DMUSER" /> <dataHost name="dmhost1" maxCon="50" minCon="5" balance="0" writeType="0" dbType="oracle" dbDriver="jdbc" switchType="1"> <heartbeat>select 1</heartbeat> <connectionInitSql>select 1</connectionInitSql> <writeHost host="dmM1" url="jdbc:dm://192.168.10.10:5236" user="dmuser" password="Dm@123456"> </writeHost> </dataHost> </mycat:schema>几个关键点逐个说。
第一,dataNode的database属性,在达梦里对应的是“模式名”,不是达梦的库名。达梦建用户后默认生成同名模式,如果达梦侧用户叫DMUSER,这里就写DMUSER,注意大小写。如果写错,连接本身不会报错,但执行SQL时会出现“模式不存在”,这个错非常容易让人绕远路。
第二,dbType和dbDriver的组合。Mycat本身没有dm这个方言类型,实际通路是“达梦提供JDBC驱动 + Mycat使用Oracle方言解析SQL”。所以这里写dbType="oracle"、dbDriver="jdbc"比较合适。达梦在Oracle兼容模式下,对分页、序列、伪列的处理与Oracle基本一致,这套组合我实测可以跑通。如果你的达梦实例初始化时选的是MySQL兼容模式,那dbType要改成mysql,千万不要跟实例初始化模式拧着来。
第三,dbDriver="jdbc"意味着Mycat不走自身原生连接协议,而是用java.sql.DriverManager去连url。这个url必须写成达梦JDBC格式:jdbc:dm://IP:端口。Mycat 1.6.7.6的jdbc驱动识别表已经认识jdbc:dm前缀;如果启动时日志提示无法判断驱动类,优先确认Mycat版本,看看使用的版本里有没有达梦驱动映射。
第四,heartbeat和connectionInitSql都用select 1就够,达梦直接支持。在Oracle方言模式下,Mycat也许会自动补dual,但显式写select 1更不容易出歧义。
第五,读写分离。这里的balance="0"表示不开启读写分离,所有流量都走writeHost。如果只有一台达梦,这个配置符合预期。后续如果做了主备或只读副本,再调整balance并新增readHost,前面先把单库链路跑通是更稳妥的顺序。
4.3 rule.xml与分片规则预留
单库接入其实用不到分片规则,schema里可以完全不声明table。但考虑到Mycat的定位,我建议把mod-long规则提前留在rule.xml里,后续接入分表直接启用:
<tableRule name="mod-long"> <rule> <columns>id</columns> <algorithm>mod-long</algorithm> </rule> </tableRule> <function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <property name="count">2</property> </function>将来把业务表拆到两个达梦节点时,先在schema的schema标签里加一行:
<table name="bus_order" dataNode="dn1,dn2" rule="mod-long" />然后新增dataNode dn2,指向另一台达梦实例即可。注意,分片键必须是业务SQL中稳定出现的等值查询条件,否则会走全节点扫描。这个约束要提前告诉业务开发,数据量小的时候看不出问题,数据量一大,一条不带分片键的查询打遍所有节点,延迟和连接占用都会明显恶化。
5. 容器启动、连通性验证与常见故障排查链路
5.1 启动容器与首次连接验证
镜像构建好、配置改完后,用下面命令启动:
docker run -d --name mycat-dm \ --network mycat-net \ -p 8066:8066 \ -p 9066:9066 \ -v /opt/mycat-dm/conf/server.xml:/usr/local/mycat/conf/server.xml \ -v /opt/mycat-dm/conf/schema.xml:/usr/local/mycat/conf/schema.xml \ -v /opt/mycat-dm/conf/rule.xml:/usr/local/mycat/conf/rule.xml \ -v /opt/mycat-dm/logs:/usr/local/mycat/logs \ --restart=always \ mycat-dm:v1启动后先看日志,这一步能判断80%的问题:
docker logs -f mycat-dm正常启动时,日志末尾会出现类似MyCAT Server startup successfully的字样,并监听8066和9066端口。如果日志一直停在连接后端节点失败,就进入排障流程,别急着重启,先看具体报错。
客户端连接时,因为Mycat对外是MySQL协议,直接用mysql客户端即可,不要求宿主机装了完整MySQL服务。命令:
mysql -h 127.0.0.1 -P 8066 -u mycat -pMycat@123登录后按顺序执行:
SHOW DATABASES; USE dmtest; SELECT 1;能看到dmtest且SELECT 1返回1,说明整条链路已经通了。接着可以试一张真实表:
SELECT COUNT(*) FROM dmuser.bus_order;SQL前面带了模式名,只要schema.xml的database属性配的是DMUSER,这种写法没有问题。
管理端口同样用mysql客户端协议连:
mysql -h 127.0.0.1 -P 9066 -u mycat -pMycat@123 show @@database; show @@datasource;其中show @@datasource会列出Mycat到达梦的实际连接数、空闲连接数、心跳状态,这是验证后端连接池最直观的命令,建议第一时间养成看它的习惯。
5.2 连接失败排查链路
第一次搭时最容易踩三个坑,正好说说我的完整排查链路。
第一类是达梦侧连接失败。日志里会出现通信相关错误和Error Number,大概率是达梦端口不通或账号错误。我当时的排查顺序是:先用disql确认达梦本身可登录,再telnet从宿主机到达梦端口,最后才怀疑Mycat。如果达梦就在宿主机本机且用bridge网络,容器里不能填127.0.0.1,要填宿主机IP或桥接网关IP。这一步走错了,后面所有配置都是白改。
第二类是驱动加载失败。日志报ClassNotFoundException: dm.jdbc.driver.DmDriver,基本就是三个原因:驱动jar没打进去、COPY路径不对、或者宿主机挂载把镜像内lib目录覆盖了。用下面命令验证:
docker exec mycat-dm ls -l /usr/local/mycat/lib | grep dm然后可以试着加载类:
docker exec mycat-dm java -cp /usr/local/mycat/lib/DmJdbcDriver18.jar dm.jdbc.driver.DmDriver这条命令不会输出成功信息,但不报ClassNotFoundException就说明驱动类能被加载。
第三类是连接成功但建连接池失败。日志里出现Unknown database或Schema not exists,这基本上是dataNode的database属性和达梦侧schema名不一致造成的,去检查大小写和空格即可,不要怀疑Mycat本身。
还有一个隐蔽问题值得单独说:驱动版本。不同版本的DmJdbcDriver对TLS等参数默认行为不同,替换驱动后同样的配置可能突然连不上,出现SSL握手类错误。如果没有明确原因,优先使用达梦安装目录自带的驱动版本,不要因为“高版本更好”去网上随便替换。
6. 生产落地前我建议你先做这几件事
6.1 连接数、内存与监控参数收紧
Mycat默认JVM堆内存可能到2G,对小规格虚拟机不友好。改/usr/local/mycat/conf/wrapper.conf里的wrapper.java.initmemory和wrapper.java.maxmemory,可以压到512M/1024M。镜像内修改的话要重建镜像,更省事的做法是把wrapper.conf也用-v单文件挂载出来,改完重启容器。
达梦端的连接数也要提前规划。schema.xml里maxCon是dataHost到后端的整体最大连接数,minCon是空闲连接数,多个dataHost时还要乘以节点数。达梦实例默认连接数上限通常是500,如果只有一台达梦,把maxCon设为50比较稳妥,别上来就配200,连接打满后达梦会拒绝新会话,连锁影响其他应用。
6.2 账号权限最小化与配置备份
达梦业务账号建议单独创建,只给Mycat需要访问的schema授权,比如SELECT、INSERT、UPDATE、DELETE这一类基础权限,不要直接拿SYSDBA跑业务。安全审计时,业务账号权限过大是经常被点名的,这里提前做小权限配置,后面省很多解释成本。
conf目录下的server.xml和schema.xml,每次修改前先cp一份带日期的备份。Mycat没有配置热加载,改完必须重启容器才生效。重启前自己过一遍改动点,避免容器一重启直接挂掉,然后又要临时回滚,手忙脚乱。
6.3 把SQL方言的约定提前交给应用团队
最后这条虽然不属于安装层面,但比安装本身更重要。Mycat连接达梦走的是Oracle兼容方言,业务SQL要守住几条底线:不要用MySQL的LIMIT写法,用达梦支持的ROWNUM或标准分页方式;不要用反引号包裹表名;表名尽量用大写或显式加双引号指定模式名。团队里如果同时有MySQL老项目和这个新入口,开发规范里一定要写明这些差异。
我在实际项目里遇到最多的线上问题,不是Mycat连不上达梦,而是开发在SQL里混用了两套数据库方言,Mycat解析后送到达梦直接报语法错误。这类问题查起来最耗时间,因为报错位置都在达梦侧,但根源却在应用SQL写法上。所以部署完成不是终点,把这份方言约束同步给每一个写SQL的人,才是整条链路稳定跑下去的关键。