在文件系统中存储一百万个图像


79

我有一个可以生成大量图像的项目。大约需要1,000,000。它们不是大图像,因此我一开始将它们全部存储在一台计算机上。

您如何建议有效地存储这些图像?(当前为NTFS文件系统)

我正在考虑一种命名方案...首先,所有图像的名称都将从1开始递增。我希望这会在以后需要时帮助我对它们进行排序,并将它们放入不同的文件夹中。

什么是更好的命名方案:

a / b / c / 0 ... z / z / z / 999

要么

a / b / c / 000 ... z / z / z / 999

有什么想法吗?


1
他们是绑定到特定用户还是只是普通用户?他们以任何方式分组吗?

仅通用。一堆由一些技术设备生成的图像。我将它们命名为从1开始递增,只是想了解一下时间参考。
s.mihai

如何使用/访问它们?通过定制的应用程序还是什么?
鸽子

16

1
:))是的...一百万 色情图片:))
s.mihai

Answers:


73

我建议使用常规文件系统而不是数据库。使用文件系统比使用数据库更容易,您可以使用常规工具访问文件,文件系统是为这种使用而设计的,等等。NTFS可以很好地用作存储系统。

不要存储数据库的实际路径。最好将图像的序列号存储到数据库中,并具有可以根据序列号生成路径的功能。例如:

 File path = generatePathFromSequenceNumber(sequenceNumber);

如果您需要以某种方式更改目录结构,则更容易处理。也许您需要将图像移动到其他位置,也许空间不足,并且开始将某些图像存储在磁盘A和磁盘B上,等等。更改一个功能比更改数据库中的路径更容易。

我将使用这种算法来生成目录结构:

  1. 首先,请使用前导零填充序列号,直到您至少有12位数字字符串为止。这是您文件的名称。您可能要添加一个后缀:
    • 12345 -> 000000012345.jpg
  2. 然后将字符串拆分为2个或3个字符块,其中每个块表示一个目录级别。具有固定数量的目录级别(例如3):
    • 000000012345 -> 000/000/012
  3. 将文件存储到生成的目录下:
    • 因此,具有序列ID的文件的完整路径和文件文件名123000/000/012/00000000012345.jpg
    • 对于具有序列ID 12345678901234的文件,路径为123/456/789/12345678901234.jpg

有关目录结构和文件存储的一些注意事项:

  • 上面的算法为您提供了一个系统,其中每个叶目录最多包含1000个文件(如果总数少于1000000000000个文件)
  • 一个目录可以包含多少个文件和子目录可能会有限制,例如,Linux上的ext3文件系统每个目录最多只能有31998个子目录。
  • 如果每个目录中有大量文件(> 1000),则常规工具(WinZip,Windows Explorer,命令行,bash shell等)可能无法很好地工作。
  • 目录结构本身会占用一些磁盘空间,因此您不需要太多目录。
  • 使用上述结构,如果您碰巧弄乱了目录结构,则总是可以通过查看文件名来找到图像文件的正确路径。
  • 如果需要从多台计算机访问文件,请考虑通过网络文件系统共享文件。
  • 如果您删除大量文件,以上目录结构将不起作用。它在目录结构中留下了“漏洞”。但是由于您没有删除任何文件,因此应该可以。

1
很有意思!分割文件名...我没想到。我认为这是一种优雅的方式:-?
s.mihai

37
使用哈希(例如MD5)作为文件名以及目录分发将有效。文件的完整性不仅会给命名方案带来好处(易于检查),而且还会在整个目录层次结构中合理分配。因此,如果您有一个名为“ f6a5b1236dbba1647257cc4646308326.jpg”的文件,则可以将其存储在“ / f / 6”中(或按需要存储)。2层深提供256个目录,对于最初的1m个文件,每个目录不足4000个文件。自动化重新分配到更深层次的方案也非常容易。

+1我刚刚注意到这个答案与我刚刚发布的答案相似。
3dinfluence

1
我绝对同意使用文件系统并创建人为的标识符来“切片”成文件夹名称。但是,您还应该尝试获得标识符的随机分布,即不要使用序列号。这将使您的文件夹树更加平衡。此外,通过随机分发,您可以更轻松地在多个文件系统之间对树进行分区。我还将使用基于ZFS的SAN,并启用dedup,并为每个文件系统设置稀疏卷。您仍可以通过使用iSCSI来访问SAN来使用NTFS。
Michael Dillon

如果在步骤2中从右到左移动文件,则文件将平均分配。另外,您不必担心没有填满足够的零,因为可以无限数量的文件
ropo 2016年

31

我将把我的2美分价值用于一条负面建议:不要使用数据库。

我使用图像存储数据库已有多年历史:大型(1兆-> 1兆)文件,经常更改,文件的多个版本,合理地访问。您在存储大文件时遇到的数据库问题非常繁琐,写入和事务问题十分棘手,并且您遇到了锁定问题,这可能会导致严重的火车事故。我必须以书面形式DBCC脚本,然后从备份中恢复的表比任何正常的人应该多练习永远有。

我使用过的大多数较新的系统都将文件存储推送到文件系统,并且仅依靠数据库就可以建立索引。文件系统旨在解决这种滥用问题,它们更易于扩展,并且如果一个条目损坏,您几乎不会丢失整个文件系统。


是。注意!
s.mihai

5
您是否看过SQL 2008的FILESTREAM数据类型?这是数据库和文件系统存储之间的交叉。
2009年

在执行快速和不频繁的IO操作时,坚持使用文件服务器而不是数据库+1。

如果每个数据库仅存储数百个文档或图片,该怎么办?使用数据库进行存储有何不利之处?
哔哔声,

1
+1 ...文件系统无论如何都是一种“数据库”(可以肯定是ntfs),所以为什么要使其过于复杂。
akira 2010年

12

我认为大多数必须处理此问题的网站都使用某种哈希值,以确保文件均匀分布在文件夹中。

假设您有一个类似这样的文件的哈希,515d7eab9c29349e0cde90381ee8f810
您可以将其存储在以下位置,并且可以使用需要将其存储多少个级别来将每个文件夹中的文件数量保持在较低水平。
\51\5d\7e\ab\9c\29\349e0cde90381ee8f810.jpg

我已经看过这种方法很多次了。您仍然需要一个数据库来将这些文件哈希映射到人类可读的名称以及您需要存储的其他任何元数据。但是这种方法可以很好地扩展b / c,您可以开始在多个计算机和/或存储池之间分配哈希地址空间。


2
Git使用类似的方法:git-scm.com/book/en/v2/Git-Internals-Git-Objects(以支持此答案)
aexl

11

理想情况下,您应该对各种结构的随机访问时间进行一些测试,因为特定的硬盘驱动器设置,缓存,可用内存等可能会改变这些结果。

假设您拥有文件名的控制权,我将其分区为每个目录1000s。添加的目录级别越多,刻录的索引节点越多,因此这里有一个推挽式的。

例如,

/ root / [0-99] / [0-99] /文件名

请注意,http://technet.microsoft.com/zh-cn/library/cc781134(WS.10).aspx包含有关NTFS设置的更多详细信息。特别是,“如果您在NTFS文件夹中使用大量文件(300,000或更多),请禁用短文件名生成以获得更好的性能,尤其是长文件名的前六个字符相似时。”

您还应该研究禁用不需要的文件系统功能(例如,上次访问时间)。 http://www.pctools.com/guides/registry/detail/50/


3
+1用于禁用8.3文件名生成和上次访问时间;当我阅读“大量的[文件]”和“ NTFS”(Windows)时,这些是第一件事。
罗布

链接........................
Pacerier's

7

无论您做什么,都不要将它们全部存储在一个目录中。

根据这些图像名称的分布,您可以创建一个目录结构,在该目录结构中您具有单个字母顶级文件夹,在该文件夹中您将拥有另一组第二个图像字母的子文件夹,等等。

所以:

文件夹img\a\b\c\d\e\f\g\将包含以“ abcdefg”开头的图像,依此类推。

您可以介绍自己所需的适当深度。

此解决方案的优点在于,目录结构有效地充当了哈希表/字典。给定图像文件名,您将知道它的目录,给定目录,您将知道去那里的图像的子集。


\ a \ b \ c \ d \ e \ f \我现在正在做,我在想有一种明智的方法。
s.mihai

1
这是如何物理存储它们的公认方法。清楚地生成图像URL可以轻松地基于图像文件名动态地完成。另外,要提供它们,甚至可以在图像服务器上引入img-a和img-b子域,以加快加载时间。

2
+1表示“不要将它们全部存储在一个目录中”。我正在支持一个旧系统,该系统在一个文件夹中的服务器上放置了超过47000个文件,而Explorer只需一分钟即可打开该文件夹。
Mark Ransom

5
进行\ b \ c \ d \ e \ f \ g会使目录结构变得很深,每个目录仅包含几个文件。最好在每个目录级别使用多个字母,例如ab \ cd \ ef \或abc \ def \。目录还占用磁盘空间,因此您不需要太多目录。
JuhaSyrjälä09年

2
我必须支持一个目录中有4百万个文件的应用程序;它工作得非常好,但是您永远都无法打开资源管理器来打开文件夹,它将不断地对新添加的内容进行排序。+1使NTFS能够不死地处理它。
SqlACID 2010年

5

我会将它们存储在文件系统中,但这取决于文件数量增长的速度。这些文件托管在网络上吗?有多少用户可以访问这些文件?这些是我需要给您更好的建议之前需要回答的问题。我还要看一下Facebook上的Haystack,他们对于存储和提供图像有很好的解决方案。

同样,如果您选择文件系统,则需要使用目录对这些文件进行分区。我一直在研究此问题,并提出了一种解决方案,但无论如何它都不是一个完美的解决方案。我按哈希表进行分区,用户可以在我的博客上阅读更多内容。


这些图像不适合经常访问。因此,这没有问题。他们的人数将增长很快。我认为会有100万。在1个月内完成标记。
s.mihai

我对程序员的观点很感兴趣,所以我不会对此考虑得太多
s.mihai

因此,如果您不需要快速访问,Haystack可能不适合您。在我看来,使用目录分区是最简单的解决方案。
卢卡斯,

5

我们有一个包含400万张图片的照片存储系统。我们仅将数据库用于元数据,并且所有图像都使用反向命名系统存储在文件系统中,在该系统中,文件夹名称是从文件的最后一位,last-1等生成的。例如:000001234.jpg存储在目录结构中,例如4 \ 3 \ 2 \ 1 \ 000001234.jpg。

该方案与数据库中的身份索引非常匹配,因为它均匀地填充了整个目录结构。


4

快速点,您不需要在DB中存储文件路径。如果文件以您描述的方式命名,则可以只存储一个数值。然后,使用已经讨论过的一种定义明确的存储方案,您可以将索引作为数字来获取,并且可以通过遍历目录结构来快速找到文件。


:-?好快点。只是现在我没有生成路径的算法。
s.mihai


4

您的图像需要唯一命名吗? 生成这些图像的进程能否多次产生相同的文件名?在不知道哪个设备正在创建文件名的情况下很难说,但要说该设备已“重置”,并且在重新启动后,它开始像上次“重置”时那样开始命名映像-如果是这样的话。

另外,您说您将在一个月的时间内拍摄100万张图像。那之后呢? 这些图像将继续以多快的速度填充文件系统? 他们会在某个时候达到顶峰并达到约100万张图像的水平,还是会继续逐月增长?

我问,因为您可以按月,然后按映像开始设计文件系统。我可能会建议您将图像存储在这样的目录结构中:

imgs\yyyy\mm\filename.ext

where: yyyy = 4 digit year
         mm = 2 digit month

example:  D:\imgs\2009\12\aaa0001.jpg
          D:\imgs\2009\12\aaa0002.jpg
          D:\imgs\2009\12\aaa0003.jpg
          D:\imgs\2009\12\aaa0004.jpg
                   |
          D:\imgs\2009\12\zzz9982.jpg
          D:\imgs\2010\01\aaa0001.jpg (this is why I ask about uniqueness)
          D:\imgs\2010\01\aab0001.jpg

月,年,甚至一天都适合使用安全性类型的图像。不确定这是您要做什么,但我是使用家庭安全摄像头做到的,该摄像头每10秒拍摄一张照片...这样,您的应用程序可以向下钻取到特定时间甚至您认为图像生成的范围。或者,而不是年,月-可以从图像文件本身派生出其他“含义”吗?除我提供的日期示例外,还有其他一些描述符吗?

我不会将二进制数据存储在数据库中。这种事情从来没有有好的表现/运气。不能想象它可以处理100万张图像。我将存储文件名,就是这样。如果它们全部都是JPG,那么甚至不存储扩展名。我将创建一个控制表,该表存储了指向文件服务器,驱动器,路径等的指针。通过这种方式,您可以将这些图像移动到另一个框并仍然找到它们。 您是否需要用关键字标记图像? 如果是这样,那么您将要构建允许这种标记的适当表。

您/其他人在我回复时可能已经解决了这些想法。希望这会有所帮助。


1.所有文件都将被唯一命名2.系统将首先增长并增长,它将获得100万张左右的图像,然后以每月成千上万的速度增长。3.将来的某个时候会有某种文件的标记,这就是为什么我要在数据库中存储某种识别数据的原因。
s.mihai

3

我参与了一个项目,该项目在一年中存储了840万张图像,用于记录各种设备的状态。除非发现了促使某人深入档案的条件,否则更频繁地访问更新的图像,并且很少寻找较旧的图像。

基于此用法,我的解决方案是将图像逐步压缩为压缩文件。图像是JPG,每个大约20kB,并且压缩程度不高,因此ZIP压缩方案没有。这样做仅是为了将它们连接到一个文件系统条目中,这在从驱动器到驱动器之间移动它们或查看文件列表时,在速度方面极大地帮助了NTFS。

一天以上的图像将合并为“每日” zip;超过一个月的拉链合并为各自的“每月”拉链;最后,一年中不再需要任何东西,因此将其删除。

该系统运行良好,因为用户可以浏览文件(通过操作系统或许多客户端应用程序),并且所有内容均基于设备名称和时间戳进行命名。通常,用户知道这两条信息,并且可以快速定位数百万个图像中的任何一个。

我了解这可能与您的特定细节无关,但我想我会分享。


2

也许是基于创建日期的命名方案-要么在文件名中包含所有信息,要么(最好稍后浏览)将其​​拆分到目录中。我可以根据您生成图像的频率来考虑以下几点:

  • 每天生成几张图像: Year/Month/Day/Hour_Minute_Second.png
  • 一个月几个: Year/Month/Day_Hour_Minute_Second.png

等等。你明白我的意思... =)


它们不会随着时间的推移而持续生成,因此某些文件夹会变胖,而其他文件夹会保持...苗条:))
s.mihai 2009年

好吧,显然您不必仅因为遵循此方案而创建每个文件夹。您甚至可以拥有Year/Month/Day/Hour/Minute-根据速率最高时生成图像的频率来决定所需的文件夹级别,然后不要创建将留空的文件夹。
Tomas Aschan 09年

2

我倾向于创建一个基于日期的文件夹结构,例如\ year \ month \ day,并为文件名使用时间戳。如果要如此快地创建图像,以至在一毫秒内可能有多个图像,则时间戳可以具有附加的计数器成分。通过使用最高有效到最低有效的顺序进行命名排序,查找和维护变得轻而易举。例如hhmmssmm [seq] .jpg


2

您在考虑灾难恢复吗?

这里提出的一些解决方案最终会修改文件名(这样,如果物理文件被移动,您将无法真正知道它是什么文件)。我建议维护一个唯一的物理文件名,以便如果文件位置的主列表损坏,则可以使用小shell,er,powershell,script来重新生成它;)

从我在这里阅读的内容看来,所有这些文件都将存储在一个文件系统中。考虑将它们存储在多台计算机上的多个文件系统中。如果有足够的资源,请确定一种将每个文件存储在两台不同计算机上的系统,以防万一断电并且替换需要2天的时间。

考虑在机器或文件系统之间迁移文件时需要创建哪种程序。在系统上实时运行此功能的能力可为您节省很多麻烦。

您可能会考虑使用GUID作为物理文件名而不是增量数字,以防您的增量数字计数器(数据库标识列?)弄乱了。

如果合适,请考虑使用CDN,例如Amazon S3。


2

尽管我没有提供过如此大的图片,但我之前已经编写了一个小型画廊应用程序,可以在400MHz的机器上提供约2.5万张图片。512 MB的RAM左右。一些经验;

  • 不惜一切代价避免使用关系数据库;毫无疑问,虽然数据库在处理数据方面很聪明,但它们并不是为此类用途而设计的(我们为称为文件系统的对象提供了专门的,层次结构的键值数据库)。尽管我只是预感,但我敢打赌,如果您向数据库高速缓存扔了很大的Blob,数据库高速缓存就会消失。当我可用的硬件处于较小范围时,在图像查找上完全不接触数据库将速度提高了几个数量级。

  • 研究文件系统的行为;在ext3上(或者当时是ext2-不记得了),能够有效查找子目录和文件的限制大约是256个标记;因此在任何给定的文件夹中只有那么多文件和文件夹。再次,明显的加速。虽然我对NTFS并不了解,但是XFS之类的东西(据我所记得,它使用B树)非常快,这仅仅是因为它们可以非常快速地进行查找。

  • 均匀分布数据;当我尝试上述方法时,我尝试将数据均匀地分布在所有目录上(我对URL进行了MD5处理,并将其用于目录;/1a/2b/1a2b...f.jpg)。这样,达到任何性能限制都需要更长的时间(而且在如此大的数据集上文件系统缓存仍然无效)。(相反,您可能想查看限制的早期位置;然后将所有内容都放入第一个可用目录中。


2

可能在这个游戏上迟到了。但是一种解决方案(如果适合您的用例)可以是文件名哈希。这是一种使用文件名创建易于复制的文件路径,同时还创建分布良好的目录结构的方法。例如,您可以使用文件名的哈希码的字节作为路径:

String fileName = "cat.gif";
int hash = fileName.hashCode();
int mask = 255;
int firstDir = hash & mask;
int secondDir = (hash >> 8) & mask;

这将导致路径为:

/172/029/cat.gif

然后您可以找到 cat.gif,通过重现算法在目录结构中。

使用HEX作为目录名与转换int值一样容易:

String path = new StringBuilder(File.separator)
        .append(String.format("%02x", firstDir))
        .append(File.separator)
        .append(String.format("%02x", secondDir)
        .toString();

导致:

/AC/1D/cat.gif

几年前,我写了一篇有关此的文章,最近将其移至Medium。它具有更多详细信息和一些示例代码:文件名哈希:创建哈希目录结构。希望这可以帮助!


我们使用类似的方式存储了18亿个项目。它运作良好。使用快速且冲突率低的哈希值即可。
CVVS


1

如果不需要立即全部使用它们,并且您可以即时生成它们并且它们是小型映像,为什么不在映像生成器上方实现LRU内存或磁盘缓存?

这样可以将您从存储设备中节省下来,并保留要通过mem提供的热图像吗?


1

我只是在zfs上进行了测试,因为我喜欢zfs,并且我有一个500gig的分区,并对其进行了压缩。我编写了一个脚本,该脚本生成了50-100k个文件,并将它们放置在嵌套目录1/2/3/4/5/6/7/8(深度为5-8级)中,并让它运行了1周。(这不是一个很好的脚本。)它填满了磁盘,最终有大约2500万个文件。立即访问具有已知路径的任何文件。立即列出具有已知路径的任何目录。

但是,(通过查找)对文件列表进行计数需要68个小时。

我还进行了一项测试,将许多文件放在一个目录中。在停止之前,我在一个目录中存储了大约370万个文件。列出目录进行计数大约需要5分钟。删除该目录中的所有文件需要20个小时。但是查找和访问任何文件都是即时的。


1

我看到其他人提到了数据库,但是您的帖子中没有提及该数据库。无论如何,我对这一点的看法是:要么坚持使用数据库,要么坚持使用文件系统。如果必须将两者混合使用,请注意这一点。事情变得更加复杂。但是您可能必须这样做。在数据库中存储一百万张照片听起来并不是最好的主意。

您可能对以下规格感兴趣,大多数数码相机都遵循它来管理文件存储: https //en.wikipedia.org/wiki/Camera_Image_File_Format

本质上,将创建一个文件夹,例如000OLYMPUS和将照片添加到该文件夹​​(例如DSC0000.RAW)。当文件名计数器到达DSC9999.RAW一个新文件夹(001OLYMPUS)并再次添加图像时,重置计数器,可能使用不同的前缀(例如:P_0000.RAW

或者,您也可以根据文件名的一部分创建文件夹(已经多次提及)。例如,如果您的照片命名为IMG_A83743.JPG,则将其存储在IMG_\A8\3\IMG_A83743.JPG。实施起来比较复杂,但是会使文件更容易找到。

根据文件系统(这需要做一些研究),您也许可以将所有映像转储到单个文件夹中,但是以我的经验,这通常会导致性能问题。


0

您可能想看看ZFS(文件系统,Sun的卷管理器)


0

从大量生成路径的一种干净方法是轻松地将其转换为十六进制然后拆分!

例如1099496034834> 0xFFFF1212>FF/FF/12/12

public string GeneratePath(long val)
{  
    string hex = val.ToString("X");
    hex=hex.PadLeft(10, '0');
    string path="";
    for(int i=0; i<hex.Length; i+=2 )
    {
        path += hex.Substring(i,2);
        if(i+2<hex.Length)
            path+="/";
    }
    return path;
}

存储和加载:

public long Store(Stream doc)
{
   var newId = getNewId();
   var fullpath = GeneratePath(newId)
   // store into fullpath 
   return newId;
}

public Stream Load(long id)
{
   var fullpath = GeneratePath(newId)
   var stream = ... 
   return stream;
}

完整的源代码:https : //github.com/acrobit/AcroFS


-1

不幸的是,文件系统在管理许多小文件方面非常糟糕(每个目录或深目录树中有许多文件的性能,检查重新启动的时间,可靠性),因此,如果要使用文件系统,则上述涉及ZIP文件的解决方案是最佳的。

到目前为止,使用数据库管理器是最佳选择。一个简单的例子,例如BDB或GDBM;甚至像MySQL这样的相对DBMS也会更好。只有不懂文件系统和数据库的懒惰的人(例如那些拒绝事务的人)倾向于将文件系统用作数据库(或者反之亦然)。


-2

具有包含ID和BLOB来存储图像的表的数据库又如何呢?然后,只要您想将更多数据元素与照片相关联,就可以添加新表。

如果您希望扩展,为什么不立即扩展?您现在和以后的IMO都可以节省时间。一次实现数据库层,这很容易开始。或者用文件夹和文件名实现一些功能,等等,后来当您启动MAX_PATH时切换到其他功能。


5
到那里去做,有疤痕可以证明这一点。大量存储图像的数据库几乎是不可思议的,并且需要过多的维护。最好将它们存储在文件系统中,除非您有只能由数据库解决的特定需求(我们是版本跟踪)
。– Satanicpuppy

1
而且,有许多实用程序可用于处理文件和文件系统,很少甚至没有用于处理数据库中的文件。
Mark Ransom

2
哦,天哪。请不要将数据库用作大型BLOB存储。
尼尔N

真是的 不知道数据库(还是吗?)的BLOB有这么多问题。

这么多评论如此糟糕的解决方案又怎么能有+1?对OP没有冒犯(我看到它来自SO),但是按下按钮是有原因的!
马克·亨德森
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.