DM8 运维实战:数据迁移、空间验证、DEM 部署与升级异常排查
DM8 数据库的日常运维并不只是实例启停和 SQL 执行。在实际环境中,经常会涉及跨 Schema 数据迁移、逻辑备份恢复、迁移前后空间校验、监控平台部署以及版本升级后的兼容性问题排查。
本文基于实际操作场景,整理一套较完整的 DM8 运维实践过程,主要涉及四个方面:
- 跨 Schema 表数据迁移与结果校验;
dexp/dimp迁移后表和索引空间差异验证;- DM8 DEM 企业管理平台部署及数据库纳管;
- DM8 升级后
DBMS_LDAP / SF_LDAP_INIT系统包异常排查。
重点不是单纯记录命令,而是说明这些操作之间的验证逻辑,以及遇到异常时应该如何缩小问题范围。
1. 跨 Schema 数据迁移:先确认对象,再执行数据导入
数据库中进行跨 Schema 数据迁移时,一个比较容易出现的问题是直接执行:
sql
INSERT INTO target_schema.table_name
SELECT *
FROM source_schema.table_name;
这种方式只有在目标表已经正确存在,并且源表和目标表结构完全一致时才比较安全。
如果目标 Schema 中还不存在对应表,或者目标表 DDL 和源表存在差异,就需要先处理数据库对象,再进行数据导入。
以一次实际迁移为例,源表位于:
text
CTMCMP.CMP_FOREIGNPAYMENT_PARALLEL
目标表位于:
text
YONBIP_FI_CTMFC.CMP_FOREIGNPAYMENT_PARALLEL
操作前首先检查源表数据量:
sql
SELECT COUNT(*)
FROM CTMCMP.CMP_FOREIGNPAYMENT_PARALLEL;
然后确认目标 Schema 中是否已经存在同名表:
sql
SELECT OWNER, TABLE_NAME
FROM ALL_TABLES
WHERE OWNER = 'YONBIP_FI_CTMFC'
AND TABLE_NAME = 'CMP_FOREIGNPAYMENT_PARALLEL';
这个检查不能省略。
如果目标端已经存在同名对象,后面是覆盖、追加还是放弃迁移,需要重新确定处理方式。如果目标端不存在,则可以继续获取源表真实 DDL。
DM8 可以通过 DBMS_METADATA.GET_DDL 获取对象定义:
sql
SELECT DBMS_METADATA.GET_DDL(
'TABLE',
'CMP_FOREIGNPAYMENT_PARALLEL',
'CTMCMP'
)
FROM DUAL;
实际取出的建表语句包含源 Schema、字段定义、主键以及存储属性,例如:
sql
CREATE TABLE "CTMCMP"."CMP_FOREIGNPAYMENT_PARALLEL"
(
"EXTEND_GJ_IS_CA" VARCHAR(36),
"EXTEND_GJ_USE_U_KEY" VARCHAR(36),
"YTENANT_ID" VARCHAR(256) NOT NULL,
"ID" BIGINT NOT NULL,
"extend_gj_pay_extend" VARCHAR(200) DEFAULT NULL,
CONSTRAINT "CONS134286058"
NOT CLUSTER PRIMARY KEY("ID")
)
STORAGE(ON "MAIN", CLUSTERBTR);
目标端创建时,需要将 Schema 修改为目标 Schema,同时注意约束名不要与已有约束产生冲突:
sql
CREATE TABLE "YONBIP_FI_CTMFC"."CMP_FOREIGNPAYMENT_PARALLEL"
(
"EXTEND_GJ_IS_CA" VARCHAR(36),
"EXTEND_GJ_USE_U_KEY" VARCHAR(36),
"YTENANT_ID" VARCHAR(256) NOT NULL,
"ID" BIGINT NOT NULL,
"extend_gj_pay_extend" VARCHAR(200) DEFAULT NULL,
CONSTRAINT "PK_CMP_FOREIGNPAYMENT_PARALLEL"
NOT CLUSTER PRIMARY KEY("ID")
)
STORAGE(ON "MAIN", CLUSTERBTR);
这一步的关键点不是"重新写一份类似的建表 SQL",而是尽可能从源端获取真实 DDL,再进行必要修改,从而减少字段类型、默认值、主键和存储属性不一致的问题。
目标表创建完成之后,再执行数据迁移:
sql
INSERT INTO YONBIP_FI_CTMFC.CMP_FOREIGNPAYMENT_PARALLEL
SELECT *
FROM CTMCMP.CMP_FOREIGNPAYMENT_PARALLEL;
COMMIT;
本次迁移实际导入了 4668 条数据。完成后分别查询源端和目标端行数:
sql
SELECT COUNT(*)
FROM CTMCMP.CMP_FOREIGNPAYMENT_PARALLEL;
SELECT COUNT(*)
FROM YONBIP_FI_CTMFC.CMP_FOREIGNPAYMENT_PARALLEL;
必要时还可以对数据内容进一步进行抽样检查:
sql
SELECT *
FROM CTMCMP.CMP_FOREIGNPAYMENT_PARALLEL;
SELECT *
FROM YONBIP_FI_CTMFC.CMP_FOREIGNPAYMENT_PARALLEL;
本次操作最终通过源、目标两侧数据量及内容进行核对。
所以对于跨 Schema 数据导入,一个比较稳妥的执行顺序应该是:
text
检查源数据
↓
确认目标对象状态
↓
获取源端真实 DDL
↓
创建目标对象
↓
执行数据迁移
↓
COMMIT
↓
源目标数据核对
相比单纯执行一条 INSERT,这套流程更适合实际数据库环境。
2. dexp/dimp 迁移后空间不一致问题验证
除了直接 SQL 迁移,DM8 中还经常使用 dexp 和 dimp 完成 Schema 或数据库对象的逻辑迁移。
实际操作中出现过一个比较有代表性的问题:
同一张表经过
dexp/dimp迁移以后,数据量、表结构和索引结构都一致,但源端和目标端的物理空间占用却明显不同。
为了判断这种差异是不是迁移异常,需要构造一个尽可能可控的测试环境。
2.1 为什么不能直接比较两个现有业务库
如果直接拿两个业务环境进行空间比较,会存在很多不可控变量,例如历史数据变化、索引使用状态、数据库初始化参数以及对象本身的长期运行状态。
因此测试时按照实际业务表 cmp_bankaccount_realtimebalance 的结构构造独立表,并建立独立的测试 Schema:
text
CMP_TEST_SRC
CMP_TEST_DST
迁移路径设计为:
text
CMP_TEST_SRC.CMP_BAL_SRC
│
│ dexp
▼
cmp_test.dmp
│
│ dimp
│ REMAP_SCHEMA
▼
CMP_TEST_DST.CMP_BAL_SRC
同时不直接使用原业务 Schema,避免测试过程影响业务对象。
但在构造测试实例之前,还有一个很重要的问题:数据库初始化参数必须尽可能一致。
尤其是验证物理空间大小时,PAGE_SIZE、EXTENT_SIZE 等参数会影响实际空间分配。
可以在源环境执行:
sql
SELECT SF_GET_PAGE_SIZE() / 1024 AS PAGE_SIZE_KB;
SELECT SF_GET_EXTENT_SIZE() AS EXTENT_SIZE;
SELECT SF_GET_CASE_SENSITIVE_FLAG() AS CASE_SENSITIVE;
SELECT SF_GET_UNICODE_FLAG() AS CHARSET_FLAG;
再按照实际参数初始化测试实例:
bash
./dminit \
PATH=/data/testdata \
DB_NAME=CMPTEST \
INSTANCE_NAME=CMPTEST \
PORT_NUM=5240 \
PAGE_SIZE=32 \
EXTENT_SIZE=32 \
CASE_SENSITIVE=N \
CHARSET=1 \
SYSDBA_PWD='<PASSWORD>' \
SYSAUDITOR_PWD='<PASSWORD>'
PAGE_SIZE 和 EXTENT_SIZE 属于建库阶段确定的重要参数,因此如果两个实例本身的参数不同,后面得出的空间比较结果就失去了部分参考价值。
2.2 测试表和索引应尽可能还原真实结构
测试表不能为了方便随意简化。
本次验证使用了实际业务表的字段结构,并保留:
sql
NOT CLUSTER PRIMARY KEY("id")
以及:
sql
STORAGE(ON "MAIN", CLUSTERBTR)
等存储定义。
普通业务索引也按照原环境中的字段、字段顺序、ASC/DESC 和存储属性进行创建。例如:
sql
CREATE OR REPLACE INDEX
"CMP_TEST_SRC"."CMP_BANKACCOUNT_REALTIMEBALANCE_I_I_YTENANTID_BANKACCOUNT"
ON "CMP_TEST_SRC"."CMP_BAL_SRC"
(
"ytenant_id" ASC,
"enterprisebankaccount" ASC
)
STORAGE(ON "MAIN", CLUSTERBTR)
WITHOUT CLU_REC_ADDR;
再例如一个字段更多的联合索引:
sql
CREATE OR REPLACE INDEX
"CMP_TEST_SRC"."IDX_CMP_231214"
ON "CMP_TEST_SRC"."CMP_BAL_SRC"
(
"balancedate" ASC,
"ytenant_id" ASC,
"first_flag" ASC,
"banktype" ASC,
"accentity" ASC,
"acctbal" ASC,
"datasource" ASC,
"currency" ASC,
"enterprisebankaccount" ASC
)
STORAGE(ON "MAIN", CLUSTERBTR)
WITHOUT CLU_REC_ADDR;
测试过程中建立了 10 个普通业务索引,同时主键 UNIQUE 索引和 CLUSTER 索引由相关定义自动产生。
这样做的目的很明确:如果最终需要分析索引空间差异,就必须尽量保证索引定义本身不是变量。
2.3 构造 100 万行测试数据
为了让空间差异具有可观察性,测试表插入 100 万行数据。
数据生成方式类似:
sql
INSERT INTO CMP_TEST_SRC.CMP_BAL_SRC
(
"id",
"tenant_id",
"ytenant_id",
"accentity",
"enterprisebankaccount",
"currency",
"balancedate",
"first_flag",
"banktype",
"acctbal",
"datasource",
"isconfirm",
"create_time",
"pubts"
)
SELECT
LEVEL,
MOD(LEVEL, 1000) + 1,
'YT_' || TO_CHAR(MOD(LEVEL, 10000)),
'ACC_' || TO_CHAR(MOD(LEVEL, 50000)),
'BANK_' || TO_CHAR(LEVEL),
CASE
WHEN MOD(LEVEL, 3) = 0 THEN 'CNY'
WHEN MOD(LEVEL, 3) = 1 THEN 'USD'
ELSE 'EUR'
END,
SYSDATE - MOD(LEVEL, 365),
CASE
WHEN MOD(LEVEL, 2) = 0 THEN '0'
ELSE '1'
END,
'TYPE_' || TO_CHAR(MOD(LEVEL, 20)),
CAST(
MOD(LEVEL * 123, 10000000) / 100
AS DECIMAL(28,8)
),
MOD(LEVEL, 5),
MOD(LEVEL, 2),
SYSDATE - MOD(LEVEL, 100),
SYSDATE
FROM DUAL
CONNECT BY LEVEL <= 1000000;
COMMIT;
提交以后必须确认:
sql
SELECT COUNT(*)
FROM CMP_TEST_SRC.CMP_BAL_SRC;
返回:
text
1000000
这里 COMMIT 很重要。测试数据如果只在当前会话中可见,却没有真正提交,随后重新连接执行 dexp 时就可能出现预期之外的结果。
2.4 导出前记录空间基线
在执行 dexp 以前,应先保存源端基线。
首先记录表段:
sql
SELECT
OWNER,
SEGMENT_TYPE,
ROUND(SUM(BYTES) / 1024 / 1024, 2) AS SIZE_MB
FROM DBA_SEGMENTS
WHERE OWNER = 'CMP_TEST_SRC'
AND SEGMENT_NAME = 'CMP_BAL_SRC'
AND SEGMENT_TYPE LIKE 'TABLE%'
GROUP BY OWNER, SEGMENT_TYPE;
然后查看每个索引的大小:
sql
SELECT
I.TABLE_OWNER,
I.INDEX_NAME,
I.INDEX_TYPE,
I.UNIQUENESS,
ROUND(SUM(S.BYTES) / 1024 / 1024, 2) AS SIZE_MB
FROM DBA_INDEXES I
JOIN DBA_SEGMENTS S
ON S.OWNER = I.OWNER
AND S.SEGMENT_NAME = I.INDEX_NAME
AND S.SEGMENT_TYPE LIKE 'INDEX%'
WHERE I.TABLE_OWNER = 'CMP_TEST_SRC'
AND I.TABLE_NAME = 'CMP_BAL_SRC'
GROUP BY
I.TABLE_OWNER,
I.INDEX_NAME,
I.INDEX_TYPE,
I.UNIQUENESS
ORDER BY SIZE_MB DESC;
最后按照索引类型汇总:
sql
SELECT
I.TABLE_OWNER,
I.INDEX_TYPE,
COUNT(DISTINCT I.INDEX_NAME) AS INDEX_COUNT,
ROUND(SUM(S.BYTES) / 1024 / 1024, 2) AS SIZE_MB
FROM DBA_INDEXES I
JOIN DBA_SEGMENTS S
ON S.OWNER = I.OWNER
AND S.SEGMENT_NAME = I.INDEX_NAME
AND S.SEGMENT_TYPE LIKE 'INDEX%'
WHERE I.TABLE_OWNER = 'CMP_TEST_SRC'
AND I.TABLE_NAME = 'CMP_BAL_SRC'
GROUP BY
I.TABLE_OWNER,
I.INDEX_TYPE;
这样得到的不是一个模糊的"Schema 占用了多少空间",而是可以精确知道:
text
TABLE 占用多少
NORMAL INDEX 占用多少
CLUSTER INDEX 占用多少
具体是哪一个索引发生了变化
2.5 dexp/dimp 执行迁移
确认目标 Schema 中不存在测试表:
sql
SELECT OWNER, TABLE_NAME
FROM DBA_TABLES
WHERE OWNER = 'CMP_TEST_DST'
AND TABLE_NAME = 'CMP_BAL_SRC';
然后执行导出:
bash
dexp USERID=<USER>/<PASSWORD>@<HOST>:<PORT> \
DROP=N \
SCHEMAS=CMP_TEST_SRC \
FILE=/dmdata/backup/cmp_test_src.dmp \
LOG=/dmdata/backup/dexp_$(date +%Y%m%d_%H%M%S).log
导入时使用 REMAP_SCHEMA:
bash
dimp USERID=<USER>/<PASSWORD>@<HOST>:<PORT> \
FILE=/dmdata/backup/cmp_test_src.dmp \
REMAP_SCHEMA=CMP_TEST_SRC:CMP_TEST_DST \
TABLE_EXISTS_ACTION=SKIP \
PARALLEL=32 \
LOG=/dmdata/backup/dimp_$(date +%Y%m%d_%H%M%S).log
迁移完成后先验证数据量:
sql
SELECT 'CMP_TEST_SRC' AS SCHEMA_NAME,
COUNT(*) AS ROW_COUNT
FROM CMP_TEST_SRC.CMP_BAL_SRC
UNION ALL
SELECT 'CMP_TEST_DST',
COUNT(*)
FROM CMP_TEST_DST.CMP_BAL_SRC;
只有源端和目标端都确认是 100 万行以后,再进行空间比较。
2.6 测试结果
本地测试得到的结果比较明显:
| 项目 | CMP_SRC | CMP_DST |
|---|---|---|
| 数据量 | 1,000,000 | 1,000,000 |
| TABLE | 474 MB | 474 MB |
| CLUSTER INDEX | 474 MB | 474 MB |
| NORMAL INDEX | 1025 MB | 597 MB |
普通索引空间减少约:
text
1025 - 597 = 428 MB
比例约为:
text
428 / 1025 ≈ 41.8%
而部分联合索引的空间占用甚至接近缩小一半。
这个实验能够说明一个比较重要的问题:
dexp/dimp迁移前后,即使数据行数、表结构和索引定义一致,索引重新创建后的物理空间占用也不一定与源端完全一致。
本次测试中,表段没有发生变化,CLUSTER 索引也基本一致,明显的空间差异主要集中在普通索引。
因此遇到"迁移后数据库占用空间变小"这种现象时,不能直接判断为数据缺失,也不能只比较整个 Schema 的空间,需要进一步拆分到 TABLE、NORMAL INDEX、CLUSTER INDEX,再结合数据行数进行判断。
3. 部署 DEM,实现 DM8 数据库集中监控
除了迁移和空间验证,数据库环境建立起来以后,如何集中查看多台实例运行状态也是运维中的一个实际问题。
DM8 提供了 DEM(达梦企业管理系统),可以通过 Web 平台配合 dmagent 对多个数据库实例进行统一监控。
本次部署的大致架构为:
text
Browser
│
▼
http://<HOST>:8080/dem/
│
▼
┌─────────────────┐
│ DEM Server │
│ │
│ DM8 Backend DB │
│ Tomcat :8080 │
│ dmagent :6364 │
└─────────────────┘
│
────────┼────────
│
Managed DM8 Hosts
DEM 最终可以采集数据库状态、会话、SQL、表空间以及服务器 CPU、内存、磁盘等监控信息。
3.1 初始化 DEM 后台数据库
首先准备专门用于 DEM 的 DM8 实例:
bash
su - dmdba
./dminit \
path=/data/dmdata \
PAGE_SIZE=32 \
EXTENT_SIZE=32 \
CASE_SENSITIVE=y \
CHARSET=1 \
DB_NAME=DMDEM \
INSTANCE_NAME=DMDEM \
PORT_NUM=5236 \
SYSDBA_PWD='<PASSWORD>' \
SYSAUDITOR_PWD='<PASSWORD>'
初始化完成后可以先通过 dmserver 直接启动:
bash
cd /home/dmdba/dmdbms/bin
./dmserver /data/dmdata/DMDEM/dm.ini
确认进程:
bash
ps -ef | grep dmserver | grep -v grep
确认监听端口:
bash
ss -lntp | grep 5236
长期运行时更推荐注册为系统服务:
bash
cd /home/dmdba/dmdbms/script/root
./dm_service_installer.sh \
-t dmserver \
-p DMDEM \
-dm_ini /data/dmdata/DMDEM/dm.ini
随后通过 systemd 管理:
bash
systemctl start DmServiceDMDEM
systemctl enable DmServiceDMDEM
这套初始化和服务注册过程也作为 DEM 部署环境的基础。
3.2 初始化 DEM 数据库对象
确认 DM8 实例可以正常连接后,执行 DEM 提供的初始化脚本。
首先连接数据库:
bash
cd /home/dmdba/dmdbms/bin
./disql SYSDBA/"<PASSWORD>"@127.0.0.1:5236
然后:
sql
set CHAR_CODE UTF8;
set define off;
start /opt/dem_war/dem_war_20260615_9.0.1/dem_init.sql;
commit;
在修改后台数据库参数之前,建议先保存 dm.ini:
bash
cp /data/dmdata/DMDEM/dm.ini \
/data/dmdata/DMDEM/dm.ini.bak_dem
配置文件变更必须保留可回退版本,这一点对于数据库参数调整尤其重要。
3.3 配置 Tomcat 和 DEM Web
DEM Web 应用需要运行在 Tomcat 中。
环境中需要保证 Java 和 Tomcat 正常,随后在 Tomcat 的 catalina.sh 中指定 DM 动态库路径:
bash
JAVA_OPTS="$JAVA_OPTS -server -Djava.library.path=/home/dmdba/dmdbms/bin"
export LD_LIBRARY_PATH=/home/dmdba/dmdbms/bin:$LD_LIBRARY_PATH
Tomcat 的 Connector 可按实际环境配置,例如:
xml
<Connector
port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxPostSize="-1" />
然后将 dem.war 放入:
text
<TOMCAT_HOME>/webapps/
启动 Tomcat:
bash
cd <TOMCAT_HOME>/bin
./startup.sh
正常情况下 Tomcat 会自动将:
text
dem.war
解压为:
text
webapps/dem/
随后修改:
text
webapps/dem/WEB-INF/db.xml
配置后台 DM8 数据库连接:
xml
<ConnectPool>
<Url>jdbc:dm://<DB_HOST>:5236?lobFetchOptimize=true</Url>
<User>SYSDBA</User>
<Password><PASSWORD></Password>
<InitPoolSize>5</InitPoolSize>
<CorePoolSize>10</CorePoolSize>
<MaxPoolSize>500</MaxPoolSize>
<KeepAliveTime>60</KeepAliveTime>
<DbDriver></DbDriver>
<DbTestStatement>select 1</DbTestStatement>
</ConnectPool>
配置完成后可以通过:
bash
grep -E "Url|User|Password" \
<TOMCAT_HOME>/webapps/dem/WEB-INF/db.xml
确认实际配置。
一个容易踩坑的地方是 dem.war。
如果一直保留 WAR 文件,Tomcat 后续重新部署时有可能再次解压并影响已经修改好的 db.xml。因此在确认 dem/ 目录正常之后,可以停止 Tomcat,并将原 WAR 改名留档,例如:
bash
cd <TOMCAT_HOME>/bin
./shutdown.sh
cd <TOMCAT_HOME>/webapps
mv dem.war dem.war.bak
再重新启动:
bash
cd <TOMCAT_HOME>/bin
./startup.sh
3.4 验证 DEM Web
首先确认 8080 端口:
bash
ss -lntp | grep 8080
在服务器本机测试:
bash
curl -L http://127.0.0.1:8080/dem/ | head
正常情况下能够看到类似:
html
<title>达梦企业管理系统</title>
如果页面打不开或者 DEM 无法连接数据库,可以优先检查 Tomcat 日志:
bash
tail -100 <TOMCAT_HOME>/logs/catalina.out
进一步过滤数据库相关异常:
bash
grep -E \
"ERROR|SQLException|DMException|网络通信异常|jdbc:dm://" \
<TOMCAT_HOME>/logs/catalina.out
如果服务器启用了 firewalld,还需要确认 Web 端口已经开放:
bash
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
3.5 配置 dmagent 并纳管数据库
DEM Web 服务正常以后,还需要在被监控服务器启动 dmagent。
进入 agent 目录:
bash
cd /dm/tool/dmagent
修改:
text
agent.ini
核心参数类似:
ini
center_url = http://<DEM_HOST>:8080/dem
ip_list = [<LOCAL_HOST>]
service_enable = true
service_port = 6364
gather_enable = true
然后生成 service key:
bash
./start.sh INSTALL_SERVICE_KEY <SERVICE_KEY>
这里需要注意,agent 中的 service key 必须和 DEM 页面:
text
系统
→ 系统配置
→ service_key
保持一致。
正式启动:
bash
./start.sh -d /dm/tool/dmagent/agent.ini
如果第一次启动出现:
text
Upgrade agent finished.
Please run agent again.
说明 agent 完成了自动升级,再执行一次启动命令即可。
检查进程:
bash
ps -ef | grep dmagent | grep -v grep
检查端口:
bash
ss -lntp | grep 6364
确认 agent 正常以后,再在 DEM 页面:
text
监控
→ 数据库
→ 添加数据库
填写数据库主机、端口、用户名和密码。
这里的主机不能随意填写,必须是已经被 dmagent 正常纳管的主机。agent 正常启动后,对应 6364 端口应有 Java 进程监听。
完成以后,DEM 就能够集中查看数据库运行状态、SQL、会话、表空间以及主机资源情况。
常用检查命令可以保留如下:
bash
# DM8
ps -ef | grep dmserver | grep -v grep
ss -lntp | grep <DB_PORT>
# DEM / Tomcat
ss -lntp | grep 8080
tail -f <TOMCAT_HOME>/logs/catalina.out
# dmagent
ps -ef | grep dmagent | grep -v grep
ss -lntp | grep 6364
对,这一块应该把"升级过程"和"升级后异常排查"连起来写,否则直接从 DBMS_LDAP 报错开始,技术博客的上下文是不完整的。
原来的第 4 章建议直接改名为:
4. DM8 版本升级及升级后 DBMS_LDAP 系统包异常排查
DM8 数据库版本升级不仅涉及程序文件替换,还需要考虑实例停启、原程序备份、目录权限恢复以及升级后的系统对象验证。
本次升级采用新版 bin_release、symbols 等程序文件替换原 DM8 程序文件的方式进行。整个过程可以划分为:
text
准备升级包
↓
停止数据库实例
↓
备份原程序目录
↓
替换新版程序文件
↓
恢复目录权限
↓
启动数据库实例
↓
执行系统对象重建
↓
检查升级结果
升级完成以后,数据库实例能够启动,但在执行系统包重建时发现 DBMS_LDAP 编译异常,因此又进一步通过 ISO 完整安装环境进行了隔离验证。
4.1 准备新版程序文件
升级前首先需要准备对应版本的 DM8 程序文件。实际环境中遇到的升级包主要有 tar 包和 ISO 安装镜像两种形式。
如果拿到的是 tar 包,可以直接创建目录并解压:
bash
mkdir -p /opt/dm_upgrade
cd /opt/dm_upgrade
tar -zxvf trunk8_rel_xxxx.tar.gz
解压后得到的 release 目录中即包含新版程序文件,可以作为后续替换原 bin 目录的来源。
如果拿到的是 ISO,则先挂载安装镜像:
bash
mkdir -p /mnt/dm_iso
mount -o loop dm8_xxxx.iso /mnt/dm_iso
cd /mnt/dm_iso
./DMInstall.bin -i
这里有一个需要注意的地方。
在交互式安装程序运行到语言选择等前期步骤时,新版本程序文件已经被解压到 /tmp/DMInstall 临时目录,但安装程序还没有真正向目标安装目录写入数据。
如果本次目的只是获取完整的新版本程序文件用于原地升级,可以保持当前安装程序不要继续,也不要退出,另外新开一个 SSH 会话检查临时目录:
bash
ls /tmp/DMInstall/source/
然后从该目录复制需要的新版本程序文件。
之所以不能先退出安装程序,是因为退出以后 /tmp/DMInstall 临时目录可能会被自动清理,刚刚解压出来的文件也会随之删除。
这样,升级前就可以先把新版程序准备好,而不需要直接在原数据库程序目录中进行操作。
4.2 停止实例并备份原程序目录
程序文件替换之前,首先停止数据库实例。
如果数据库已经注册为服务,可以使用服务脚本:
bash
cd /data/dm8/bin
./DmServiceXXXX stop
也可以使用 systemctl:
bash
systemctl stop DmServiceXXXX
停止以后不能只看命令有没有返回成功,还需要确认 dmserver 进程是否已经真正退出:
bash
ps -ef | grep dmserver
确认数据库进程完全停止之后,再进行程序文件替换。
正式替换之前还需要保存当前版本的程序目录。例如:
bash
cp -r /data/dm8/bin \
/data/dm8/bin_bak_$(date +%Y%m%d)
也可以根据实际安装路径进行备份:
bash
cp -r /dmdbms/bin \
/dmdbms/bin_bak_$(date +%Y%m%d)
这里备份的意义比较直接:如果新版程序替换以后实例无法正常启动,至少原程序目录仍然保留,可以继续用于问题定位或者恢复。
因此不能采用"直接覆盖,出了问题再找旧版本安装包"的方式处理数据库程序升级。
4.3 替换新版程序并恢复权限
完成备份以后,将准备好的新版 release/bin_release 等程序文件复制到原程序目录。
例如:
bash
\cp -rf ./* /data/dm8/bin/
本次升级实际采用的是将新版 bin_release、symbols 中的文件直接复制到原有程序目录的方式。
如果程序文件是通过 root 用户执行复制操作,还需要重新确认 DM8 目录属主和属组。
例如:
bash
chown -R dmdba:dinstall /data/dm8
这是程序文件覆盖过程中容易忽略的一步。
因为即使文件内容本身没有问题,如果新版文件的属主或者权限发生变化,同样可能影响后续 dmdba 用户启动数据库或者访问相关动态库。原升级记录中也在程序替换后重新执行了目录权限调整。
所以程序替换阶段实际完成的是:
text
原 bin 备份
↓
新版程序覆盖
↓
检查目录属主和权限
↓
再启动数据库
而不是简单执行一次 cp 就结束。
4.4 启动数据库并进行升级后验证
程序文件完成替换以后,可以先通过 dmserver 直接启动原实例进行测试。
切换到 dmdba 用户:
bash
su - dmdba
进入 DM8 程序目录:
bash
cd /data/dm8/bin
使用原数据库实例的 dm.ini 启动:
bash
./dmserver /data/dm8/instance/DAMENG/dm.ini
这种方式比较适合升级后的第一次手工启动,可以直接观察 dmserver 输出的信息。
如果数据库原本就是通过操作系统服务管理,则也可以使用:
bash
systemctl start DmServiceXXXX
随后检查:
bash
systemctl status DmServiceXXXX
长期运行环境中,可以通过 dm_service_installer.sh 将实例注册为系统服务:
bash
cd /dm8/script/root
./dm_service_installer.sh \
-t dmserver \
-dm_ini /dmdata/data/XXXX/dm.ini \
-p XXXX
注册后常用管理命令为:
bash
systemctl start DmServiceXXXX
systemctl stop DmServiceXXXX
systemctl restart DmServiceXXXX
systemctl status DmServiceXXXX
systemctl enable DmServiceXXXX
数据库实例启动只是升级验证的第一步。
对于数据库版本升级,还需要进一步检查系统视图和系统包是否能够正常重建。因此连接数据库后执行:
sql
SP_CREATE_SYSTEM_VIEWS(0);
SP_CREATE_SYSTEM_VIEWS(1);
SP_CREATE_SYSTEM_PACKAGES(0);
SP_CREATE_SYSTEM_PACKAGES(1);
前三项均可以正常执行,但是在执行:
sql
SP_CREATE_SYSTEM_PACKAGES(1);
时,虽然过程能够执行完成,但创建的系统对象存在编译错误。
核心报错为:
text
[-2207]:第 12 行附近出现错误:
无法解析的成员访问表达式 [SF_LDAP_INIT]
[-3328]:
包/对象 [DBMS_LDAP] 主体解析出错
实际验证结果如下:
| 验证项 | 结果 |
|---|---|
SP_CREATE_SYSTEM_VIEWS(0) |
执行成功 |
SP_CREATE_SYSTEM_VIEWS(1) |
执行成功 |
SP_CREATE_SYSTEM_PACKAGES(0) |
执行成功 |
SP_CREATE_SYSTEM_PACKAGES(1) |
执行完成,但对象存在编译错误 |
也就是说,这次升级并不是数据库"完全无法启动",而是表现为:
text
数据库实例可以启动
↓
数据库能够正常连接
↓
部分系统对象可以正常重建
↓
SP_CREATE_SYSTEM_PACKAGES(1)
↓
DBMS_LDAP 编译异常
这种情况也说明,数据库实例能够正常启动不能直接作为版本升级成功的唯一判断标准,升级以后还需要继续验证系统对象和相关功能。
4.5 初步怀疑:是否为手工覆盖程序文件导致
由于本次升级采用的是:
text
bin_release
symbols
↓
直接复制
↓
覆盖原 DM8 程序目录
因此出现 DBMS_LDAP 系统包错误以后,一个比较直接的怀疑就是:
手工复制新版程序时是否出现了文件遗漏,或者新版程序文件与动态库没有完整替换?
如果这个假设成立,那么使用一套通过 ISO 正常完整安装得到的 DM8 程序启动同一个数据库,理论上应该不再出现相同问题。
因此后续测试没有继续修改原 /data/dm8 程序目录,而是重新通过 ISO 安装了一套独立的新版本 DM8:
text
/opt/dm8_iso
原程序仍然保留在:
text
/data/dm8
测试过程中不执行 dminit,也不重新创建数据库。
仍然使用原数据库实例的:
text
/data/dm8/instance/DAMENG/dm.ini
也就是说,这次实验固定了数据库实例,只替换数据库运行所使用的 DM8 程序。
4.6 隔离新旧程序运行环境
为了防止虽然使用了 /opt/dm8_iso/bin/dmserver,但实际运行时又加载到 /data/dm8 中的旧动态库,需要先隔离当前终端的运行环境。
执行:
bash
export DM_HOME=/opt/dm8_iso
export PATH=$DM_HOME/bin:$DM_HOME/tool:$PATH
export LD_LIBRARY_PATH=$DM_HOME/bin:/lib:/usr/lib
然后检查:
bash
echo $DM_HOME
echo $LD_LIBRARY_PATH
which dmserver
预期:
text
DM_HOME
↓
/opt/dm8_iso
dmserver
↓
/opt/dm8_iso/bin/dmserver
另外通过 ldd 检查新版本 dmserver 是否显式加载原程序目录中的旧动态库:
bash
ldd /opt/dm8_iso/bin/dmserver | grep '/data/dm8'
本次检查没有发现 dmserver 显式加载 /data/dm8 下的旧动态库。
这样可以尽量保证测试使用的是 ISO 完整安装得到的新程序环境,而不是新旧执行码混合。
4.7 使用 ISO 新程序启动原数据库再次验证
确认原数据库实例已经停止以后,直接使用 ISO 完整安装目录中的 dmserver 启动原数据库:
bash
/opt/dm8_iso/bin/dmserver \
/data/dm8/instance/DAMENG/dm.ini
启动过程中出现:
text
file dm.key not found, use default license!
该提示没有阻断本次验证,数据库仍然能够继续连接并执行 SQL。
随后再次执行:
sql
SP_CREATE_SYSTEM_VIEWS(0);
SP_CREATE_SYSTEM_VIEWS(1);
SP_CREATE_SYSTEM_PACKAGES(0);
SP_CREATE_SYSTEM_PACKAGES(1);
结果并没有发生变化。
SP_CREATE_SYSTEM_PACKAGES(1) 仍然出现:
text
SF_LDAP_INIT
DBMS_LDAP
相关解析错误,与之前直接使用 bin_release、symbols 覆盖原程序目录后的现象一致。
因此可以得到一个阶段性的排查结论:
使用 ISO 完整安装得到的新版本
dmserver启动原数据库后,SP_CREATE_SYSTEM_PACKAGES(1)仍然能够复现DBMS_LDAP / SF_LDAP_INIT解析错误,因此该异常并非只在"bin_release 文件直接覆盖原 bin"这种升级方式下出现。
也就是说,可以初步排除:
text
"手工覆盖程序文件不完整"
是导致当前问题的唯一原因。
但这里还不能直接说明 DBMS_LDAP 的真正问题已经定位。
后续仍然需要继续检查 LDAP 相关动态库依赖,重点确认是否存在:
text
not found
加载到了旧版本路径
LDAP 相关动态库版本不一致
依赖库缺失
可以继续结合:
bash
ldd <LDAP相关动态库>
对实际动态库依赖进行检查。
从整个升级和排查过程来看,本次问题实际上经历了:
text
准备升级程序
↓
停止数据库
↓
备份原 bin
↓
覆盖新版程序
↓
恢复权限
↓
启动原实例
↓
重建系统对象
↓
发现 DBMS_LDAP 异常
↓
怀疑手工覆盖不完整
↓
ISO 完整安装新版程序
↓
隔离 DM_HOME / PATH / LD_LIBRARY_PATH
↓
新程序启动同一原数据库
↓
再次复现 DBMS_LDAP 异常
↓
排除"手工覆盖不完整"为唯一原因
↓
继续检查 LDAP 动态库依赖