达梦数据库 dexp/dimp 实战指南:模式克隆、REMAP_SCHEMA 与特殊字符踩坑记录
在日常的数据库运维和开发测试中,我们经常需要对某个模式(Schema)进行完整备份,或者将其"克隆"一份用于测试环境。在达梦数据库(DMDB)中,dexp(逻辑导出)和 dimp(逻辑导入)是完成这项工作的利器。
本文将结合实战经验,分享如何使用 dexp/dimp 进行模式导出与重命名导入(REMAP_SCHEMA),并记录一个由密码特殊字符引发的"导出 0 张表"的隐蔽 Bug。
一、核心踩坑:密码中的 .. 导致导出 0 张表
诡异的现象
在使用 dexp 导出 V5_EAM 模式时,命令行执行后瞬间结束,日志中显示:
[WARNING]invalid object:V5_EAM
export total 0 TABLE
schema[V5_EAM] export terminate.....查询 DBA_TABLES 确认该模式下明明有 218 张表,但导出结果却是 0。
根因分析
经过排查,发现该数据库 SYSDBA 用户的密码为 woshimima..(末尾包含连续的两个点 ..)。
当在命令行直接拼接连接串时:
# 错误示范
./dexp USERID=SYSDBA/woshimima..@localhost:5236 SCHEMAS=V5_EAM ...连续点号 .. 在 Shell 解析或 dexp 内部参数解析时,被错误地识别为路径跳转或特殊指令,导致 dexp 内部验证 Schema 属主时发生异常,从而跳过了所有对象的导出。
解决方案
方案 A(推荐):使用交互式密码输入
不在命令行中明文传递密码,执行命令后根据提示输入:
./dexp USERID=SYSDBA@localhost:5236 SCHEMAS=V5_EAM FILE=... LOG=...方案 B:使用单引号包裹整个 USERID
利用单引号屏蔽 Shell 的特殊字符解析:
./dimp 'USERID=SYSDBA/woshimima..@localhost:5236' ...二、实战:导出模式 (dexp)
确认连接方式后,执行完整的模式导出。建议加上 PARALLEL 参数提升导出速度。
cd /data/dmdbms/bin/
./dexp USERID=SYSDBA@localhost:5236 \
SCHEMAS=V5_EAM \
FILE=/data/dmexport/V5_EAM.dmp \
LOG=/data/dmexport/V5_EAM_export.log \
PARALLEL=4执行后按提示输入密码。看到 terminate export success without warning 即代表完美导出。
三、实战:导入并重命名模式 (dimp + REMAP_SCHEMA)
很多时候,我们导出的数据是为了在同一实例下建立一份测试副本。这时就需要用到 REMAP_SCHEMA 参数,将源模式的数据导入到目标模式中。
⚠️ 核心注意点:目标模式必须提前创建dimp的REMAP_SCHEMA不会自动创建目标模式!如果目标模式不存在,导入会直接报错。
Step 1:创建目标模式
/data/dmdbms/bin/disql SYSDBA@localhost:5236 -e "CREATE SCHEMA V5_EAM_TEST_0813;"Step 2:执行导入
cd /data/dmdbms/bin/
./dimp USERID=SYSDBA@localhost:5236 \
FILE=/data/dmexport/V5_EAM.dmp \
LOG=/data/dmexport/V5_EAM_TEST_0813.log \
REMAP_SCHEMA=V5_EAM:V5_EAM_TEST_0813 \
TABLE_PARALLEL=4参数解析:REMAP_SCHEMA=源模式:目标模式。此操作对源模式 V5_EAM 是只读的,绝对不会影响原数据。
四、总结
- 密码安全与兼容性:强烈建议使用交互式密码输入来调用
dexp/dimp,既能避免密码泄露在history中,又能完美规避特殊字符(如@、..、#等)导致的解析 Bug。 - REMAP_SCHEMA 前置条件:永远记得在导入前,先
CREATE SCHEMA目标模式。 - 数据完整性:导入完成后,务必通过
SELECT COUNT(*) FROM DBA_TABLES WHERE OWNER='目标模式';核对表数量,确保数据无损迁移。
💻 第二部分:自动化交互式 Shell 脚本
将以下代码保存为 dm_migrate.sh,赋予执行权限(chmod +x dm_migrate.sh)后运行。脚本包含了环境检查、交互式菜单、密码隐藏输入和自动创建 Schema 等完善功能。
脚本修正说明(发布前已订正):原稿在 Markdown 渲染中丢失了部分语法符号,已在此处还原/修正:
- 还原变量符号
${VAR}(原稿误为{VAR});- 还原颜色转义
\033(原稿误为033);- 修正命令替换
$(...)与退出码$?;disql检查/建模式语句改用 双引号包裹密码(user/"pass"@host:port),与文章主旨一致,规避..等特殊字符被解析的坑。
#!/bin/bash
# ==============================================================================
# 达梦数据库 dexp/dimp 交互式迁移脚本
# 作者: DBA 运维助手
# 描述: 支持模式导出、模式重命名导入(REMAP_SCHEMA),自动处理前置校验
# ==============================================================================
# ================= 基础配置区 (可根据实际环境修改) =================
DM_BIN_DIR="/data/dmdbms/bin" # 达梦 bin 目录路径
EXPORT_BASE_DIR="/data/dmexport" # 导出文件默认存放目录
DEFAULT_HOST="localhost" # 默认数据库 IP
DEFAULT_PORT="5236" # 默认数据库端口
DEFAULT_USER="SYSDBA" # 默认执行用户
# =====================================================================
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 检查达梦工具是否存在
check_env() {
if [ ! -f "${DM_BIN_DIR}/dexp" ] || [ ! -f "${DM_BIN_DIR}/dimp" ]; then
echo -e "${RED}[ERROR] 未找到 dexp 或 dimp 工具,请检查 DM_BIN_DIR 配置: ${DM_BIN_DIR}${NC}"
exit 1
fi
mkdir -p "${EXPORT_BASE_DIR}"
}
# 获取数据库连接参数
get_db_conn() {
read -p "请输入数据库 IP [默认 ${DEFAULT_HOST}]: " DB_HOST
DB_HOST=${DB_HOST:-$DEFAULT_HOST}
read -p "请输入数据库端口 [默认 ${DEFAULT_PORT}]: " DB_PORT
DB_PORT=${DB_PORT:-$DEFAULT_PORT}
read -p "请输入数据库用户名 [默认 ${DEFAULT_USER}]: " DB_USER
DB_USER=${DB_USER:-$DEFAULT_USER}
echo -n "请输入 ${DB_USER} 的密码 (输入时不可见): "
read -s DB_PASS
echo ""
if [ -z "${DB_PASS}" ]; then
echo -e "${RED}[ERROR] 密码不能为空!${NC}"
exit 1
fi
# dexp/dimp:用单引号包裹整个 USERID,防止密码中的特殊字符(如 .. @ #)被 Shell 解析
CONN_STR="'USERID=${DB_USER}/${DB_PASS}@${DB_HOST}:${DB_PORT}'"
# disql:用双引号包裹密码(达梦语法 user/"pass"@host:port),同样规避特殊字符
DISQL_CONN="${DB_USER}/\"${DB_PASS}\"@${DB_HOST}:${DB_PORT}"
}
# 导出模式
do_export() {
echo -e "\n${BLUE}========== 模式导出 (dexp) ==========${NC}"
get_db_conn
read -p "请输入要导出的源模式名 (如 V5_EAM): " SRC_SCHEMA
if [ -z "${SRC_SCHEMA}" ]; then
echo -e "${RED}[ERROR] 模式名不能为空!${NC}"
return
fi
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DMP_FILE="${EXPORT_BASE_DIR}/${SRC_SCHEMA}_${TIMESTAMP}.dmp"
LOG_FILE="${EXPORT_BASE_DIR}/${SRC_SCHEMA}_${TIMESTAMP}_exp.log"
echo -e "${YELLOW}[INFO] 开始导出 ${SRC_SCHEMA} 到 ${DMP_FILE} ...${NC}"
# 使用 eval 执行带有单引号的连接串(去掉外层单引号后整体作为一个参数传给 dexp)
eval "${DM_BIN_DIR}/dexp ${CONN_STR} SCHEMAS=${SRC_SCHEMA} FILE=${DMP_FILE} LOG=${LOG_FILE} PARALLEL=4"
if [ $? -eq 0 ]; then
echo -e "${GREEN}[SUCCESS] 导出完成!请检查日志: ${LOG_FILE}${NC}"
else
echo -e "${RED}[ERROR] 导出失败,请查看日志排查原因: ${LOG_FILE}${NC}"
fi
}
# 导入模式
do_import() {
echo -e "\n${BLUE}========== 模式导入 (dimp + REMAP) ==========${NC}"
get_db_conn
read -p "请输入 DMP 文件绝对路径: " DMP_FILE
if [ ! -f "${DMP_FILE}" ]; then
echo -e "${RED}[ERROR] 文件不存在: ${DMP_FILE}${NC}"
return
fi
read -p "请输入 DMP 文件中的源模式名 (如 V5_EAM): " SRC_SCHEMA
read -p "请输入要导入的目标模式名 (如 V5_EAM_TEST_0813): " DEST_SCHEMA
if [ -z "${SRC_SCHEMA}" ] || [ -z "${DEST_SCHEMA}" ]; then
echo -e "${RED}[ERROR] 模式名不能为空!${NC}"
return
fi
# 检查目标模式是否存在,不存在则提示创建
echo -e "${YELLOW}[INFO] 检查目标模式 ${DEST_SCHEMA} 是否存在...${NC}"
CHECK_SQL="SELECT COUNT(*) FROM DBA_USERS WHERE USERNAME='${DEST_SCHEMA}';"
# 使用 disql 检查(密码用双引号包裹,规避特殊字符)
USER_EXISTS=$(echo "${CHECK_SQL}" | ${DM_BIN_DIR}/disql ${DISQL_CONN} -e 2>/dev/null | grep -oE '[0-9]+' | head -1)
if [ "${USER_EXISTS}" != "1" ]; then
echo -e "${YELLOW}[WARNING] 目标模式 ${DEST_SCHEMA} 不存在!dimp 不会自动创建模式。${NC}"
read -p "是否立即通过 SYSDBA 创建该模式? (y/n): " CREATE_CHOICE
if [[ "${CREATE_CHOICE}" =~ ^[Yy] ]]; then
${DM_BIN_DIR}/disql ${DISQL_CONN} -e "CREATE SCHEMA ${DEST_SCHEMA};"
if [ $? -eq 0 ]; then
echo -e "${GREEN}[SUCCESS] 模式 ${DEST_SCHEMA} 创建成功!${NC}"
else
echo -e "${RED}[ERROR] 模式创建失败,请检查权限或名称是否合法。${NC}"
return
fi
else
echo -e "${RED}[INFO] 取消导入。请先手动创建目标模式。${NC}"
return
fi
fi
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${EXPORT_BASE_DIR}/${DEST_SCHEMA}_${TIMESTAMP}_imp.log"
echo -e "${YELLOW}[INFO] 开始导入 ${SRC_SCHEMA} -> ${DEST_SCHEMA} ...${NC}"
eval "${DM_BIN_DIR}/dimp ${CONN_STR} FILE=${DMP_FILE} LOG=${LOG_FILE} REMAP_SCHEMA=${SRC_SCHEMA}:${DEST_SCHEMA} TABLE_PARALLEL=4"
if [ $? -eq 0 ]; then
echo -e "${GREEN}[SUCCESS] 导入完成!请检查日志: ${LOG_FILE}${NC}"
else
echo -e "${RED}[ERROR] 导入出现异常,请查看日志排查原因: ${LOG_FILE}${NC}"
fi
}
# 主菜单
main_menu() {
check_env
while true; do
echo -e "\n${GREEN}========================================${NC}"
echo -e "${GREEN} 达梦数据库 dexp/dimp 迁移工具箱${NC}"
echo -e "${GREEN}========================================${NC}"
echo "1. 导出模式 (Export Schema)"
echo "2. 导入模式 (Import & REMAP_SCHEMA)"
echo "0. 退出"
echo -e "${GREEN}----------------------------------------${NC}"
read -p "请选择操作 [0-2]: " CHOICE
case ${CHOICE} in
1) do_export ;;
2) do_import ;;
0) echo -e "${BLUE}再见!${NC}"; exit 0 ;;
*) echo -e "${RED}[ERROR] 无效的选择,请重新输入。${NC}" ;;
esac
done
}
# 执行主菜单
main_menu💡 脚本使用指南
- 创建脚本文件:
vi /data/dm_migrate.sh - 粘贴上述代码并保存。
- 赋予执行权限:
chmod +x /data/dm_migrate.sh - 运行脚本:
./dm_migrate.sh
脚本亮点
- 隐藏密码输入:使用
read -s,密码不会在屏幕上显示,也不会留在命令历史中。 - 自动处理特殊字符:脚本内部自动对
dexp/dimp用单引号包裹连接串('USERID=...')、对disql用双引号包裹密码(user/"pass"@host:port),完美解决你之前遇到的..密码解析 Bug。 - 智能校验:在选择导入时,脚本会自动调用
disql检查目标模式是否存在。如果不存在,会主动询问并帮你创建,避免了dimp因找不到目标 Schema 而报错的问题。 - 自动命名:导出文件和日志自动加上
年月日_时分秒时间戳,防止覆盖历史备份。
评论 (0)