一.问题描述

某项目生产环境在使用达梦数据库时,使用dmfldr导出表T_XF_WTXX 640w数据 大概需要两个小时,其 中导出后的dat文件(普通字段文件)大小2.1G,大字段文件14G

二.问题分析

表结构T_XF_WTXX 含有三个CLOB大字段,使用测试环境复现含有大字段的DB_XFJFW.T_XF_WTXX表导 出,观察导出过程,发现当导出记录到150w时导出速度明显变慢, 此时cpu利用率不高,磁盘写入也没 有等待,猜测是含有大字段的原因。对此做验证:测试不导出表的大字段导出时间需要375024.903ms (6.25min), 全部导出完成需要4699080.456ms(1.3h),证明导出慢和大字段有关。

[root@YTHZGHT2 fldr]# cat t_xf_wtxx.ctrl
OPTIONS (
READ_ROWS = 5000
skip = 0
ERRORS = 0
)
LOAD DATA
INFILE '/home/dmdba/fldr/DB_XFJFW.t_xf_wtxx.dat' STR X '0D0A'
APPEND
INTO TABLE DB_XFJFW.T_XF_WTXX
FIELDS '$'
(C_BH, ...)
[root@YTHZGHT2 fldr]# /home/dmdba/dmdbms/bin/dmfldr
userid=SYSDBA/SYSDBA@ip:5236
control=\'/home/dmdba/fldr/t_xf_wtxx.ctrl\' character_code=\'utf-8\'
lob_directory=\'/home/dmdba/fldr\' lob_file_name=\'DB_XFJFW.t_xf_wtxx.lob\'
mode=\'out\'
dmfldr V8
26387 rows is load out
51615 rows is load out
76566 rows is load out
101518 rows is load out
126463 rows is load out
151422 rows is load out
176370 rows is load out
201326 rows is load out
226276 rows is load out
251230 rows is load out
276146 rows is load out
300981 rows is load out
325821 rows is load out
350659 rows is load out
375491 rows is load out
400332 rows is load out
425168 rows is load out
450015 rows is load out
474850 rows is load out
499689 rows is load out
524525 rows is load out
549357 rows is load out
574196 rows is load out
599030 rows is load out
623881 rows is load out
648702 rows is load out
673538 rows is load out
698363 rows is load out
723192 rows is load out
748038 rows is load out
772873 rows is load out
798738 rows is load out
831670 rows is load out
860244 rows is load out
886639 rows is load out
912376 rows is load out
937702 rows is load out
965309 rows is load out
996297 rows is load out
1027922 rows is load out
1052769 rows is load out
1077603 rows is load out
1110349 rows is load out
1135254 rows is load out
1160150 rows is load out
1185060 rows is load out
1209958 rows is load out
1235242 rows is load out
1262237 rows is load out
1289217 rows is load out
1316196 rows is load out
1343183 rows is load out
1370163 rows is load out
1397139 rows is load out
1424134 rows is load out
1451042 rows is load out
1477878 rows is load out
1505039 rows is load out
1532202 rows is load out
1559362 rows is load out
1586414 rows is load out
1613470 rows is load out
1640506 rows is load out
1667560 rows is load out
1694605 rows is load out
1721648 rows is load out

同时使用脚本监测导出文件变化

java 达梦属性大写 达梦数据库大字段_sql


对导出过程中文件写入速度数据做分析,得出以下图表

java 达梦属性大写 达梦数据库大字段_mysql_02


从图表中可得知,对于普通字段(导出后为.dat文件),导出速度较为平稳; 而对于大字段的导出(导出后为lob文件),时间越久,速度会越慢。

三.解决方案

1.对数据量大的表分批导出

从问题分析中可知,导出记录达到一定数量时会变慢,因此利用sql选项设置查询语句,限制查询条数, 从而将大数据量表拆分成多个任务导出.一共用时18min18s

[root@YTHZGHT2 fldr]# cat T_XF_WTXX0.ctrl
OPTIONS (
READ_ROWS = 5000
skip = 0
ERRORS = 0
SQL = 'SELECT C_BH, ... FROM DB_XFJFW.T_XF_WTXX order by C_BH limit
437500 offset 0'
)
LOAD DATA
INFILE '/home/dmdba/fldr/DB_XFJFW.T_XF_WTXX1.dat' STR X '0D0A'
APPEND
INTO TABLE DB_XFJFW.T_XF_WTXX
FIELDS '$'
(C_BH, ...)
[root@YTHZGHT2 fldr]# cat wtxx_out.sh
/home/dmdba/dmdbms/bin/dmfldr userid=SYSDBA/SYSDBA@ip:5236
control=\'/home/dmdba/fldr/T_XF_WTXX0.ctrl\' character_code=\'utf-8\'
lob_directory=\'/home/dmdba/fldr\' lob_file_name=\'DB_XFJFW.T_XF_WTXX0.lob\'
log=\'/home/dmdba/fldr\' mode=\'out\' >> T_XF_WTXX0.log
/home/dmdba/dmdbms/bin/dmfldr userid=SYSDBA/SYSDBA@ip:5236
control=\'/home/dmdba/fldr/T_XF_WTXX1.ctrl\' character_code=\'utf-8\'
lob_directory=\'/home/dmdba/fldr\' lob_file_name=\'DB_XFJFW.T_XF_WTXX1.lob\'
log=\'/home/dmdba/fldr\' mode=\'out\' >> T_XF_WTXX1.log
......
[root@YTHZGHT2 fldr]# sh wtxx_out.sh
总共导出 437500 行数据
用时:125340.040(ms)
总共导出 437500 行数据
用时:128830.880(ms)
总共导出 437500 行数据
用时:130286.686(ms)
总共导出 437500 行数据
用时:135571.581(ms)
总共导出 437500 行数据
用时:139830.011(ms)
总共导出 437500 行数据
用时:144251.783(ms)
总共导出 437500 行数据
用时:148284.039(ms)
总共导出 413227 行数据
用时:143810.542(ms)
startTime: 2022年 08月 05日 星期二 21:00:05 CST
endTime: 2022年 08月 05日 星期二 21:18:23 CST

2.利用多核cpu多进程导出

在数据库服务器多核cpu的条件下,为提升效率,多进程同时导出,经测试,用时7min12s

[root@YTHZGHT2 fldr]# sh generatectrl.sh
437500
8
2
4
1
3
5
6
7
8
总共导出 437500 行数据
用时:404997.500(ms)
总共导出 413227 行数据
用时:412821.554(ms)
总共导出 437500 行数据
用时:416868.575(ms)
总共导出 437500 行数据
用时:422614.471(ms)
总共导出 437500 行数据
用时:425621.073(ms)
总共导出 437500 行数据
用时:427365.440(ms)
总共导出 437500 行数据
用时:429723.847(ms)
总共导出 437500 行数据
用时:431494.616(ms)
startTime: 2022年 08月 05日 星期二 21:21:23 CST
endTime: 2022年 08月 05日 星期二 21:28:35 CST

3.多进程导出文件写入速度分析

由图可知,任务拆分后多进程导出会提高文件写入速度,从而提高导出效率

java 达梦属性大写 达梦数据库大字段_sql_03

四.可能存在的问题

验证过程中发现目标表数据和原表数据稍有差异,原因是导出过程中数据发生了变动,导致limit分组SQL数据发生错位,从而出现数据重复。

多进程无法保证数据一致性,因此在数据导出查询期间不能修改表数据。若要保证表数据一致性,开启一 个事务,手动锁表,执行完导出过程后提交。

如果不想锁表,可以改写SQL根据hash值取余分组导出,这样就不会有数据错位的问题,但是无法保证数据一致性。

--导出前
LOCK TABLE tablename IN SHARE MODE;
--导出完成后
commit;

附录

多进程导出脚本

[root@YTHZGHT2 fldr]# cat template_T_XF_WTXX.ctrl
OPTIONS (
READ_ROWS = 5000
skip = 0
ERRORS = 0
SQL = 'SELECT C_BH, * FROM DB_XFJFW.T_XF_WTXX
order by C_BH limit ${size} offset ${begin}'
)
LOAD DATA
INFILE '${FLDR_DATA_PATH}/DB_XFJFW.T_XF_WTXX${index}.dat' STR X '0D0A'
APPEND
INTO TABLE DB_XFJFW.T_XF_WTXX
FIELDS '$'
(C_BH, ...)
[dmdba@YTHZGHT2 fldr]$ cat wtxx_count.sql
SET HEADING OFF TIMING OFF LINESHOW OFF;
select count(*) from DB_XFJFW.T_XF_WTXX;
EXIT;
[dmdba@YTHZGHT2 fldr]$ cat generatectrl.sh
#!/bin/bash
DM_PATH=/home/dmdba/dmdbms
disql=SYSDBA/SYSDBA@172.16.35.199:5236
FLDR_DATA_PATH=/home/dmdba/fldr
SCHEMA_NAME=DB_XFJFW
TABLE_NAME=T_XF_WTXX
total=3500000
total=$(${DM_PATH}/bin/disql -S $disql \`wtxx_count.sql)
#分成几个任务来导出这个表
parallelthrds=8
index=0
size=$(($total/$parallelthrds))
#echo $size
begin=0
for((i=0;i<${parallelthrds};i++));
do
begin=$(($i*$size))
index=$i
eval "cat <<EOF
$(< template_${TABLE_NAME}.ctrl)
EOF
" > ${TABLE_NAME}${i}.ctrl
done
rm -rf command.txt
for((i=0;i<${parallelthrds};i++));
do
echo "$DM_PATH/bin/dmfldr userid=$disql
control=\'${FLDR_DATA_PATH}/${TABLE_NAME}${i}.ctrl\' character_code=\'utf-8\'
lob_directory=\'${FLDR_DATA_PATH}\'
lob_file_name=\'${SCHEMA_NAME}.${TABLE_NAME}${i}.lob\' log=\'${FLDR_DATA_PATH}\'
mode=\'out\' >> ${TABLE_NAME}${i}.log " >>command.txt
done
#任务数
all_num=$((parallelthrds))
#可用线程数
thread_num=8
a=$(date)
#通过seq 1 ${all_num} 组合出一组以换行或者空格组成的数据,从而来控制总共需要执行后面脚本的次
数.然后通过-n 来控制传递给shell的参数个数,通过xargs -P控制最大进程数,从而实现多线程并发
seq 1 ${all_num} | xargs -n 1 -I {} -P ${thread_num} sh -c 'v=`echo {}`;echo
$v;myfile=$(cat command.txt|sed -n ${v}p);eval $myfile'
b=$(date)
echo -e "startTime:\t$a"
echo -e "endTime:\t$a"