尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux数据拷贝与服务器间文件传输实战避坑指南

Linux数据拷贝与服务器间文件传输实战避坑指南 1. 数据拷贝这件事远比你想的要复杂刚接手服务器运维那会儿我以为拷贝文件就是敲个cp或者scp完事。直到有一次给客户做数据迁移几百个 G 的文件传到一半断了重新传又得从头开始还有一次跨机房同步日志因为没加对参数软链接全变成了实体文件把目标磁盘撑爆了。这些坑踩下来我才明白Linux 下的数据拷贝看着简单但不同场景用什么工具、加什么参数、怎么处理权限和网络问题里面的门道相当多。这篇文章想聊的就是Linux 主机数据拷贝和Linux 服务器之间拷贝文件这两件事。我会把本地拷贝、跨服务器传输、增量同步、断点续传、权限保留、性能调优这些内容都串一遍重点讲清楚每个命令背后为什么要这样用以及我在实际项目中总结出来的避坑经验。不管你是刚接触Linux常用命令的新手还是已经能熟练敲命令但想搞清楚底层逻辑的运维老手这篇内容都能让你在下次遇到数据拷贝任务时少走弯路。整篇内容会从场景分析讲到工具选型再深入到参数细节和问题排查涉及cp、rsync、scp、sftp、tar、nc等多个命令的组合使用。我会尽量用实际案例来说明不会只罗列参数表而是告诉你什么情况下该选什么方案。2. 先搞清楚你要解决的是什么问题2.1 本地主机内部拷贝不只是 cp 那么简单本地拷贝听起来最简单cp source dest谁都会敲。但真正在生产环境里做数据拷贝你会遇到一堆需要思考的问题源目录里有软链接怎么办要不要保留文件的权限、属主、时间戳拷贝过程中如果中断了怎么续目标分区空间不够怎么提前判断我见过太多人直接用cp -r递归拷贝结果目标目录里全是软链接指向的实体文件因为cp -r默认会跟随符号链接。正确的做法是用cp -a这个参数相当于-dR --preserveall的组合会保留链接本身、权限、属主、时间戳等所有属性。如果是跨文件系统拷贝大量数据cp其实不是最优选择因为它的错误处理和进度反馈都比较弱。提示如果源目录里有硬链接cp -a能保留硬链接关系但前提是源和目标在同一个文件系统内。跨文件系统时硬链接无法保留这个要提前有预期。另一个容易被忽略的点是文件系统本身。从 ext4 拷到 XFS或者从本地磁盘拷到 NFS 挂载的网络存储行为可能完全不同。NFS 上的文件锁、权限映射、稀疏文件处理都有各自的坑。我一般会在正式拷贝前先用df -h看目标空间用du -sh估算源大小再用find统计文件数量做到心里有数再动手。2.2 跨服务器传输网络才是最大的变量服务器之间拷文件本质上是把本地磁盘 I/O 变成了网络 I/O 加磁盘 I/O。网络的不稳定性、带宽限制、延迟抖动都会直接影响传输效率和成功率。我经历过最典型的一个场景是从国内机房往海外节点同步备份数据单文件超过 50G用scp传到 80% 断了没有任何续传机制只能从头再来。这就是为什么我后来几乎所有跨服务器传输都优先用rsync。rsync的增量算法可以在断点后只传差异部分对于大文件尤其友好。但rsync也不是万能的它的增量计算在首次传输时并没有优势反而因为要计算校验和会略慢于scp。所以选型的关键在于你是传一次还是反复传文件大不大网络稳不稳定还有一个维度是传输方向。是从本地推到远程还是从远程拉到本地是两台服务器之间直接传还是经过跳板机中转这些都会影响命令的写法和参数选择。比如经过跳板机时可以用 SSH 的 ProxyJump 功能也可以用rsync的-e参数指定 SSH 选项实现穿透传输。2.3 工具选型对比没有最好只有最合适我把常用的几个工具做个横向对比方便你根据场景快速决策工具适用场景断点续传增量传输保留属性速度cp本地拷贝否否支持快scp跨机小文件否否部分中等rsync跨机大文件/增量支持支持完整快sftp交互式操作部分否部分较慢tarnc大量小文件否否支持极快dd磁盘级拷贝支持否完整取决于块大小这个表不是绝对的实际选型还要看具体需求。比如传输几万个小文件时rsync的文件扫描阶段会消耗大量时间反而不如先tar打包再传输来得快。而如果是传输单个超大文件rsync的--partial和--progress组合几乎是最优解。3. 本机数据拷贝的实操细节与参数解析3.1 cp 命令的那些隐藏参数cp是使用频率最高的拷贝命令但很多人只用过cp -r。我把几个关键参数列一下这些都是实际工作中会反复用到的-a归档模式等于-dR --preserveall保留所有属性最常用的拷贝参数-p保留权限、属主、时间戳但不处理软链接-L跟随符号链接把链接指向的内容拷贝过去-d保留符号链接本身不跟随-u只拷贝源比目标新的文件做增量更新时有用-v显示拷贝过程大文件拷贝时建议加上--sparsealways处理稀疏文件时节省空间实际案例从/data/app拷贝到/backup/app要求保留所有权限和链接同时显示进度。命令是cp -av /data/app /backup/app如果目标已存在部分文件只想更新较新的cp -auv /data/app /backup/app注意cp -u判断的是修改时间不是内容差异。如果源文件时间戳被改过但内容没变它也会重新拷贝。真正的内容比对要用rsync -c。还有一个坑拷贝大量小文件时cp不会显示整体进度只有-v会逐文件输出刷屏严重。这时候可以用rsync --progress替代体验好很多。3.2 rsync 本地同步的进阶玩法rsync不仅能跨服务器本地目录同步也非常强大。它的核心优势是增量算法先比较文件大小和修改时间只传输有差异的部分。本地使用时它甚至能利用这个算法做块级增量对超大文件特别有效。一个典型场景是本地备份网站目录要求删除目标中源已删除的文件保留权限显示进度rsync -av --delete --progress /var/www/ /backup/www/注意源路径末尾的斜杠/var/www/表示拷贝目录内容/var/www表示拷贝目录本身。这个细节搞错会导致目录结构多一层。如果要做本地增量同步且不依赖时间戳比如时间戳不可靠的场景可以用校验和模式rsync -avc --progress /source/ /dest/-c会计算文件的校验和来比对比默认的时间戳比对更准确但速度会慢很多。我一般只在时间戳明显不可信时才用。还有一个实用参数是--bwlimit可以限制传输速率避免拷贝任务把磁盘 I/O 或网络带宽占满影响其他服务rsync -av --bwlimit50000 /source/ /dest/这里的单位是 KB/s50000 就是大约 50MB/s。3.3 大文件与海量小文件的处理差异这是实际工作中最容易踩坑的地方。大文件和小文件的拷贝策略完全不同用错了工具效率可能差几十倍。单个大文件几个 G 到几十 G瓶颈在磁盘 I/O 和网络带宽。cp和rsync差别不大但如果中断了rsync --partial --progress可以续传cp只能重来。另外可以用dd配合合适的块大小来提升速度dd if/source/bigfile of/dest/bigfile bs4M statusprogressbs4M是块大小设大一点能减少系统调用次数。但也不是越大越好超过一定值后提升就不明显了一般 1M 到 8M 之间比较合适。海量小文件几万到几百万个瓶颈在文件系统的元数据操作每次open、stat、close都是开销。这种情况下cp -r和rsync都会非常慢因为每个文件都要单独处理。最优方案是先打包再拷贝tar -cf - /source/dir | (cd /dest tar -xf -)这个管道方式避免了中间文件速度比逐个拷贝快很多。如果目标目录已存在且只想更新可以配合rsync但加上--no-whole-file参数优化。我在一次迁移 200 万个图片小文件的项目中直接cp -r跑了六个小时还没完后来用tar管道方式 40 分钟搞定。这个差距就是这么夸张。4. 服务器之间拷贝文件的完整方案4.1 scp 的正确用法与它的局限scp基于 SSH 协议用法直观适合快速传几个文件。基本语法scp /local/file userremote:/remote/path/ scp userremote:/remote/file /local/path/ scp -r /local/dir userremote:/remote/path/常用参数-P指定 SSH 端口注意是大写 P小写 p 是保留时间戳-C启用压缩传输文本类文件时有效-l限制带宽单位 Kbit/s-i指定私钥文件scp最大的问题是没有断点续传。传大文件时如果网络抖动导致中断整个传输就失败了。而且它的速度在大文件场景下不如rsync因为scp的加密和传输是串行的而rsync的增量算法能减少实际传输量。不过scp也有它的优势简单、直接、几乎所有 Linux 都自带。对于配置文件、脚本、小体积日志这类文件scp依然是最快最省事的选择。提示新版 OpenSSH 中scp底层已经改用 SFTP 协议实现行为有些变化比如通配符展开的时机不同。如果发现脚本行为异常可以用scp -O强制使用旧协议排查。4.2 rsync 跨服务器传输的完整配置rsync跨服务器传输是我最推荐的方案没有之一。它的核心优势是增量同步、断点续传、属性保留和灵活的过滤规则。基本命令格式rsync -avz --progress /local/dir/ userremote:/remote/dir/参数说明-a归档模式保留权限、时间戳、软链接等-v详细输出-z传输时压缩对文本类数据有效对已压缩文件如 jpg、zip反而浪费 CPU--progress显示传输进度--partial保留中断的部分文件下次续传--partial-dir.rsync-partial把部分文件放到指定目录更干净完整的生产级命令我一般这样写rsync -avz --progress --partial --partial-dir.rsync-partial \ --bwlimit80000 --exclude*.log --excludetmp/ \ -e ssh -p 2222 -o StrictHostKeyCheckingno \ /data/app/ userremote:/backup/app/这里有几个细节值得展开--partial-dir指定了部分文件的存放目录这样中断后目标目录不会留下不完整的文件下次传输时rsync会自动从这个目录恢复。比单纯的--partial更整洁。--exclude可以多次使用排除不需要传输的文件。也可以用--exclude-fromfile从文件读取排除列表适合规则很多的情况。-e参数指定远程 shell可以用来指定非标准 SSH 端口、私钥、跳板机等。用-e ssh -p 2222比-e ssh加环境变量更直观。如果要从远程拉到本地只需交换源和目标位置rsync -avz --progress userremote:/data/app/ /local/backup/关于断点续传rsync的续传分两个层面。对于未传完的文件--partial保留的部分文件加上--append或--append-verify可以从断点继续。--append假设文件前面部分没变直接从断点追加--append-verify会校验已传部分更安全但稍慢。对于已传完但内容有变化的文件rsync的增量算法会只传差异块。4.3 sftp 交互式操作与批量脚本sftp适合需要交互式浏览、上传下载的场景。进入 sftp 会话后常用命令sftp userremote # 进入后 ls # 列出远程目录 lls # 列出本地目录 cd /remote # 切换远程目录 lcd /local # 切换本地目录 get file # 下载文件 put file # 上传文件 mget *.log # 批量下载 mput *.txt # 批量上传sftp支持-b参数执行批处理脚本适合自动化场景sftp -b commands.txt userremotecommands.txt内容示例cd /remote/backup lcd /local/data put *.tar.gz byesftp的优点是交互友好、支持断点部分客户端缺点是速度较慢不适合大批量文件传输。我一般只在需要手动浏览和选择性下载时用它。4.4 tar 加 nc 的极速传输方案这是一个偏野路子但极其高效的方案特别适合内网环境大量小文件的迁移。原理是一端用tar打包输出到标准输出通过nc直接走网络传输另一端nc接收后直接tar解包。接收端先启动监听nc -l -p 9999 | tar -xf - -C /dest/发送端打包发送tar -cf - -C /source/ . | nc receiver_ip 9999这个方案的优势是没有 SSH 加密开销速度极快tar管道避免了中间文件适合内网可信环境。缺点是没有加密不适合公网没有断点续传需要接收端先启动监听。如果数据敏感可以配合 SSH 隧道使用# 接收端 nc -l -p 9999 | tar -xf - -C /dest/ # 发送端通过 SSH 隧道 ssh -L 9999:localhost:9999 receiver \ tar -cf - -C /source/ . | nc localhost 9999这样数据走 SSH 加密通道安全性有保障速度也比纯scp快因为减少了 SSH 的交互开销。注意nc的版本不同参数差异较大有的用-l -p 9999有的直接nc -l 9999。CentOS 和 Ubuntu 上的行为可能不一样用之前先nc -h确认一下。5. 权限、网络与性能的关键处理5.1 文件权限与属主属组的正确处理跨服务器拷贝时权限问题是最常见的坑之一。cp -a和rsync -a都会尝试保留源文件的属主和属组但如果目标服务器上不存在对应的用户和组就会失败或变成数字 UID/GID。比如源文件属主是www-data:www-dataUID 33目标服务器上没有这个用户rsync -a会报错或者把文件属主设成 33。解决办法有几个方案一用--numeric-ids让rsync直接使用数字 ID不查用户名rsync -avz --numeric-ids /source/ userremote:/dest/方案二用--chown在传输后统一修改属主rsync -avz --chownnginx:nginx /source/ userremote:/dest/方案三如果不需要保留权限用--no-perms --no-owner --no-group让目标文件继承目标目录的权限。我的经验是如果是同构环境两边用户体系一致直接用-a如果是异构环境或者不确定加上--numeric-ids最稳妥后续再按需调整权限。5.2 SSH 免密登录与传输自动化跨服务器传输频繁的话每次输密码太麻烦也影响脚本自动化。配置 SSH 免密登录是基本功# 生成密钥对如果还没有 ssh-keygen -t ed25519 -C backup-key # 拷贝公钥到远程 ssh-copy-id -i ~/.ssh/id_ed25519.pub userremote如果要指定非标准端口ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 userremote高级一点的做法是在~/.ssh/config里配置主机别名简化命令Host backup-server HostName 192.168.1.100 User backupuser Port 2222 IdentityFile ~/.ssh/id_ed25519 StrictHostKeyChecking no配置之后rsync命令可以简化为rsync -avz /data/ backup-server:/backup/这在写自动化脚本时特别有用把连接细节都封装在配置文件里脚本只关心业务逻辑。注意StrictHostKeyChecking no会跳过主机密钥验证方便但降低安全性。生产环境建议配合UserKnownHostsFile指定专用 known_hosts 文件或者先手动验证一次。5.3 传输性能优化与带宽控制传输性能受多个因素影响磁盘读写速度、网络带宽、加密开销、文件数量。优化思路是找到瓶颈所在针对性处理。判断瓶颈传输时用iostat -x 1看磁盘利用率用iftop或nload看网络流量用top看 CPU 占用。如果磁盘利用率接近 100%瓶颈在磁盘如果网络流量跑满带宽瓶颈在网络如果 CPU 的sy系统态很高可能是加密或系统调用开销大。网络优化rsync的-z压缩对文本有效对已压缩文件zip、jpg、mp4反而增加 CPU 负担。内网高带宽环境可以去掉-z。广域网低带宽环境保留-z通常有收益。并发优化rsync本身是单线程的传输大量小文件时可以起多个rsync进程并行处理不同目录。但要注意不要超过磁盘和网络的承受能力。SSH 加密算法选择新版 SSH 默认的加密算法性能已经不错但如果 CPU 较弱且网络带宽很高可以尝试用更快的算法rsync -avz -e ssh -c aes128-gcmopenssh.com /source/ userremote:/dest/aes128-gcm比默认的chacha20-poly1305在有 AES 硬件加速的 CPU 上更快。大文件传输的块大小rsync内部有个--block-size参数默认是自动计算的。对于超大文件适当增大块大小可以减少校验和计算次数但会增加内存占用。我一般保持默认除非有明确的性能问题。6. 常见问题排查与避坑实录6.1 传输中断了怎么办这是最让人头疼的问题。不同工具的处理方式不同scp 中断没有续传机制只能重新传。如果文件特别大建议改用rsync。rsync 中断如果用了--partial或--partial-dir重新执行相同命令会自动续传。rsync会检查目标文件的大小从断点继续。如果同时加了--append会直接从末尾追加速度更快但不校验已有内容。tarnc 中断没有续传只能重来。所以这个方案只适合网络稳定的内网环境。一个实用技巧对于超大文件比如 100G 以上可以先用split切成多个小文件分别传输传输完成后再cat合并。这样即使某个分片失败也只需要重传那一个分片# 切分 split -b 10G bigfile.bin part_ # 传输后合并 cat part_* bigfile.bin6.2 文件名乱码与特殊字符处理跨服务器传输时如果两边的 locale 设置不一致中文文件名可能出现乱码。比如源服务器是zh_CN.UTF-8目标服务器是en_US.ISO-8859-1传输后文件名就乱了。解决办法是确保两边的 locale 一致# 查看当前 locale locale # 临时设置 export LANGzh_CN.UTF-8 export LC_ALLzh_CN.UTF-8对于包含空格、引号、特殊字符的文件名用rsync时要特别注意。rsync支持--protect-args参数可以安全处理特殊字符rsync -avz --protect-args /source/ userremote:/dest/用scp时包含空格的文件名需要转义或者用引号包裹scp my file.txt userremote:/dest/ scp my\ file.txt userremote:/dest/更好的做法是养成好习惯文件名不要包含空格和特殊字符。如果已有这类文件传输前可以用rename批量重命名。6.3 权限拒绝与空间不足的典型报错Permission denied最常见的原因是目标目录没有写权限或者 SSH 密钥权限不对。SSH 对密钥文件权限要求严格~/.ssh/id_rsa必须是 600.ssh目录必须是 700。权限不对会直接拒绝使用密钥。chmod 700 ~/.ssh chmod 600 ~/.ssh/id_rsa chmod 644 ~/.ssh/id_rsa.pub chmod 600 ~/.ssh/authorized_keysNo space left on device目标磁盘满了。传输前一定要用df -h检查目标空间并且预留至少 10% 的余量因为文件系统本身需要空间rsync的临时文件也需要空间。rsync还有一个隐藏问题如果用了--partial-dir部分文件会占用额外空间。目标空间紧张时要注意这个。Too many open files传输海量小文件时可能遇到因为每个文件操作都要打开文件描述符。解决办法是提高 ulimitulimit -n 65535或者修改/etc/security/limits.conf永久生效。6.4 常见问题速查表问题现象可能原因解决办法scp 传输中断无法续传scp 不支持断点续传改用 rsync --partial中文文件名乱码两边 locale 不一致统一 LANG 和 LC_ALLPermission denied (publickey)密钥权限不对chmod 600 密钥文件目标磁盘空间不足未提前检查df -h 检查并清理软链接变成实体文件cp -r 跟随了链接用 cp -a 或 rsync -a传输速度慢瓶颈在磁盘或网络iostat/iftop 定位瓶颈rsync 报错 code 23部分文件权限或属性问题检查 --numeric-ids 和属主文件属主变成数字 UID目标无对应用户用 --numeric-ids 或 --chown大量小文件传输极慢元数据操作开销大先 tar 打包再传SSH 连接超时网络不稳定或防火墙检查网络和端口放行6.5 我踩过的几个印象深刻的坑坑一rsync 删掉了目标目录的额外文件。rsync -av --delete会删除目标中源没有的文件。有一次我源目录写错了差点把整个备份盘清空。后来养成习惯执行带--delete的命令前先用--dry-run跑一遍确认删除列表没问题再正式执行rsync -av --delete --dry-run /source/ /dest/这个--dry-run参数救过我很多次强烈建议所有破坏性操作前都加上。坑二cp -r 把软链接变成了实体文件。源目录里有个软链接指向一个 100G 的数据文件cp -r跟随链接把这个文件也拷了过去直接把目标盘撑爆。正确做法是cp -a或cp -d。坑三rsync 的 -z 压缩拖慢了传输。有次传输一批已经压缩过的视频文件加了-z参数CPU 跑满但传输速度反而比不加还慢。后来才明白对已压缩数据再压缩是纯浪费 CPU。现在我的习惯是文本类加-z二进制和压缩包不加。坑四SSH 连接数限制导致批量传输失败。写了个脚本并发起 50 个rsync进程传输不同目录结果 SSH 服务端达到MaxStartups限制后面的连接全被拒绝。解决办法是控制并发数或者调整服务端的MaxStartups和MaxSessions参数。这些坑的共同点是命令本身没错但对参数的理解不够深入导致在特定场景下出了问题。所以我的建议是每学一个新参数都要搞清楚它在什么情况下该用什么情况下不该用而不是无脑加上去。数据拷贝这件事说到底是对场景的理解加上对工具的熟练。同样一个rsync命令参数加对了效率翻倍加错了可能造成数据丢失。多动手试多用--dry-run多检查df和日志慢慢就能形成自己的肌肉记忆。
返回列表