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

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

我正在考虑一种命名方案...首先,所有图像的名称都将从1开始递增名称
希望以后可以对它们进行排序


更好的命名方案是:

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


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


对此有任何想法吗?

评论

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

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

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

这是你吗? i46.tinypic.com/1z55k7q.jpg

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

#1 楼

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

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

 File path = generatePathFromSequenceNumber(sequenceNumber);


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

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


首先填充具有前导零的数字序列,直到您拥有至少12位数字的字符串。这是您文件的名称。您可能需要添加后缀:



12345-> 000000012345.jpg




然后将字符串拆分为2或3个字符块,其中每个块表示目录级别。具有固定数量的目录级别(例如3):



000000012345-> 000/000/012



商店文件到生成的目录下:


因此序列号为123的文件的完整路径和文件名为000/000/012/00000000012345.jpg

序列号为12345678901234的文件路径将是123/456/789/12345678901234.jpg





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


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


评论


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

– s.mihai
09年12月17日在18:17

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

–杰夫·弗里茨(Geoff Fritz)
09年12月17日在19:41

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

– 3dinfluence
09年12月17日在20:18

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

–迈克尔·狄龙(Michael Dillon)
2010年8月3日在15:32

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

–ropo
16年1月13日在8:45

#2 楼

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

我已经使用图像存储数据库多年了:大(1兆) -> 1 gig)文件(经常更改),该文件的多个版本通常会被合理访问。在存储大文件时遇到的数据库问题非常繁琐,编写和事务问题十分棘手,并且您遇到了锁定问题,这些问题可能导致严重的破坏。我在编写dbcc脚本和从备份还原表方面比任何普通人都拥有更多的实践。

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

评论


是。注意!

– s.mihai
09年12月17日在17:13

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

–NotMe
09年12月17日在17:25

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

–user28770
09年12月17日在17:33

如果您每个数据库仅存储数百个文档或图片-使用数据库进行存储有何不利之处?

–哔哔声
09年12月18日在4:38

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

– akira
2010-6-1 14:44



#3 楼

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

因此,假设您的文件哈希值类似于这个515d7eab9c29349e0cde90381ee8f810
您可以将其存储在以下位置,并且可以使用需要多少层的深度来将每个文件夹中的文件数量保持在较低水平。 de\abc9e0cde90381ee8f810.jpg

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

评论


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

– aexl
16-10-12在21:08

#4 楼

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

假设您可以控制文件名,我将它们按每个目录1000s的级别进行分区。添加的目录级别越多,刻录的inode越多,因此这里有一个推挽式操作。

例如,

/ root / [0-99] / [0 -99] / filename

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

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

评论


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

– rob
09年12月17日在22:23

链接...

–起搏器
17年11月20日在22:27

#5 楼

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

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

因此:

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

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

该解决方案的优点在于,目录结构实际上像哈希表/字典一样工作。给定图像文件名,您将知道它的目录,给定目录,您将知道去那里的图像子集。

评论


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

– s.mihai
09年12月17日17:00

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

– Wim Hollebrandse
09年12月17日在17:04

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

– Mark Ransom
09年12月17日在17:10

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

– JuhaSyrjälä
09年12月17日在17:25

我必须支持一个目录中有4百万个文件的应用程序;它工作得非常好,但是您永远也找不到浏览器打开该文件夹,它会不断地对新添加的内容进行排序。 +1使NTFS能够不死地处理它。

–SqlACID
2010-2-23在0:22

#6 楼

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

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

#7 楼

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

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

评论


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

– s.mihai
09年12月17日在17:02

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

– s.mihai
09年12月17日在17:03

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

–卢卡斯
09年12月17日在17:06

#8 楼

新的MS SQL 2008具有处理此类情况的新功能,称为FILESTREAM。看看:

Microsoft TechNet FILESTREAM概述

#9 楼

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

评论


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

– s.mihai
09年12月17日在17:22

#10 楼

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

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

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

imgs\yyyy\mm\filename.ext

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

example:  D:\imgs09\aaa0001.jpg
          D:\imgs09\aaa0002.jpg
          D:\imgs09\aaa0003.jpg
          D:\imgs09\aaa0004.jpg
                   |
          D:\imgs09\zzz9982.jpg
          D:\imgs10\aaa0001.jpg (this is why I ask about uniqueness)
          D:\imgs10\aab0001.jpg


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

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

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

评论


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

– s.mihai
09年12月17日在18:29

#11 楼

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

#12 楼

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

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

比将一天合并为一个“每日”拉链;超过一个月的拉链合并为各自的“每月”拉链;最终不再需要一年中的任何内容,因此将其删除。

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

我知道这可能与您的特定细节无关,但我想我会分享。
/>

#13 楼

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


每天生成几张图像:Year/Month/Day/Hour_Minute_Second.png

一个月两个:Year/Month/Day_Hour_Minute_Second.png


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

评论


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

– s.mihai
09年12月17日在17:09

好吧,显然您不必仅因为遵循此方案而创建每个文件夹。您甚至可以设置年/月/日/小时/分钟-根据速率最高时生成图像的频率来决定所需的文件夹级别-然后不要创建将留空的文件夹。

– Tomas Aschan
09年12月17日在17:49

#14 楼

您是否正在考虑灾难恢复?

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

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

考虑需要创建哪种程序在机器或文件系统之间迁移文件。通过系统实时在线执行此操作的功能可能会为您节省很多麻烦。

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

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

#15 楼

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


不惜一切代价避免建立关系数据库;毫无疑问,尽管数据库在处理数据方面很聪明,但它们并不是为这种用途而设计的(我们为所谓的文件系统提供了专门的层次结构键值数据库)。虽然我只是预感,但我敢打赌,如果您向数据库高速缓存扔了很大的Blob,数据库高速缓存就会消失。虽然我的可用硬件处于较小范围,但在图像查找上完全不接触数据库的速度提高了几个数量级。
研究文件系统的行为;在ext3上(或者当时是ext2-不记得了),能够有效查找子目录和文件的限制大约是256个标记;因此在任何给定的文件夹中只有那么多文件和文件夹。再次,明显的加速。虽然我不了解NTFS,但是XFS之类的东西(据我所记得,它使用B树)非常快,这仅仅是因为它们可以非常快速地进行查找。
均匀地分配数据;当我尝试上述方法时,我尝试将数据平均分配到所有目录中(我对URL进行了MD5处理,并将其用于目录; /1a/2b/1a2b...f.jpg)。这样,达到任何性能限制都需要更长的时间(而且在如此大的数据集上文件系统缓存仍然无效)。 (相反,您可能想查看限制的早期位置;然后将所有内容都放入第一个可用目录中。


#16 楼

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

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
18年4月2日在15:41

#17 楼

如果您在Windows上,则在exFat文件系统上如何操作

http://msdn.microsoft.com/zh-cn/library/aa914353.aspx

记住存储媒体文件,并且现在可用。

#18 楼

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

这样可以节省时间您可以从存储中保存并保留要从mem提供的热图像吗?

#19 楼

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

但是,通过查找找到文件列表的时间却花了68个小时。

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

#20 楼

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

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

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

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

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

#21 楼

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

#22 楼

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

例如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

#23 楼

不幸的是,文件系统在管理许多小文件方面非常糟糕(每个目录或深目录树中有许多文件的性能,检查重新启动的时间,可靠性),因此,如果要使用文件系统,则上述涉及ZIP文件的解决方案是最佳的。到目前为止,使用数据库管理器是最好的选择。一个简单的例子,例如BDB或GDBM;甚至像MySQL这样的相对DBMS也会更好。只有不懂文件系统和数据库的懒惰的人(例如那些拒绝事务的人)才倾向于将文件系统用作数据库(或者反之亦然)。

#24 楼

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

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

评论


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

–撒旦小狗
09年12月17日在17:05

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

– Mark Ransom
09年12月17日在17:14

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

–尼尔N
09年12月17日在17:26

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

– jdmichal
09年12月17日在18:56

这么多评论如此糟糕的解决方案又怎么能有+1?对OP没有冒犯(我看到它来自SO),但是在这里按下按钮是有原因的!

–马克·亨德森(Mark Henderson)
10年7月20日在23:08