达梦数据库(DM8)部署与 MySQL 迁移
本文覆盖两件事:
- 部署——用 Docker(本地或阿里云 ECS)跑起一个达梦 DM8 实例,并完成兼容模式、客户端驱动、连通性配置;
- MySQL 迁移到达梦——把平台原有的 MySQL 建表 DDL 通过正则批量转换成达梦 SQL,并用脚本灌库。
平台的元数据库连接统一通过
DM_SERVICE环境变量配置,URI 形如dm+dmPython://SYSDBA:<密码>@<host>:<port>?schema=kubeflow(见install/docker/docker-compose.yml:57、myapp/config.py:484)。 三类元数据库(MySQL / PostgreSQL / 达梦)的总体选型与切换说明见本段「元数据库配置.md」。
一、部署
推荐两种方式,生产优先用阿里云上常驻实例:阿里云上的参数已调优、性能好,部署完只要给服务器开外网 IP、安全组放开端口即可,不建议用本地自建实例顶生产。
1.1 本地 Docker 运行(测试用)
容器内端口固定为
5236,不能改(参考 https://blog.csdn.net/chou342175867/article/details/145902563 )。对外可映射成任意 NodePort,下例映射到30226。
# 清理旧数据与旧容器
rm -rf /opt/data
docker rm dm8_test -f
# 启动 DM8(容器内 5236 -> 宿主 30226)
docker run -d -p 30226:5236 --restart=always --name=dm8_test --privileged=true \
-v /opt/data:/opt/dmdbms/data \
-e LD_LIBRARY_PATH=/opt/dmdbms/bin \
-e PAGE_SIZE=16 -e EXTENT_SIZE=32 -e LOG_SIZE=1024 -e UNICODE_FLAG=1 \
-e INSTANCE_NAME=dm8_test \
ccr.ccs.tencentyun.com/cube-studio/dm8:dm8_20250206_rev257733_x86_rh6_64
1.2 阿里云上运行(生产)
阿里云镜像启动时直接挂载配置文件会失败,只能起来后再进容器改(见下文兼容模式)。
docker run -d -p 30226:5236 --restart=always --name=dm8_test --privileged=true \
-v /opt/data:/opt/dmdbms/data \
-e SYSDBA_PWD=19910101a \
-e COMPATIBLE_MODE=4 \
-e LD_LIBRARY_PATH=/opt/dmdbms/bin \
-e PAGE_SIZE=16 -e EXTENT_SIZE=32 -e LOG_SIZE=1024 -e UNICODE_FLAG=1 \
-e INSTANCE_NAME=dm8_test \
-e PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
-e CASE_SENSITIVE=0 -e LENGTH_IN_CHAR=0 -e BUFFER=30000 -e MODE=dmsingle \
-e CHG_PASSWD=dameng777 -e DM_USER_PWD=dameng777 -e BLANK_PAD_MODE=0 \
-e LANG=en_US.UTF-8 \
ccr.ccs.tencentyun.com/cube-studio/dm8:dm8_20241022_rev244896_x86_rh6_64 /opt/startup.sh
常用启动参数说明:
| 环境变量 | 含义 |
|---|---|
SYSDBA_PWD / CHG_PASSWD / DM_USER_PWD |
SYSDBA / 修改后口令 / 业务用户口令 |
COMPATIBLE_MODE=4 |
兼容模式,4 为兼容 MySQL(迁移 MySQL DDL 必须开) |
CASE_SENSITIVE=0 |
大小写不敏感(对象名不区分大小写) |
LENGTH_IN_CHAR=0 / 1 |
varchar 长度按字节(0)还是按字符(1)计 |
PAGE_SIZE / EXTENT_SIZE / LOG_SIZE / BUFFER |
页大小/簇大小/日志大小/缓冲,性能相关 |
UNICODE_FLAG=1 |
启用 Unicode |
MODE=dmsingle |
单机模式 |
注意:源
install/kubernetes/mysql/dm/kubeflow/readme.md:10的阿里云示例里-e COMPATIBLE_MODE=4写了两次,是冗余笔误(已核对源文件),本文已合并为一个,效果不变。
1.3 兼容模式(进容器改 dm.ini)
如果启动时没设或要调整兼容模式,进容器改配置文件后重启:
docker exec -it dm8_test /bin/bash
vi /opt/dmdbms/data/DAMENG/dm.ini
# 修改/确认以下两项
COMPATIBLE_MODE = 4
LENGTH_IN_CHAR = 1 # 或者 NVARCHAR_LENGTH_IN_CHAR = 1
docker restart dm8_test
1.4 查看日志与口令
docker logs -f dm8_test # 看日志里是否 success
docker inspect dm8_test | grep PWD # 注意:口令可能被改写,例如变成 SYSDBA_dm001
进容器用 disql 连:
docker exec -it dm8_test bash
source /etc/profile
cd /opt/dmdbms/bin/
./disql SYSDBA/SYSDBA_dm001 # 用 docker inspect 看到的真实口令
1.5 客户端驱动安装
平台后端通过 dm+dmPython 方言连接达梦,需要两个 Python 包:
| 包 | 平台镜像内版本(以 install/docker/requirements.txt 为准) |
|---|---|
dmPython |
2.5.30(requirements.txt:64,达梦驱动) |
dmSQLAlchemy |
1.4.46(requirements.txt:65,达梦 SQLAlchemy 方言) |
安装方式:
# Linux(有 wheel,直接 pip)
pip install dmPython==2.5.30
pip install dmSQLAlchemy==1.4.46
# 其他平台(无 wheel,源码安装)
# 1) 到 https://eco.dameng.com/download/ 下载“达梦 Python 接口源码”
# 2) dmPython:在解压出的 dmPython 目录下执行 python setup.py install
# 3) dmSQLAlchemy:在 dmSQLAlchemy/dmSQLAlchemy1.4 目录下执行 python setup.py install
注意:源
readme.md:32写的是pip install dmSQLAlchemy==1.4.39,而平台镜像install/docker/requirements.txt:65钉的是1.4.46(已核对)。本文以requirements.txt为准写成1.4.46,与服务端版本保持一致。注:PC(非 Linux)上
dmPython==2.5.30没有 wheel 装不上。不需要达梦的开发者可临时绕过(见install/docker/README.md:144):grep -vE "^(dmPython|dmSQLAlchemy)" install/docker/requirements.txt > /tmp/req.txt pip install -r /tmp/req.txt
1.6 连通性测试(DBeaver / disql)
用 DBeaver 等客户端连上后,建 schema、建测试表验证:
create schema KUBEFLOW AUTHORIZATION SYSDBA;
CREATE TABLE KUBEFLOW.ACCOUNTS (
ACCOUNT_ID INT PRIMARY KEY,
ACCOUNT_NAME VARCHAR(100),
BALANCE DECIMAL(15,2)
);
注意 schema 大小写:
readme.md用大写KUBEFLOW建 schema,而平台连接串docker-compose.yml:57用的是小写schema=kubeflow,迁移脚本to_dm.md也以小写"kubeflow"建 schema。配合启动参数CASE_SENSITIVE=0(大小写不敏感)时二者等价;若实例区分大小写,务必让 schema 名与DM_SERVICE里的schema=完全一致。
1.7 平台侧行为差异(重要)
达梦作为元数据库时,平台不走 Alembic 迁移,而是用 db.create_all() 直接建表:
myapp/config.py:484-488:检测到DM_SERVICE后清空migrations/versions目录;install/docker/entrypoint.sh:25-28:仅当DM_SERVICE为空时才执行myapp db upgrade;达梦走myapp/create_db.py里的db.create_all()建表。
这也是为什么从 MySQL 迁移到达梦时,需要手工把 DDL 转换好(下一节),而不是依赖迁移文件。
二、MySQL 迁移到达梦
思路:把平台原有的 MySQL 建表脚本(kubeflow.sql)用编辑器/IDE 的正则替换,按下列规则批量改写成达梦语法,再用 to_dm.py 灌进达梦。涉及文件都在 install/kubernetes/mysql/dm/kubeflow/ 目录下(kubeflow.sql 建表、data.sql 初始数据、to_dm.py 执行器)。
2.1 正则批量转换规则
按顺序在编辑器里做正则「查找→替换」($1/$2 为捕获组):
清理无关语句与注释
| 查找(正则) | 替换为 |
|---|---|
^SET.* 开头的行 |
删除 |
^DROP.* 开头的行 |
删除 |
`(反引号) |
"(双引号) |
BEGIN; |
空 |
COMMIT; |
空 |
INSERT INTO.* |
空 |
^--.*(行注释) |
空 |
COMMENT.*?,\n |
,\n |
类型与子句改写
| 查找(正则) | 替换为 |
|---|---|
ENGINE=InnoD.* |
STORAGE(ON "MAIN", CLUSTERBTR) ; |
AUTO_INCREMENT |
IDENTITY(1,1) |
UNIQUE KEY "(.*?)" |
CONSTRAINT TABLE1_$1 UNIQUE |
COLLATE utf8mb4_general_ci |
空 |
tinyint(1) |
TINYINT |
mediumtext |
CLOB |
datetime DEFAULT NULL |
TIMESTAMP(0) |
enum.*?\) |
varchar(100) |
varchar(64)(可选) |
VARCHAR(64 CHAR) |
主键 / 外键改写
| 查找(正则) | 替换为 |
|---|---|
PRIMARY KEY |
CONSTRAINT "TABLE1_PRIMARY" NOT CLUSTER PRIMARY KEY |
FOREIGN KEY(.*?), |
FOREIGN KEY$1 WITH INDEX, |
FOREIGN KEY(.*?)\n\) |
FOREIGN KEY$1 WITH INDEX\n) |
把每个表压成一行(便于后续整表替换)
需要执行很多遍,直到没有可匹配的行——目标是把跨多行的 CREATE TABLE 折叠成一行:
| 查找(正则) | 替换为 |
|---|---|
CREATE TABLE (.*)\n |
CREATE TABLE $1 |
再执行一遍把表末尾的右括号接上:
| 查找(正则) | 替换为 |
|---|---|
CREATE TABLE (.*)\n\) |
CREATE TABLE $1 ) |
把占位表名 TABLE1 换回真实表名
| 查找(正则) | 替换为 |
|---|---|
CREATE TABLE "(.*?)" (.*)TABLE1 |
CREATE TABLE "$1" $2$1 |
把索引创建从表内提取到表外
| 查找(正则) | 替换为 |
|---|---|
, *KEY |
, KEY |
CREATE TABLE "(.*?)" (.*?), KEY "(.*?)" (.*?),(.*) |
CREATE TABLE "$1" $2, $5\nCREATE OR REPLACE INDEX "$1_$3" ON "$1"$4 STORAGE(ON "MAIN", CLUSTERBTR) ; |
幂等与达梦保留字处理
| 查找(正则) | 替换为 |
|---|---|
CREATE TABLE |
CREATE TABLE IF NOT EXISTS |
user |
"user" |
public |
"public" |
table(区分大小写) |
"table" |
cluster |
"cluster" |
resource |
"resource" |
date |
"date" |
skip |
"skip" |
kubeflow.dimension (维表,转小写) |
kubeflow."dimension" |
kubeflow.model (训练模型表,转小写) |
kubeflow."model" |
\n\n(连续空行) |
\n |
2.2 重建 schema 并设置当前 schema
在转换好的 SQL 顶部加上:
DROP SCHEMA "kubeflow" CASCADE;
CREATE SCHEMA "kubeflow" AUTHORIZATION "SYSDBA";
SET SCHEMA "kubeflow";
2.3 批量灌库
用 to_dm.py 逐行执行转换后的 kubeflow.sql 与 data.sql:
python to_dm.py
to_dm.py 要点(见 install/kubernetes/mysql/dm/kubeflow/to_dm.py):
- 连接串为
dm+dmPython://SYSDBA:<密码>@<host>:<port>?schema=KUBEFLOW(脚本里是写死的示例值,实际请改成你的实例); - 用
dmPython.connect(... autoCommit=True)建连接,逐行cursor.execute(sql); - 单行执行失败会被
try/except吞掉继续(容忍重复建表等报错),先执行kubeflow.sql再执行data.sql。
注意:
to_dm.py:2写死的示例端口是30236,而部署小节docker run映射的是30226。这两个都是示例值,实际以你映射的 NodePort 为准。
2.4 为 ab_ 开头的权限表建序列
达梦下自增主键改成了 IDENTITY 或序列。Flask-AppBuilder 的权限/用户表(ab_ 前缀)需要手工补建序列:
CREATE SEQUENCE "kubeflow"."ab_user_id_seq" INCREMENT BY 1 MAXVALUE 9223372036854775807 MINVALUE 1;
CREATE SEQUENCE "kubeflow"."ab_role_id_seq" INCREMENT BY 1 MAXVALUE 9223372036854775807 MINVALUE 1;
CREATE SEQUENCE "kubeflow"."ab_user_role_id_seq" INCREMENT BY 1 MAXVALUE 9223372036854775807 MINVALUE 1;
CREATE SEQUENCE "kubeflow"."ab_permission_id_seq" INCREMENT BY 1 MAXVALUE 9223372036854775807 MINVALUE 1;
CREATE SEQUENCE "kubeflow"."ab_view_menu_id_seq" INCREMENT BY 1 MAXVALUE 9223372036854775807 MINVALUE 1;
CREATE SEQUENCE "kubeflow"."ab_permission_view_id_seq" INCREMENT BY 1 MAXVALUE 9223372036854775807 MINVALUE 1;
CREATE SEQUENCE "kubeflow"."ab_permission_view_role_id_seq" INCREMENT BY 1 MAXVALUE 9223372036854775807 MINVALUE 1;
2.5 禁掉 fab 自动建权限的部分
迁移完成后,fab 创建权限的那些初始化步骤需要禁掉(否则会与已迁移的权限数据冲突)。
⚠️ 待核实:原文
to_dm.md:84只写了「fab 创建权限的那些部分还要禁掉」,未给出具体改哪段代码。结合install/docker/entrypoint.sh里的myapp fab create-admin与myapp init,禁用范围以实际代码为准,请确认。
三、连接配置回填
迁移完成后,平台通过 DM_SERVICE 连接达梦(在 docker-compose.yml 或 K8s 环境变量里设置):
# install/docker/docker-compose.yml 示例(注意去掉行首注释启用)
DM_SERVICE: 'dm+dmPython://SYSDBA:Sysdba001@192.168.3.22:5236?schema=kubeflow'
三类元数据库的优先级(MYSQL_SERVICE > POSTGRESQL_SERVICE/KINGBASE_SERVICE > DM_SERVICE)与切换细节见本段「元数据库配置.md」。