You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何提升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

火山引擎 最新活动