第十周技术博客

DM8 运维实战:数据迁移、空间验证、DEM 部署与升级异常排查

DM8 数据库的日常运维并不只是实例启停和 SQL 执行。在实际环境中,经常会涉及跨 Schema 数据迁移、逻辑备份恢复、迁移前后空间校验、监控平台部署以及版本升级后的兼容性问题排查。

本文基于实际操作场景,整理一套较完整的 DM8 运维实践过程,主要涉及四个方面:

  1. 跨 Schema 表数据迁移与结果校验;
  2. dexp/dimp 迁移后表和索引空间差异验证;
  3. DM8 DEM 企业管理平台部署及数据库纳管;
  4. 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 中还经常使用 dexpdimp 完成 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_SIZEEXTENT_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_SIZEEXTENT_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>&lt;PASSWORD&gt;</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_releasesymbols 等程序文件替换原 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_releasesymbols 中的文件直接复制到原有程序目录的方式。

如果程序文件是通过 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_releasesymbols 覆盖原程序目录后的现象一致。

因此可以得到一个阶段性的排查结论:

使用 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 动态库依赖

eco.dameng.com

相关推荐
1314lay_10071 小时前
C#调用Sql Server存储过程,并且使用传入表结构
数据库·经验分享·笔记·sqlserver·c#
anxiao_m1 小时前
2026大文件传输软件推荐,从速度安全多维度客观测评
数据库·文件传输·传输
IT大白鼠1 小时前
MySQL 分布式集群系列 · 第七篇——生产调优实战:NDB 性能、内存、高可用全方位优化
数据库·分布式·mysql
IvorySQL2 小时前
打造下一代 AI Agent 的统一多模智能数据底座——PostgreSQL 与 AI 的融合演进
数据库·人工智能·postgresql
ShineWinsu2 小时前
对于MySQL:内置函数的解析
linux·数据库·c++·mysql·面试·函数·查询
isNotNullX2 小时前
智能问数为什么答不准?数据、语义、模型、SQL四层拆解
数据库
烂蜻蜓3 小时前
Django入门教程(三):django-admin与manage.py命令完全指南
数据库·django·sqlite
yunlaodacom3 小时前
腾讯云国际版代理商:COS标准、低频、归档和深度归档怎么选?存储成本与数据取回区别
数据库·云计算·腾讯云
Nturmoils3 小时前
SQL Server数据库迁移:V9R4C019 如何接住存量 T-SQL 批处理
数据库