如何在Python中高效地将小文件上传到Amazon S3

18

最近,我需要尽快用Python实现一个程序,将存储在Amazon EC2中的文件上传到S3。这些文件的大小为30KB。

我尝试了一些解决方案,包括使用多线程、多进程和协程。以下是我在Amazon EC2上进行性能测试的结果。

3600(文件数)* 30K(文件大小)~~ 105M(总共) --->

       **5.5s [ 4 process + 100 coroutine ]**
       10s  [ 200 coroutine ]
       14s  [ 10 threads ]

以下是代码:

用于多线程

def mput(i, client, files):
    for f in files:
        if hash(f) % NTHREAD == i:
            put(client, os.path.join(DATA_DIR, f))


def test_multithreading():
    client = connect_to_s3_sevice()
    files = os.listdir(DATA_DIR)
    ths = [threading.Thread(target=mput, args=(i, client, files)) for i in range(NTHREAD)]
    for th in ths:
        th.daemon = True
        th.start()
    for th in ths:
        th.join()

关于协程

client = connect_to_s3_sevice()
pool = eventlet.GreenPool(int(sys.argv[2]))

xput = functools.partial(put, client)
files = os.listdir(DATA_DIR)
for f in files:
    pool.spawn_n(xput, os.path.join(DATA_DIR, f))
pool.waitall()

针对多进程和协程的处理

def pproc(i):
    client = connect_to_s3_sevice()
    files = os.listdir(DATA_DIR)
    pool = eventlet.GreenPool(100)

    xput = functools.partial(put, client)
    for f in files:
        if hash(f) % NPROCESS == i:
            pool.spawn_n(xput, os.path.join(DATA_DIR, f))
    pool.waitall()


def test_multiproc():
    procs = [multiprocessing.Process(target=pproc, args=(i, )) for i in range(NPROCESS)]
    for p in procs:
        p.daemon = True
        p.start()
    for p in procs:
        p.join()

机器的配置为Ubuntu 14.04, 2个CPU (2.50GHz), 4G内存

最高速度约为19Mb/s (105 / 5.5)。 总体而言,速度太慢了。有什么方法可以加速吗? 是否可以使用无栈python使其更快?


2
有趣的是,如果我将文件大小设置为1M,我可以获得超过90Mb/s的速度。 - Jacky1205
3
问题比答案更具信息量 =D 感谢协程示例。 - Georgii Oleinikov
3个回答

8

最近我需要将大约5 TB的小文件上传到AWS,通过在~/.aws/config文件中设置更高的“max_concurrent_request”值,我成功地达到了完整的网络带宽750 Mbits(每个服务器1 Gb连接)。

通过在bash for-loop中启动多个上传作业并将这些作业发送到不同的服务器,我进一步加快了上传速度。

我还尝试使用Python例如s3-parallel-put,但我认为这种方法更快。当然,如果文件太小,则应考虑:压缩->上传到EBS / S3并在那里解压缩

以下是可能有所帮助的代码。

$cat .aws/config 
[default]
region = eu-west-1
output = text
s3 =
    max_concurrent_requests = 100

开始多个 AWS 复制作业,例如:

for folder in `ls`; do aws s3 cp $folder s3://<bucket>/$folder/whatever/; done

1
这个解决方案看起来不错,但根本不是用Python编写的。 - Rami
1
当然,我只是这样做是为了让其他人也受益。 - PlagTag
1
@Rami 你可以从Python中调用AWS CLI。 - GG.

8

2
该链接仅展示了多线程和进程,未包括协程以及多进程与协程的组合。在我的测试中,后者将获得更好的性能表现。 - Jacky1205
1
我已经使用简单的shell命令'aws s3 cp myfolder s3://mybucket/myfolder'测试了CLI。它的性能也很差。再次强调,文章“使用Python中的Boto和线程进行并行S3上传”的结果并不准确。作者如何仅使用10个线程就获得了70倍的速度。这太棒了! - Jacky1205
1
我刚刚测试了“使用Python中的Boto和线程进行并行S3上传”的方法,并确认70倍加速不准确。 Python报告我的代码几乎瞬间完成,但是我可以从监视s3上实际情况看到上传仍在后台进行。不确定如何获得此方法的真正准确时间,但它看起来与其他方法相当。 - Sohier Dane
1
@SohierDane 如果您希望脚本等待上传完成,那么您需要在 Python 代码末尾加入进程/线程。这样可以给您准确的时间。否则,线程会与父进程分离并自行完成,因此您的主 Python 脚本会立即退出。 - alfredox

1
我和你有同样的问题。我的解决方案是将数据发送到AWS SQS,然后使用AWS Lambda将它们保存到S3。
因此,数据流如下: 应用程序 -> SQS -> Lambda -> S3
整个过程是异步的,但接近实时 :)

好的解决方案,但它不会有点开销吗?我的意思是为了执行异步上传需要很多非免费的基础设施。 - Imnl
1
是的,肯定有开销。但它完全是异步和可扩展的(而这正是我所需要的)。 - Hkar
@Hkar 但如果我们有数十万个需要上传到S3的小文件,它能否在这种情况下工作? XML文件的最大大小为20kb。 - Atharv Thakur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接