如何提升Linux下Shell脚本跨服务器复制XML文件的性能?
批量处理XML文件的性能优化方案
你的脚本当前处理5.5万个文件耗时5小时,核心瓶颈在于单文件循环操作带来的重复开销:每次scp都要重新建立SSH连接、每条SQL单独执行commit、单线程串行处理。针对200万级别的文件量,以下是具体优化措施:
1. 替换scp为rsync批量传输
scp每次传输单个文件都要新建SSH连接,这是最大的耗时点。rsync可以通过一次SSH连接完成所有文件传输,还支持压缩、增量传输,效率提升数倍。
优化实现:
先收集待处理文件列表,批量传输后统一移动到备份目录:
# 生成临时文件列表存储待处理XML tmp_file_list=$(mktemp) ls -1 "$origin"/*.xml > "$tmp_file_list" # 批量传输文件,--remove-source-files会在传输成功后删除源文件 rsync -avzC --files-from="$tmp_file_list" --remove-source-files "$origin/" "$username@$ip:$destination/" # 批量移动已传输文件到备份目录并添加时间戳 while read -r file; do name=${file%.*} name=${name##*/} save="${name}_$dt.xml" mv -f -- "$origin/$file" "$backup/$save" done < "$tmp_file_list" rm -f "$tmp_file_list"
2. 合并SQL语句,减少数据库交互
原脚本每个文件生成3条update+1次commit,200万文件会产生600万条SQL+200万次commit,数据库IO开销极大。
优化实现:
- 将三个update合并为一条,减少SQL数量
- 所有update完成后只执行一次commit(或每1000条commit一次,避免事务过大)
# 清空旧SQL文件 > /flexsync/update.sql # 添加SQL环境配置 cat >> /flexsync/update.sql <<EOF SET DEFINE OFF; EOF # 批量生成合并后的update语句 while read -r file; do name=${file%.*} name=${name##*/} fname="${name}.xml" cat >> /flexsync/update.sql <<EOF UPDATE TRC_ISTOILOVA.X_EISERVER_ASSETS_CURR_10_24_2023 SET FLEXSYNC='YES', FLEXSYNC_DT=SYSTIMESTAMP, LAST_UPDATE_TIME=SYSTIMESTAMP WHERE FILE_NAME='$fname'; EOF done < "$tmp_file_list" # 最后统一提交事务 cat >> /flexsync/update.sql <<EOF COMMIT; EOF
3. 并行处理提升吞吐量
单线程处理无法充分利用多核CPU和网络带宽,使用parallel或xargs实现并行传输/处理:
并行传输示例(用parallel):
# 先安装parallel(CentOS:yum install parallel;Debian/Ubuntu:apt install parallel) ls -1 "$origin"/*.xml | parallel -j 10 scp -C -- {} "$username@$ip:$destination/"
-j 10表示同时开启10个并行进程,可根据服务器CPU和带宽调整(建议从5-10开始测试,避免资源过载)。
4. 其他细节优化
- 环境变量复用:如果脚本是定时任务,可将Oracle环境变量放到用户
~/.bash_profile中,减少脚本内重复导出 - 错误处理:添加rsync、sqlplus执行结果校验,避免部分步骤失败后继续执行
- 分批次处理:200万文件建议分批次处理(比如每10万一批),避免一次性占用过多系统资源
优化后的完整脚本示例
#!/usr/bin/env bash export ORACLE_HOME=/u01/app/oracle/product/19.3.0/db export LD_LIBRARY_PATH=/u01/app/oracle/product/19.3.0/db/lib export PATH=$PATH:$ORACLE_HOME/bin username="eip" origin="/flexsync" backup="/flexsync/processed/processedAssets" destination="/home/eip2/data/synchronization/flexsync/in" ip="abc.def.xyz" dt=$(date +%Y%m%d%H%M%S) # 打印配置信息 echo "Oracle Home: $ORACLE_HOME" echo "Oracle Library: $LD_LIBRARY_PATH" echo "Oracle Path: $PATH" echo "Source Directory: $origin" echo "Source Backup directory: $backup" echo "Target Directory: $destination" echo "Target System: $ip" # 生成临时文件列表 tmp_file_list=$(mktemp) ls -1 "$origin"/*.xml > "$tmp_file_list" if [ ! -s "$tmp_file_list" ]; then echo "No XML files to process." rm -f "$tmp_file_list" exit 0 fi echo "Starting batch file transfer..." # 批量传输文件 rsync -avzC --files-from="$tmp_file_list" --remove-source-files "$origin/" "$username@$ip:$destination/" if [ $? -ne 0 ]; then echo "File transfer failed, exiting." rm -f "$tmp_file_list" exit 1 fi echo "Moving processed files to backup..." # 批量移动并重命名 while read -r file; do name=${file%.*} name=${name##*/} save="${name}_$dt.xml" mv -f -- "$origin/$file" "$backup/$save" done < "$tmp_file_list" echo "Generating batch update SQL..." # 生成批量SQL > /flexsync/update.sql cat >> /flexsync/update.sql <<EOF SET DEFINE OFF; EOF while read -r file; do name=${file%.*} name=${name##*/} fname="${name}.xml" cat >> /flexsync/update.sql <<EOF UPDATE TRC_ISTOILOVA.X_EISERVER_ASSETS_CURR_10_24_2023 SET FLEXSYNC='YES', FLEXSYNC_DT=SYSTIMESTAMP, LAST_UPDATE_TIME=SYSTIMESTAMP WHERE FILE_NAME='$fname'; EOF done < "$tmp_file_list" cat >> /flexsync/update.sql <<EOF COMMIT; EOF echo "Updating database table..." sql_file="/flexsync/update.sql" /u01/app/oracle/product/19.3.0/db/bin/sqlplus -S abc/xyz@def:2222/def_svc @"$sql_file" if [ $? -eq 0 ]; then echo "Table updated successfully." else echo "Database update failed." fi rm -f "$tmp_file_list" echo "File upload and processing completed! ;)"
内容的提问来源于stack exchange,提问作者user3792741
相关产品推荐
相关产品推荐

