多线程分段下载文件时,为什么不下载到同一个大文件中?而是要分别下载到单独的文件然后再合并。

2024-05-11 10:54:04 +08:00
 lsk569937453

https://gist.github.com/lsk569937453/e0496754cf3ebe740a33a7759516015e

我自己写了个 demo ,假设开 50 个线程下载,每个线程下载时间为 10s 钟,可以看到最后写入文件的时间才 1s 不到。

所以下载文件时,瓶颈是网络 IO 吧,操作文件 IO 的时间可以忽略不计了。所以是不是下载到一个文件中更优?

15196 次点击
所在节点    程序员
103 条回复
Karte
2024-05-11 10:57:15 +08:00
1. 文件占用
2. 内容覆盖
Te11UA
2024-05-11 10:58:15 +08:00
> 所以下载文件时,瓶颈是网络 IO 吧,操作文件 IO 的时间可以忽略不计了。所以是不是下载到一个文件中更优?

这一句话不是前后冲突吗
kenvix
2024-05-11 10:59:00 +08:00
没有为什么,就是写代码的太菜,连 pre-allocate+seek 都不会
ppllss
2024-05-11 10:59:30 +08:00
感觉是利用网速吧。很多时候 1 个文件下载就 100KB/s 那么要下载好久。分多个文件,每个文件 100KB/s 不是更快了?

为什么 1 个文件下载就 100KB/s 不清楚呀。带宽几百 M 下载一个文件我也没有跑满
kenvix
2024-05-11 10:59:34 +08:00
实际上除了 IDM 主流下载器都是不需要合并的
huixia0010
2024-05-11 11:00:04 +08:00
动动脑子啊,万一其中一片数据出问题要重下的时候,你一个文件里怎么搞?
kenvix
2024-05-11 11:01:00 +08:00
@huixia0010 #6 动动脑子啊,你说的这种问题分多个文件就能解决?
jifengg
2024-05-11 11:06:59 +08:00
首先,迅雷、FDM ,都是下载到同一个大文件里的。
这种方式,首先需要申请这个文件的空间,也就是你刚开始下载 10g 的文件,立刻就要在硬盘里创建 10g 的文件。
然后,写数据是一个持续的过程,多线程需要自己调度好文件占用的问题。你 demo 是等 10 秒后一次性写入。
分段需要处理临时文件,各有优劣,看自己更熟悉哪种方式了。
AceDogs
2024-05-11 11:11:13 +08:00
文件分开可以解决更多普遍的问题,而且文件名是天生的索引信息,支持的功能更广泛。
单一文件在在特定情况下确实很好,比如知道文件总大小 等等。个人感觉好像单一文件的优势不是特别多。
结合一下也是一种方案, 用较小的缓冲区不断下载, 同时完成的数据合并入大文件中。实现方案各有优缺点。
yazinnnn0
2024-05-11 11:11:39 +08:00
感觉下载到一个大文件没啥优势呢
tool2dx
2024-05-11 11:12:17 +08:00
@ppllss http/tcp 单线程下载有两个大问题,一个是掉包,另一个是延迟。任何一个有问题,下载速度就会断崖式下跌。

当年 TCP 设计有问题,才会出现 BBR 那种内核补丁。
kenvix
2024-05-11 11:14:33 +08:00
@jifengg #8 我以前做过多线程下载器,关于文件分配,直接向 OS 申请预分配一块存储空间就行了,预写入 10G 那是 HDD 时代老 Windows 的东西。关于线程调度,直接开个 BlockingQueue<<Index, ByteBuffer>>然后由专门的 Writer 负责写就行了,其他下载线程只需要负责把下到的 block(比如设定每个 block 为 512K)挂到 queue 上就完事

IDM 那种分文件下载再合并,对于大文件下载来说是不可接受的开销。除了菜逼我不知道还有什么理由会这样做
lsk569937453
2024-05-11 11:15:44 +08:00
@yazinnnn0 省去了合并文件的时间以及删除临时文件的时间。代码上更简洁了。。。
lsk569937453
2024-05-11 11:19:20 +08:00
@huixia0010 有一个分片出问题,那么重新下载这个分片,然后文件 seek 到指定的位置,重写那个分片就可以了。
Karte
2024-05-11 11:20:43 +08:00
多线程多文件时是由下载线程写入到文件中. 如果是多线程单文件则是由单独的线程负责写入.

多线程多文件设计简单, 下载完直接写入. 最后由最后一个线程负责文件合并.

多线程单文件则是高阶写法, 需要控制写入的位置之类的.

别一口一个菜逼, 都是从菜鸟过来的. 只有适合的方案, 没有完美的设计.
mayli
2024-05-11 11:30:29 +08:00
一般都是写到一个大文件吧,下载到单独文件,相当于 IO 两次,除了某些场景下,现在的 OS 对于这个没有特殊差别。
但是对于真的大文件,比如 10GB 以上的,你这种写两遍的操作会占用两倍的磁盘空间,而且对于 HDD ,一边读一边写会巨慢。
所以,这么做,单纯的是菜(不会 seek )。
IvanLi127
2024-05-11 11:31:40 +08:00
只要有写文件的缓冲区,往一个大文件里写正常是更优解,毕竟事后合并文件的话磁盘 IO 还得吃一波。

话说回来到底啥软件会存碎片文件最后再合并
lltlo
2024-05-11 11:36:39 +08:00
同一机器多线程下载没啥可玩的,有没有成熟的多机器同时下载同一个文件的工具?公司网络做了限制,每个终端有带宽有限,所以想用多台 pc 来下同一个文件的不同部分,最后合并起来,类似于分布式下载工具。
github 上找了款 distributed-downloader ,有些小问题,自己改吧改吧,算是可以用,但是不太方便。
augustheart
2024-05-11 11:39:04 +08:00
纯看写代码的怎么想。通常情况下,下到同一个文件中都是最优解。
但是,在不知道最终文件多大但是又可以多线下载的时候(流媒体可能碰到),分块下载最后合并应该是一个相对稳妥的办法。
MrKrabs
2024-05-11 12:21:32 +08:00
去问写程序的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1039715

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX