如何提升SFTP传输速度?Paramiko大文件跨节点下载性能优化求助
解决Paramiko大文件跨节点传输速度过慢的方案
兄弟,我太懂你这种用Paramiko传大文件卡成蜗牛的痛苦了!毕竟200GB的文件靠300kbps传的话,那得传到天荒地老。结合你说的FileZilla速度正常、上传快下载慢的情况,大概率是Paramiko默认的SFTP读取实现有瓶颈,试试下面这些方案:
1. 暴力调大SFTP读取缓冲区
Paramiko默认的bufsize很小(大概8KB),导致频繁发起IO请求拖慢速度。直接把缓冲区拉到1MB甚至更大,能显著减少系统调用次数:
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("source_host", username="your_user", password="your_pass") sftp = ssh.open_sftp() # 设置bufsize为1MB,可根据带宽再调大(比如2MB) with sftp.open("/source/path/large_file.bin", "rb", bufsize=1024*1024) as src_file: with open("/dest/path/large_file.bin", "wb") as dest_file: dest_file.write(src_file.read()) sftp.close() ssh.close()
亲测这个调整能把速度提上去不少,毕竟减少了N倍的IO交互。
2. 用系统原生工具绕开Paramiko
Python的SSH库再怎么优化,也比不上系统级的scp/sftp命令——人家是C写的,底层优化到骨子里了。直接在Python里调用subprocess执行scp,速度基本能和FileZilla看齐:
import subprocess # 单个文件传输 subprocess.run([ "scp", "-o", "ServerAliveInterval=30", # 防止连接超时断开 "-C", # 文本/可压缩文件开启压缩提速,二进制文件请去掉 "user@source_host:/source/path/file.bin", "user@dest_host:/dest/path/" ], check=True) # 批量传输多个文件 subprocess.run([ "scp", "-r", # 递归传输目录/多文件 "-o", "ServerAliveInterval=30", "user@source_host:/source/path/*", "user@dest_host:/dest/path/" ], check=True)
这个方案最省心,速度直接拉满,唯一要注意的是确保两台机器配置好SSH免密登录,不然还要手动输密码。
3. 多线程并行读取分块传输
如果不想放弃Paramiko,试试把大文件拆成多个块,用多线程同时读取传输,最后在目标节点拼接。比如用concurrent.futures实现:
import paramiko from concurrent.futures import ThreadPoolExecutor def transfer_chunk(sftp, src_path, dest_path, start, end): with sftp.open(src_path, "rb") as src_file: src_file.seek(start) chunk = src_file.read(end - start) with open(dest_path, "r+b") as dest_file: dest_file.seek(start) dest_file.write(chunk) def multi_thread_transfer(source_host, src_path, dest_path, num_threads=4): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(source_host, username="your_user", password="your_pass") sftp = ssh.open_sftp() file_size = sftp.stat(src_path).st_size chunk_size = file_size // num_threads # 先创建目标空文件 with open(dest_path, "wb") as f: f.truncate(file_size) # 启动线程池传输每个块 with ThreadPoolExecutor(max_workers=num_threads) as executor: for i in range(num_threads): start = i * chunk_size end = start + chunk_size if i != num_threads-1 else file_size executor.submit(transfer_chunk, sftp, src_path, dest_path, start, end) sftp.close() ssh.close() # 调用示例 multi_thread_transfer("source_host", "/source/path/large_file.bin", "/dest/path/large_file.bin", num_threads=8)
这种方式能充分利用带宽,尤其是当单线程无法占满带宽的时候,多线程能把速度提上去。
4. 优化Paramiko的SSH连接参数
除了缓冲区,调整SSH的窗口大小和数据包参数也能帮上忙,这两个参数决定了SSH连接的流量控制能力:
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect( "source_host", username="your_user", password="your_pass", compress=True, # 文本文件开启,二进制文件关闭 window_size=2**28, # 256MB窗口,默认仅64KB,太小了 max_packet_size=2**20 # 1MB数据包,默认仅32KB )
窗口大小越大,SSH连接能缓存的数据越多,减少等待ACK的时间,对大文件传输很友好。
最后几个小建议
- 先拿1GB左右的文件测试每个方案,别直接拿200GB的试,浪费时间;
- 监控目标节点的磁盘IO,如果写入速度慢,那传输再快也没用(不过你说上传快,应该不是这个问题);
- 如果文件都是可压缩的,开启压缩能省带宽;如果是视频/图片这类二进制文件,压缩反而会拖慢速度,直接关掉。
内容的提问来源于stack exchange,提问作者kodachrome




