dul 11的研究
* dul的名气比较大,做oracle恢复的人都知道这个工具,但是这个工具是国外的开发的,发布的版本一般人也是下载不到的,类似这类的工具有很多,国内常见的有odu、aul、prmdul等。
* 最早的时候我是用过9和10的版本,当时恢复的是单纯的堆表,没有大字段的数据,导出的时候有两种选择,第一是csv,第二是dmp格式,但是我发现一个问题是如果用户的表较多,就会产生多个dmp出来,那么再加上恢复数据就变得更加复杂,曾经我用它在windows环境下恢复过几千个表,当时写的是windows的批处理脚本。过了几年,现在大部分都是11g~19c的环境,再用这个版本就不太合适。
* 关于该工具的使用方法网上有很多,我这里不做太多的介绍,主要讲一下它的弊端吧,对中文的clob支持不太友好,导出的是二进制的unicode编码,但是导进去是乱码,目前我还没解决,有相关经验的可以帮我解解惑。
* 关于该工具的使用特别要注意的是不同的版本都有时间的限制,如果想用对应的版本就要修改软件发布的对应的时间,另外还要修改文件的创建时间。这块在windows上比较麻烦,linux上很好处理,下面文章我有介绍。
* 下面正式进入实验:
一、环境说明
1、1 环境
[oracle@his dul]$ ./dul11205
Data UnLoader: 11.2.0.5.0 - Internal Only - on Mon May 1 00:25:22 2017
with 64-bit io functions and the decompression option
Copyright (c) 1994 2017 Bernard van Duijnen All rights reserved.
Strictly Oracle Internal Use Only
Cannot start now
You need a more recent DUL version for this os --这里可以看到必须使用最近的dul,怎么解决呢?观察上面的Copyright (c) 1994 2017 Bernard van Duijnen All rights reserved. 只需要把操作系统时间改为2017年的,然后数据文件也改到那个时间。

再次打开dul就可以了


二、测试过程
2.1 配置dul说明
* 用过dul类似的工具都知道,dul的配置分为2个文件,一个是init.dul文件,另外一个是数据库的路径结构信息的文件control.dul(可自定义,按照init.dul配置的修改)。
2.2 配置init.dul
vi init.dul
OSD_BIG_ENDIAN_FLAG=FALSE --大端还是小端
osd_dba_file_bits=10
osd_c_struct_alignment=32
osd_file_leader_size=1
OSD_WORD_SIZE=32
dc_columns=2000000
dc_tables=100000
dc_objects=100000
dc_users=8000
dc_segments=1000000
db_block_size=8192 --数据块尺寸
control_file=/dul/control.dul --控制文件的位置
export_mode=true --导出成dmp
#export_mode=false --导出成csv,即sqlldr加载的文件
compatible=10 --兼容性参数,是什么版本就改成什么
buffer=200000000
#FILE_SIZE_IN_MB=1000
file=zlhis

2.3 配置control.dul
set heading off verify off trimspool on tab off echo off feedback off
set trimout on
set space 1 pagesize 0 linesize 999
set numwidth 3
select ts# || ' '|| rfile# || ' '|| name || ' block_size '|| block_size from v$datafile;
vi control.dul

2.4 初始化数据库字典信息

初始化后,在/dul目录下生成的文件信息

[oracle@his dul]$ ll
total 15448
-rw-r--r-- 1 oracle oinstall 752 May 1 00:09 ATTRIBUTE.ctl --属性
-rw-r--r-- 1 oracle oinstall 1192966 May 1 00:09 ATTRIBUTE.dat
-rw-r--r-- 1 oracle oinstall 332 May 1 00:09 BOOTSTRAP.ctl --引导
-rw-r--r-- 1 oracle oinstall 18907 May 1 00:09 BOOTSTRAP.dat
-rw-r--r-- 1 oracle oinstall 950 May 1 00:09 COL.ctl --列
-rw-r--r-- 1 oracle oinstall 6627420 May 1 00:09 COL.dat
-rw-r--r-- 1 oracle oinstall 754 May 1 00:09 COLLECTION.ctl
-rw-r--r-- 1 oracle oinstall 127466 May 1 00:09 COLLECTION.dat
-rw-r--r-- 1 oracle oinstall 608 May 1 00:09 COLTYPE.ctl
-rw-r--r-- 1 oracle oinstall 162407 May 1 00:09 COLTYPE.dat
-rw-r--r-- 1 oracle oinstall 392 May 1 00:09 ICOL.ctl
-rw-r--r-- 1 oracle oinstall 175558 May 1 00:09 ICOL.dat
-rw-r--r-- 1 oracle oinstall 810 May 1 00:09 IND.ctl --索引的控制文件
-rw-r--r-- 1 oracle oinstall 288360 May 1 00:09 IND.dat --索引的数据文件
-rw-r--r-- 1 oracle oinstall 334 May 1 00:09 INDCOMPART.ctl
-rw-r--r-- 1 oracle oinstall 0 May 1 00:09 INDCOMPART.dat
-rw-r--r-- 1 oracle oinstall 678 May 1 00:09 INDPART.ctl
-rw-r--r-- 1 oracle oinstall 5563 May 1 00:09 INDPART.dat
-rw-r--r-- 1 oracle oinstall 684 May 1 00:09 INDSUBPART.ctl
-rw-r--r-- 1 oracle oinstall 0 May 1 00:09 INDSUBPART.dat
-rw-r--r-- 1 oracle oinstall 880 May 1 00:09 LOB.ctl --lob相关信息
-rw-r--r-- 1 oracle oinstall 66978 May 1 00:09 LOB.dat
-rw-r--r-- 1 oracle oinstall 336 May 1 00:09 LOBCOMPPART.ctl
-rw-r--r-- 1 oracle oinstall 0 May 1 00:09 LOBCOMPPART.dat
-rw-r--r-- 1 oracle oinstall 608 May 1 00:09 LOBFRAG.ctl
-rw-r--r-- 1 oracle oinstall 40 May 1 00:09 LOBFRAG.dat
-rw-r--r-- 1 oracle oinstall 600 May 1 00:09 OBJ.ctl --对象
-rw-r--r-- 1 oracle oinstall 4720362 May 1 00:09 OBJ.dat
-rw-r--r-- 1 oracle oinstall 254 May 1 00:09 PROPS.ctl --数据库字符集等信息
-rw-r--r-- 1 oracle oinstall 1108 May 1 00:09 PROPS.dat
-rw-r--r-- 1 oracle oinstall 880 May 1 00:09 TAB.ctl --表
-rw-r--r-- 1 oracle oinstall 238130 May 1 00:09 TAB.dat
-rw-r--r-- 1 oracle oinstall 334 May 1 00:09 TABCOMPART.ctl --表分区相关
-rw-r--r-- 1 oracle oinstall 19 May 1 00:09 TABCOMPART.dat
-rw-r--r-- 1 oracle oinstall 678 May 1 00:09 TABPART.ctl
-rw-r--r-- 1 oracle oinstall 4498 May 1 00:09 TABPART.dat
-rw-r--r-- 1 oracle oinstall 684 May 1 00:09 TABSUBPART.ctl
-rw-r--r-- 1 oracle oinstall 1431 May 1 00:09 TABSUBPART.dat
-rw-r--r-- 1 oracle oinstall 318 May 1 00:09 TS.ctl --表空间
-rw-r--r-- 1 oracle oinstall 729 May 1 00:09 TS.dat
-rw-r--r-- 1 oracle oinstall 392 May 1 00:09 TYPE.ctl --type
-rw-r--r-- 1 oracle oinstall 233062 May 1 00:09 TYPE.dat
-rw-r--r-- 1 oracle oinstall 532 May 1 00:09 UNDO.ctl --undo段
-rw-r--r-- 1 oracle oinstall 9721 May 1 00:09 UNDO.dat
-rw-r--r-- 1 oracle oinstall 252 May 1 00:09 USER.ctl --用户表
-rw-r--r-- 1 oracle oinstall 1778 May 1 00:09 USER.dat
-rwxrwxr-x 1 oracle oinstall 1904 Apr 1 2019 control.dul
-rw-r--r-- 1 oracle oinstall 6429 May 1 00:09 dict.ddl --数据字典
-rw-r--r-- 1 oracle oinstall 20163 May 1 00:09 dul.log --dul允许过程中的日志
-rwxrwxr-x 1 oracle oinstall 851656 Apr 1 2019 dul11205
-rwxrwxr-x 1 oracle oinstall 901896 Apr 1 2019 dul12.2
-rwxrwxr-x 1 oracle oinstall 383 May 1 00:03 init.dul
----dul允许过程中的日志这个文件是很重要的,它记录了dul启动后参数的配置情况和对应的版本里面的参数,如果不清楚对应版本的参数,从中可以找到。
----USERS.dat可以了解到改库的schema信息,可以为unload user做参考。

2.5 按表导出测试


--从上面可以看到,dul在linux下对中文的表单独导出支持不太友好,不知道是不是字符集环境设置得不对,我改了NLS_LANG\LANG都不行。
--导出英文表是可以得,导出完成后会在库下按照表名生成对应的dmp文件。

2.6 按库导出测试
DUL> unload user zlhis;

--上面可以看到表很多都是中文的,但是导出后去/dul目录,没有一张中文的表;

--为了解决这个中文的问题,我调整了参数:

--删除/dul目录下的.dmp文件,再次unload user zlhis;

--以上数据导出后,在导入的时候怎么弄,肯定不能单个文件去导入吧,所以我又写了脚本...
三、导入结果
3.1 准备相同版本的数据库,新建表空间、新建用户。
这一步简单,大家都会,跳过。
3.2 脚本
##方法1##
function read_dir(){
read -t 30 -p "输入路径: " d
read -t 30 -p "输入用户: " u
read -t 30 -p "输入用户密码: " p
rm /tmp/imp.sh &>/dev/null
for file in `ls $d/*.dmp`
do
#echo 正在执行---------file=$file 的$u导入------------------
echo imp $u/$p file=$file log=$file"."log full=y feedback=10000 buffer=10240000 commit=y ignore=y >>/tmp/imp.sh
done
}
daoru() {
i=0;
cat /tmp/imp.sh|while read line
do
if [ "$line" != "" ] && [ $i -lt 10 ];then
$line &
let i++;
processnum=`ps -ef|grep imp|grep -v grep|wc -l`
if [ $processnum -eq 10 ];then
wait
fi
elif [ "$line" != "" ] && [ $i -lt 20 ];then
$line &
let i++;
processnum=`ps -ef|grep imp|grep -v grep|wc -l`
if [ $processnum -eq 10 ];then
wait
fi
else
$line &
processnum=`ps -ef|grep imp|grep -v grep|wc -l`
if [ $processnum -eq 10 ];then
wait
fi
fi
done
wait
}
read_dir
daoru
##方法2##
#!/bin/bash
start_time=$(date +"%Y-%m-%d %H:%M:%S")
echo "Script started at $start_time"
USER="zlhis"
PASSWORD="his"
DUMP_DIR="/dul/dump1"
LOG_DIR="/dul/dump1"
FAILED_TASKS="$LOG_DIR/failed_tasks.txt"
MAX_CONCURRENT=100
#要小于数据库允许的并发,否则要提前修改 alter system set processes=1000 scope=spfile;
mkdir -p "$LOG_DIR" > "$FAILED_TASKS"
pids=() # 用于存储后台任务的 PID 列表
for dumpfile in "$DUMP_DIR"/*.dmp; do
dumpname=$(basename "$dumpfile" .dmp)
logfile="$LOG_DIR/$dumpname.log"
echo "开始导入: $dumpfile -> $logfile"
# 启动任务并将 PID 存储到数组中
(
imp userid=$USER/$PASSWORD file="$dumpfile" log="$logfile" full=y feedback=10000 buffer=10240000 commit=y ignore=y &>/dev/null
if [ $? -ne 0 ]; then
echo "$dumpfile" >> "$FAILED_TASKS"
fi
) &
pids+=($!) # 将后台任务 PID 添加到数组
# 检查并发数
while [ "${# pids[ @ ]}" -ge "$MAX_CONCURRENT" ]; do
for i in "${! pids[ @ ]}"; do
if ! kill "${ pids[i ]}" 2>/dev/null; then
unset pids[i ] # 移除已完成的任务
fi
done
pids=("${pids[ @ ]}") # 重建数组(移除空值)
sleep 1 # 等待片刻后再检查
done
done
# 等待所有剩余任务完成
for pid in "${ pids[ @ ]}"; do
wait "$pid"
done
if [ -s "$FAILED_TASKS" ]; then
echo "以下任务导入失败,请检查日志:"
cat "$FAILED_TASKS"
else
echo "所有任务成功完成!"
fi
end_time=$(date +"%Y-%m-%d %H:%M:%S")
echo "Script ended at $end_time"
ps -ef|grep dmp|wc -l
[oracle@his dump]$ ll *.dmp |wc -l
1045
[oracle@his dump]$ ll *.log|wc -l
1045
检查日志:
find ./ -name "*.log" |xargs grep -A 2 'importing table'|more
3.23 脚本导入



--导入过程中另外的窗口也可以打开观察进程和日志情况





--表已经进来。
如果有报错,可以看对对应的日志;

或者看dmp导入过程中的日志;
find ./ -name "*.log" |xargs grep -A 2 'importing table'|more
四、clob测试导出导入
1、创建带有CLOB字段的表:
sqlplus zlhis/his
CREATE TABLE clob_test (
id NUMBER,
clob_column CLOB
);
2、插入数据
INSERT INTO clob_test (id, clob_column) VALUES (1, TO_CLOB('这是一个CLOB字段的文本数据,啊发吉萨的房间里撒地方阿斯蒂芬'));
alter system flush buffer_cache;
3、查询数据
SELECT id, clob_column FROM zlhis.clob_test;
4、编写vi init.dul
OSD_BIG_ENDIAN_FLAG=FALSE
osd_dba_file_bits=10
osd_c_struct_alignment=32
osd_file_leader_size=1
OSD_WORD_SIZE=32
dc_columns=20000000
dc_tables=10000000
dc_objects=10000000
dc_users=8000
dc_segments=10000000
db_block_size=8192
control_file=/dul/control.dul
export_mode=true
#export_mode=false
compatible=11
buffer=2000000000
#FILE_SIZE_IN_MB=1000
file=zlhis
#LDR_OUTPUT_IN_UTF8=TRUE
5、dmp导出测试
show datafiles;
bootstrap;
scan database;
DUL> desc zlhis.clob_test;
Table ZLHIS.CLOB_TEST
obj#= 97187, dataobj#= 97191, ts#= 4, file#= 4, block#=301946
tab#= 0, segcols= 2, clucols= 0
Column information:
icol# 01 segcol# 01 ID len 22 type 2 NUMBER(0)
icol# 02 segcol# 02 CLOB_COLUMN len 4000 type 112 CLOB cs 852(ZHS16GBK)
LOB Segment: dataobj#= 97188, ts#= 4, file#= 4, block#=318850 chunk=1
LOB Index: dataobj#= 97189, ts#= 4, file#= 4, block#=318858
unload table zlhis.clob_test;
exit;
6、dmp导入测试
sqlplus zlhis/his
truncate table clob_test;
imp zlhis/his file=/dul/ZLHIS_CLOB_TEST.dmp log=/dul/ZLHIS_CLOB_TEST.log full=y feedback=10000 buffer=10240000 commit=y ignore=y
SELECT id, clob_column FROM clob_test;
[oracle@his dul]$ env|grep -i lang
NLS_LANG=american_america.ZHS16GBK
LANG=zh_CN.UTF-8
zh_CN
zh_CN.gb18030
zh_CN.gb2312
zh_CN.gbk
zh_CN.utf8
export LANG=zh_CN.gbk
7、sqlldr导出测试
修改init.dul
export_mode=false
sqlldr zlhis/his control=ZLHIS_CLOB_TEST.ctl
再次导入,还是乱码。网上搜索了下,看了两篇文章,还是没能解决这个问题
--https://tool.ip138.com/ascii/
--http://www.killdb.com/2014/09/14/%e5%a6%82%e4%bd%95%e8%a7%a3%e5%86%b3oracle-dul%e6%81%a2%e5%a4%8dclob%e6%97%b6%e4%b8%ad%e6%96%87%e4%b9%b1%e7%a0%81%e9%97%ae%e9%a2%98%ef%bc%9f/
[oracle@his dul]$ iconv -l|grep UCS
10646-1:1993/UCS4/
CSUCS4//
ISO-10646-UCS-2//
ISO-10646/UCS2/
ISO-10646/UCS4/
UCS-2//
UCS-2BE//
UCS-2LE//
UCS-4//
UCS-4BE//
UCS-4LE//
UCS2//
UCS4//
[oracle@his dul]$ iconv -l|grep GB
CN-GB//
CSGB2312//
CSISO58GB1988//
EBCDIC-CP-GB//
GB//
GB2312//
GB13000//
GB18030//
GBK//
GB_1988-80//
GB_198880//
ISO646-GB//
iconv -f UCS-4LE -t gb2312 ZLHIS_CLOB_TEST.dat > 1
cat 1


看了上面链接的文章,分析了数据,是unicode编码,用的"分割每列,空格一列后又是下一个字段,最后一个字段后是回车。通过iconv -f UCS-4LE -t gb2312 ZLHIS_CLOB_TEST.dat > 1还是不行。
五、总结
1、单表导出,测试中文不支持,要改为使用用户;unload 全库会遇到DUL: FATAL Error: UTF16 can only be unloaded if LDR_OUTPUT_IN_UTF8=TRUE
2、lob字段对中文支持不友好,可能是外国佬写的原因吧。
3、init.dul里面的最重要的几个参数要清楚,特别是不同版本的数据库这里要修改,不然bootstrap生成的内容不准确,数据就无法导出。
4、dul类似的工具是最后一步,通过上面的操作也能看到这仅仅是把数据导出来,然后恢复,其实还有约束、索引、触发器、函数等等工作需要做,这也就要求必须要有应用厂商的人配合恢复,或者要有一个对应版本的测试库环境。
写到最后,提醒各位做好备份,技术再高,也怕挨刀。




