文件夹中数百万个(小的)文本文件


15

我们希望在Linux文件系统中存储数百万个文本文件,目的是能够压缩并提供任意集合作为服务。我们尝试了其他解决方案,例如键/值数据库,但是对并发性和并行性的要求使使用本机文件系统成为最佳选择。

最直接的方法是将所有文件存储在一个文件夹中:

$ ls text_files/
1.txt
2.txt
3.txt

在EXT4文件系统上应该是可能的,而EXT4文件系统对文件夹中的文件数没有限制。

这两个FS过程将是:

  1. 从网络抓取中写入文本文件(不受文件夹中文件数量的影响)。
  2. 压缩所选文件,由文件名列表给出。

我的问题是,在一个文件夹中存储多达一千万个文件是否会影响上述操作的性能或整个系统的性能,这与为文件驻留一个子文件夹树有什么不同?


4
相关:当设备有足够的空间时,如何在mv期间解决间歇性“设备上没有剩余空间”错误。使用dir_index(默认情况下通常启用)会加快查找速度,但可能会限制每个目录中的文件数。
Mark Plotnick

为什么不在虚拟机上快速尝试一下,看看它是什么样的呢?使用bash可以很容易地在文件夹中填充一百万个带有随机字符的文本文件。我觉得除了您将在这里学到的东西之外,您还将通过这种方式获得真正有用​​的信息。
JoshuaD

2
@JoshuaD:如果将它们一次全部填充到新的FS上,则可能会将所有inode邻接在磁盘上,因此目录中每个inode的ls -l其他任何操作stat(例如,bashglobbing / tab补全)都将被人为地加快。而不是经过一些磨损(删除一些文件,编写一些新文件)。ext4在此方面可能比XFS更好,因为XFS为i节点和数据动态地分配空间,所以我认为i节点可能会更加分散。(但这是基于很少的详细知识的纯猜测;我几乎没有使用ext4)。与abc/def/子目录一起。
彼得·科德斯

是的,我认为我建议的测试不能告诉OP“这将起作用”,但是肯定可以很快告诉他“这将不起作用”,这很有用。
JoshuaD

1
但是我们对并发性和并行性的要求使使用本机文件系统成为最佳选择 。您尝试了什么?副手,我认为即使是像MySQL和Java servlet这样的低端RDBMS都可以即时ZipOutputStream创建zip文件,也几乎可以击败任何免费的Linux本地文件系统-我怀疑您想为IBM的GPFS买单。处理JDBC结果集并使其成为zip流的循环可能仅是6至8行Java代码。
安德鲁·亨利

Answers:


10

ls命令,甚至shell的TAB补全或通配符扩展,通常将以字母数字顺序显示其结果。这需要阅读整个目录列表并对其进行排序。在单个目录中有一千万个文件时,此排序操作将花费不可忽略的时间。

如果您可以抵制TAB补全的冲动,例如编写要完整压缩的文件名,则应该没有问题。

通配符的另一个问题可能是通配符扩展可能会产生比最大长度命令行所容纳的文件名更多的文件名。在大多数情况下,典型的最大命令行长度将绰绰有余,但是当我们在单个目录中谈论数百万个文件时,这不再是一个安全的假设。在通配符扩展中超过最大命令行长度时,大多数shell只会使整个命令行失败而不执行它。

这可以通过使用以下find命令进行通配符操作来解决:

find <directory> -name '<wildcard expression>' -exec <command> {} \+

或尽可能使用类似的语法。在find ... -exec ... \+将自动考虑到最大命令行的长度,并且将根据需要多次执行该命令,同时配合的文件名的最大数量为每行命令。


现代文件系统使用B,B +或类似的树来保留目录条目。en.wikipedia.org/wiki/HTree
昏暗[

4
是的...但是,如果外壳程序或ls命令不知道目录列表已被排序,则无论如何它们将花费时间来运行排序算法。此外,用户空间可能使用的本地化排序顺序(LC_COLLATE)可能与文件系统内部执行的操作不同。
telcoM

17

这很接近基于观点的问题/答案,但是我将尝试用我的观点提供一些事实。

  1. 如果文件夹中有大量文件,则任何尝试枚举这些文件的基于shell的操作(例如mv * /somewhere/else)都可能无法成功扩展通配符,或者结果可能太大而无法使用。
  2. ls 枚举大量文件将比枚举少量文件花费更长的时间。
  3. 文件系统将能够在单个目录中处理数百万个文件,但人们可能会很努力。

一种建议是将文件名分成两个,三个或四个字符块,然后将其用作子目录。例如,somefilename.txt可能存储为som/efi/somefilename.txt。如果您使用的是数字名称,则从右向左拆分,而不是从左向右拆分,以使分布更均匀。例如,12345.txt可能存储为345/12/12345.txt

您可以使用的等效项zip -j zipfile.zip path1/file1 path2/file2 ...来避免在ZIP文件中包含中间子目录路径。

如果您是从Web服务器提供这些文件的(我不完全确定这是否相关),则可以使用Apache2中的带有重写规则的虚拟目录来隐藏此结构,以支持虚拟目录。我认为Nginx也是如此。


*扩建将成功,除非你的内存用完,但除非你提出堆栈大小限制(在Linux上)或使用shell那里mv是内置的,也可以是内置(ksh93的,zsh中),该execve()系统调用可能会失败E2BIG的错误。
斯特凡Chazelas

@StéphaneChazelas是的,我对单词的选择可能更好,但是对用户的最终效果是差不多的。我将看看我是否可以稍稍更改这些单词,而不会陷入复杂性的泥潭。
roaima

只是好奇如果您避免在其中包含中间子目录路径而又不涉及所讨论的问题,将如何解压缩该zip文件?
Octopus,

1
@Octopus OP声明该zip文件将包含“ 所选文件,由文件名列表给出 ”。
roaima

我建议使用zip -j - ...并将输出流直接通过管道传递到客户端的网络连接zip -j zipfile.zip ...。将实际的zipfile写入磁盘意味着从磁盘读取数据路径->压缩->写入磁盘->从磁盘读取->发送至客户端。通过从磁盘->压缩->发送到客户端读取,可以将磁盘IO需求提高三倍
安德鲁·亨利

5

我经营一个处理电影,电视和视频游戏数据库的网站。对于其中的每一个,电视都有多个图像,每个节目包含数十个图像(例如,情节快照等)。

最终会有很多图像文件。在25万以上的范围内。这些都存储在访问时间合理的已安装块存储设备中。

我第一次存储图像的尝试是在一个文件夹中 /mnt/images/UUID.jpg

我遇到了以下挑战。

  • ls通过远程终端将只是挂起。该过程将变得僵尸,CTRL+C并且不会破坏它。
  • 在达到这一点之前,任何ls命令都会迅速填充输出缓冲区,CTRL+C并且不会停止无休止的滚动。
  • 从单个文件夹压缩250,000个文件大约需要2个小时。您必须运行与终端分离的zip命令,否则连接中断将意味着您必须重新开始。
  • 我不会冒险尝试在Windows上使用zip文件。
  • 该文件夹很快成为一个禁止人员进入的区域。

我最终不得不使用创建时间来将文件存储在子文件夹中来创建路径。如/mnt/images/YYYY/MM/DD/UUID.jpg。这解决了上述所有问题,并允许我创建针对日期的zip文件。

如果文件的唯一标识符是数字,则这些数字倾向于按顺序运行。为什么不将它们分组100000100001000

例如,如果您有一个名为384295.txt的文件,则路径为:

/mnt/file/300000/80000/4000/295.txt

如果您知道您将达到几百万。使用01,000,000的前缀

/mnt/file/000000/300000/80000/4000/295.txt

1

从网络抓取中写入文本文件(不受文件夹中文件数量的影响)。

要创建新文件,需要扫描目录文件,以寻找足够的空白空间来存储新目录条目。如果找不到足够大的空间来存储新目录条目,则它将放置在目录文件的末尾。随着目录中文件数量的增加,扫描目录的时间也会增加。

只要目录文件保留在系统缓存中,这样做的性能就不会太差,但是如果释放数据,则从磁盘读取目录文件(通常是高度碎片化的)可能会花费大量时间。SSD可以改善这一点,但是对于包含数百万个文件的目录,性能仍然可能会受到明显影响。

压缩所选文件,由文件名列表给出。

在包含数百万个文件的目录中,这也可能需要额外的时间。在具有哈希目录条目(例如EXT4)的文件系统中,这种差异很小。

一个文件夹中最多存储一千万个文件是否会影响上述操作的性能或整个系统的性能,这与为文件驻留一个子文件夹树有什么不同?

子文件夹树没有以上性能缺点。此外,如果将基础文件系统更改为不具有哈希文件名,则树方法仍然可以正常工作。


1

首先:防止'ls'与'ls -U'一起排序,也许将〜/ bashrc更新为具有'alias ls =“ ls -U”'或类似名称。

对于大型文件集,您可以这样尝试:

  • 创建一组测试文件

  • 查看是否有许多文件名引起问题

  • 使用xargs parmeter-batching和zip的(默认)行为将文件添加到zip中,以避免出现问题。

这很好用:

# create ~ 100k files
seq 1 99999 | sed "s/\(.*\)/a_somewhat_long_filename_as_a_prefix_to_exercise_zip_parameter_processing_\1.txt/" | xargs touch
# see if zip can handle such a list of names
zip -q /tmp/bar.zip ./*
    bash: /usr/bin/zip: Argument list too long
# use xargs to batch sets of filenames to zip
find . -type f | xargs zip -q /tmp/foo.zip
l /tmp/foo.zip
    28692 -rw-r--r-- 1 jmullee jmullee 29377592 2017-12-16 20:12 /tmp/foo.zip
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.