2.2. 并行导出导入
2.2.1. 说明
1、使用exportdb.sh $dbname可以自动备份数据库中表结构和数据
2、备份的结果在BAK*目录中
3、数据恢复的时候在BAK目录下执行:
创建库(不建议带DELIMIDENT=y环境变量)
检查
${dbname}_env_file内容是否正确importdb.sh $dbname
2.2.2. 脚本
exportdb.sh内容
#!/bin/bash
#脚本功能:将数据导出成文本,迁移至其他实例
#最后更新时间:2025-05-20
#使用方法:
#1.执行该脚本
#2.输入要导出的数据库名称后回车
#3.导出程序在后台执行,可执行 ps -ef|grep dbaccess 监控是否所有表都导出完毕
#4.导出过程中,会在数据文件夹内自动生成数据导入 importdb.sh,执行该脚本可将数据导入至其他实例
#5.修正三个问题,第一个是外键的自动处理;第二个用dbload替换load解决了长事务的问题;第三实现了并行的dbload
#6.注意aix平台没有sed -i参数,可能会有问题,需要单独处理
#7.通过dbload后台执行的方式,并行加载数据,不建议同时使用dbaccess dbname idx.sql。这样可能会带来先创建索引,后倒入数据的现象
#8.增加对开启DELIMIDENT的处理,以及3.6之后的系统表ID改为大于999
#9.增加-o的处理,修复-o all
checkRunDba(){
RUNDBA=$(ps -ef | grep dbaccess | grep -v grep | wc -l)
if [ ${RUNDBA} -ge 20 ]; then
return 1
else
return 0
fi
}
_loginfo(){
echo -e "[$(date +'%F %T')] $*"
}
if [ $# -lt 1 ]; then
read -p "Please input database name: " DBNAME
else
DBNAME=$1
fi
BACKDIR=BAK-${DBNAME}-$(date +%Y%m%d%H%M)
mkdir -p $BACKDIR/ctl
mkdir -p $BACKDIR/log
cd $BACKDIR
export DBDATE=Y4MD-
# 环境变量文件
env | egrep '(DB_LOCALE|CLIENT_LOCALE|GL_|DBDATE|DELIMIDENT)' | awk -F'=' \
'{if($0~/ /){print "export "$1"=\047"$2"\047"}else{print "export "$1"="$2}}' > ${DBNAME}_env_file
# 不同版本的系统表ID不一样
DBVERMID=$(onstat -version | awk -F'.' '/^Build Number:/{print $2}')
if [ ${DBVERMID:-5} -ge 6 ]; then
SYSTABID=999
else
SYSTABID=99
fi
_loginfo "DBVERMID is ${DBVERMID}"
_loginfo "Dbschema out.."
dbschema -d ${DBNAME} -q -ss ${DBNAME}.sql
if [ ! $? -eq 0 ]; then
_loginfo "Dbschema out error."
exit 1
fi
# 设置3.5以下需要获取package
if [ ${DBVERMID:-5} -ge 5 ]; then
_loginfo "Dbschema package out.."
dbschema -d ${DBNAME} -q -o all ${DBNAME}_package.sql
if [ ! $? -eq 0 ]; then
_loginfo "Dbschema package out error."
exit 1
fi
fi
dbaccess ${DBNAME} - << EOF >/dev/null 2>&1
unload to BAK_tabname.unl delimiter ' '
SELECT t.tabname,t.ncols, p.nrows::int8 nrows
FROM systables t, sysmaster:sysptnhdr p
WHERE t.tabid > ${SYSTABID}
AND t.tabtype = 'T'
AND t.partnum = p.partnum
UNION
SELECT t.tabname,t.ncols,sum(p.nrows)::int8 nrows
FROM systables t, sysfragments f, sysmaster:sysptnhdr p
WHERE t.tabid > ${SYSTABID}
AND t.tabtype = 'T'
AND t.tabid = f.tabid
AND f.fragtype = 'T'
AND f.partn = p.partnum
GROUP BY 1,2;
EOF
# 处理表名(开启delimident的情况)
if [ -f BAK_tabname.unl ]; then
while read TAB NCOL NROWS
do
# 当前规则:表名:第1位是小写字母及"_",之后是小写字母、数字及"_",不符合的认为是开了DELIMIDENT
if [[ "$TAB" =~ ^[a-z_][a-z0-9_]*$ ]] ; then
echo "$TAB $NCOL $NROWS 0" >> BAK_tabname.unl_tmp
else
echo "$TAB $NCOL $NROWS 1" >> BAK_tabname.unl_tmp
fi
done < BAK_tabname.unl
mv BAK_tabname.unl BAK_tabname.unl_nodelimident
if [ -f BAK_tabname.unl_tmp ]; then
mv BAK_tabname.unl_tmp BAK_tabname.unl
fi
fi
_loginfo "Unload tables.."
while read TAB NCOL NROWS DEL
do
{
if [ x"${DEL}" = x1 ]; then
TMPTAB="\"${TAB}\""
else
TMPTAB="${TAB}"
fi
dbaccess ${DBNAME} - << EOF >/dev/null 2>&1
set isolation to dirty read;
unload to ${TAB}.unl select * from ${TMPTAB};
EOF
}&
while true
do
checkRunDba
if [ $? -eq 0 ]; then
break
else
sleep 5
fi
done
done < BAK_tabname.unl
_loginfo "Unload table finish."
# 建表语句和建索引语句分离,允许去除主键、约束名称
echo "set pdqpriority 96;" > IDX_${DBNAME}.sql
sed -n '/revoke usage /,$p' ${DBNAME}.sql >> IDX_${DBNAME}.sql
sed -i '/revoke usage /,$d' ${DBNAME}.sql
# 导出注释
_loginfo "Export comments."
dbaccess ${DBNAME} - << EOF >/dev/null 2>&1
-- tabcomm, delimiter '\t' (ctrl + i)
unload to _tmp_table_comment.unl delimiter ' '
select 'comment on table ' || tabname || ' is ''' || replace(comments, chr(39), '''''') || ''';' as comment from syscomments;
-- colcomm, delimiter '\t' (ctrl + i)
unload to _tmp_column_comment.unl delimiter ' '
select 'comment on column ' || tabname || '.' || colname || ' is ''' || replace(comments, chr(39), '''''') || ''';' as comment
from syscolcomments;
EOF
if [ -f _tmp_table_comment.unl ]; then
cat _tmp_table_comment.unl > COMM_${DBNAME}.sql
rm -f _tmp_table_comment.unl
fi
if [ -f _tmp_column_comment.unl ]; then
cat _tmp_column_comment.unl >> COMM_${DBNAME}.sql
rm -f _tmp_column_comment.unl
fi
cat << EOF > importdb.sh
#!/bin/bash
# filename : importdb.sh newdbname
if [ \$# -eq 1 ]; then
NEWDBNAME=\$1
fi
OLDDBNAME=${DBNAME}
NEWDBNAME=\${NEWDBNAME:-${DBNAME}}
checkRunDbl(){
RUNDBA=\$(ps -ef | grep "dbload \-d \${NEWDBNAME}" | grep -v grep | wc -l)
if [ \${RUNDBA} -gt \${1:-20} ]; then
return 1
else
return 0
fi
}
_loginfo(){
echo -e "[\$(date +'%F %T')] \$*"
}
if [ -f ${DBNAME}_env_file ]; then
. ./${DBNAME}_env_file
fi
DBEXISTS=\$(echo "select count(1) as count from sysdatabases where name = '\${NEWDBNAME}'" | dbaccess sysmaster 2>/dev/null)
DBEXISTS=\$(echo \${DBEXISTS} | awk '{print \$2}')
if [ \${DBEXISTS} -eq 0 ]; then
_loginfo "ERROR: database \${NEWDBNAME} not exists, please create it !"
_loginfo " e.g: create database \${NEWDBNAME} in datadbs01 with log; "
exit 1
fi
if [ -f ${DBNAME}_package.sql ]; then
dbaccess -e \${NEWDBNAME} ${DBNAME}_package.sql 2> \${NEWDBNAME}_package_error_run.log
if [ ! \$? -eq 0 ]; then
_loginfo "Load package schema error! Please check if Database [ \${NEWDBNAME} ] exists, or/and DDL error/warning at \${NEWDBNAME}_package_error_run.log. "
exit 1
fi
fi
dbaccess -e \${NEWDBNAME} ${DBNAME}.sql 2> \${NEWDBNAME}_error_run.log
if [ ! \$? -eq 0 ]; then
_loginfo "Load schema error! Please check if Database [ \${NEWDBNAME} ] exists, or/and DDL error/warning at \${NEWDBNAME}_error_run.log. "
exit 1
fi
awk -v db="\${NEWDBNAME}" '{sum+=\$3}END{printf("Rows: %d for database: "db" will load.\n",sum)}' BAK_tabname.unl
while read TAB NCOL NROWS DEL
do
if [ x"\$DEL" = x1 ]; then
TMPTAB="\"\$TAB\""
else
TMPTAB="\$TAB"
fi
cat << ! > ./ctl/\${TAB}.ctl 2>/dev/null
FILE '\${TAB}.unl' DELIMITER '|' \${NCOL};
INSERT INTO \${TMPTAB};
!
done < BAK_tabname.unl
while read TAB NCOL NROWS DEL
do
{
dbload -d \${NEWDBNAME} -c ./ctl/\${TAB}.ctl -n 5000 -l ./log/\${TAB}.log
}&
while true
do
checkRunDbl
if [ \$? -eq 0 ]; then
break
else
sleep 5
fi
done
done < BAK_tabname.unl
while true
do
checkRunDbl 0
if [ \$? -eq 0 ]; then
break
else
sleep 5
fi
done
if [ -f IDX_${DBNAME}.sql ]; then
dbaccess -e \${NEWDBNAME} IDX_${DBNAME}.sql > ./log/IDX_\${NEWDBNAME}.log 2>&1
if [ ! \$? -eq 0 ]; then
_loginfo "Create index had error, check ./log/IDX_\${NEWDBNAME}.log"
fi
fi
if [ -f COMM_${DBNAME}.sql ]; then
dbaccess -e \${NEWDBNAME} COMM_${DBNAME}.sql > ./log/COMM_\${NEWDBNAME}.log 2>&1
if [ ! \$? -eq 0 ]; then
_loginfo "Comment on table had error, check ./log/COMM_\${NEWDBNAME}.log"
fi
fi
exit 0
EOF
chmod +x importdb.sh
_loginfo "Finish."
exit 0