我有一个项目,将生成大量的图像。 大约100万开始。 他们不是大的图像,所以我会把它们全部存储在一台机器上。
你如何build议有效地存储这些图像? (目前的NTFS文件系统)
我正在考虑一个命名scheme…开始所有的图像将有一个从1增量名称,我希望这将帮助我稍后sorting,如果需要,并把它们放在不同的文件夹中。
什么是更好的命名scheme:
a / b / c / 0 ... z / z / z / 999
要么
a / b / c / 000 ... z / z / z / 999
任何想法呢?
我build议使用常规文件系统而不是数据库。 使用文件系统比数据库更容易,你可以使用普通的工具来访问文件,文件系统是为这种使用而devise的。等等,NTFS应该可以很好地用作存储系统。
不要将实际path存储到数据库。 最好将图像的序号存储到数据库中,并具有可以从序号生成path的function。 例如:
File path = generatePathFromSequenceNumber(sequenceNumber);
如果你需要改变一下目录结构的话,处理起来会比较容易。 也许你需要将图像移动到不同的位置,也许你用尽了空间,你开始在磁盘A上存储一些图像,在磁盘B上一些图像。更改一个function比改变数据库中的path更容易。
我将使用这种algorithm来生成目录结构:
12345
– > 000000012345.jpg
000000012345
– > 000/000/012
123
文件的完整path和文件名是000/000/012/00000000012345.jpg
12345678901234
的文件,path为123/456/789/12345678901234.jpg
有关目录结构和文件存储需要考虑的一些事情:
我将把我的2美分的价值放在一个负面的build议:不要去与数据库。
我一直在使用图像存储数据库多年:大(1兆 – > 1演出)文件,经常更改,文件的多个版本,经常访问合理。 你遇到的大型文件存储的数据库问题是非常繁琐的处理,写作和交易问题是棘手的,你遇到的locking问题,可能会导致大火车残骸。 我有更多的练习编写dbcc脚本,并且从备份恢复表比任何正常的人应该有。
我所使用的大多数新系统都将文件存储推送到文件系统,并且只依靠数据库进行索引。 文件系统被devise为采取这种滥用,它们更容易扩展,并且如果一个条目被损坏,你很less会丢失整个文件系统。
理想情况下,您应该对各种结构的随机访问时间进行一些testing,因为您的特定硬盘驱动器设置,caching,可用内存等可以更改这些结果。
假设你可以控制文件名,我会把它们分成1000个目录。 您添加的目录级别越多,烧录的索引节点越多,因此这里有一个推拉式的。
例如,
/根/ [0-99] / [0-99] /文件名
请注意, http://technet.microsoft.com/en-us/library/cc781134(WS.10).aspx有关于NTFS安装程序的更多详细信息。 特别是,“如果在NTFS文件夹(300,000或更多)中使用大量文件,请禁用生成短文件名以获得更好的性能,尤其是如果长文件名的前六个字符相似。
您还应该考虑禁用不需要的文件系统function(例如上次访问时间)。 http://www.pctools.com/guides/registry/detail/50/
我想大多数处理这个问题的网站都使用某种散列来确保文件均匀分布在文件夹中。
所以说你有一个这样的文件的散列515d7eab9c29349e0cde90381ee8f810
你可以把它存储在下面的位置,你可以使用深层次的深度来保持每个文件夹中文件的数量低。
\51\5d\7e\ab\9c\29\349e0cde90381ee8f810.jpg
我已经看过这种方法很多次了。 您仍然需要一个数据库将这些文件散列映射到人类可读的名称以及您需要存储的其他元数据。 但是这种方法可以很好地扩展B / C,你可以开始在多台计算机和/或存储池之间分配哈希地址空间。
不pipe你做什么,都不要把它们全部存放在一个目录中。
根据这些图像名称的分布情况,您可以创build一个目录结构,其中包含单字母顶层文件夹,您将在其中拥有另一组子文件夹以用于第二个字母的图像等。
所以:
文件夹img\a\b\c\d\e\f\g\
将包含以“abcdefg”开头的图像等等。
你可以引入你自己的适当的深度。
这个解决scheme的好处是,目录结构有效地像一个哈希表/字典。 给定一个图像文件的名字,你会知道它的目录,并给出一个目录,你会知道一个图像的子集去那里。
我会将这些文件存储在文件系统上,但这取决于文件数量增长的速度。 这些文件是否在networking上托pipe? 有多less用户可以访问这些文件? 在我能给你一个更好的build议之前,这些都是需要回答的问题。 我也会看看来自Facebook的Haystack,他们有一个非常好的解决scheme来存储和提供图像。
另外,如果您select文件系统,则需要将这些文件与目录进行分区。 我一直在看这个问题,并提出了一个解决scheme,但它不是一个完美的任何手段。 我正在通过散列表和用户分区,你可以在我的博客上阅读更多。
我们有一个拥有400万图像的照片存储系统。 我们仅将数据库用于元数据,并且所有图像都使用反转命名系统存储在文件系统上,其中文件夹名称是从文件的最后一位生成的,last-1,依此类推。 例如:000001234.jpg存储在4 \ 3 \ 2 \ 1 \ 000001234.jpg目录结构中。
这个scheme在数据库中的身份索引工作得很好,因为它填充了均匀的整个目录结构。
快点,你不需要在你的数据库中存储文件path。 如果您的文件按照您描述的方式命名,则可以只存储一个数值。 然后,使用已经讨论过的定义良好的存储scheme之一,可以将索引作为数字,并通过遍历目录结构非常快速地find文件。
你的图像是否需要唯一命名? 生成这些图像的过程是否可以多次生成相同的文件名? 很难说,不知道什么设备正在创build文件名,但说设备是“重置”,并重新启动时,它开始命名的图像,因为它做了上次的“重置” – 如果这是一个担心。
另外,你说你会在一个月的时间里打100万张图片。 那怎么样? 这些图像能够以多快的速度继续填充文件系统? 它们会在某个时候达到顶峰吗?它会以大约100万个TOTAL图像的水平出现,还是会继续逐月增长?
我问,因为你可以开始按月devise你的文件系统,然后通过图像。 我可能倾向于build议您将图像存储在这样的目录结构中:
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
每月,每年,甚至每天都适合安全types的图像。 不知道这是你在做什么,但我做了一个家庭安全摄像头,每隔10秒拍摄一张照片…这样,您的应用程序可以深入到特定的时间,甚至一个范围,你可能会认为图像生成。 或者,而不是年,月 – 是否有其他“意义”,可以从图像文件本身导出? 一些其他的描述符,而不是我给的date的例子?
我不会将二进制数据存储在数据库中。 从来没有好的performance/运气与这样的事情。 不能想象,它与100万图像运行良好。 我会存储文件名,就是这样。 如果他们都将是JPG然后不存储扩展名。 我将创build一个控制表,存储一个指向文件的服务器,驱动器,path等的指针。这样,您可以将这些图像移动到另一个框,并仍然find它们。 你需要关键词标记你的图片? 如果是这样,那么你会想build立适当的表,允许这种标签。
当我回复时,你/他人可能已经解决了这些想法。希望这有助于..
新的MS SQL 2008有一个新function来处理这种情况,它被称为FILESTREAM。 看一看:
Microsoft TechNet FILESTREAM概述
我参与了一个项目,每年存储840万张图像,用于logging各种设备的状态。 更新的图像更频繁地访问,除非发现促使某人挖掘档案的条件,否则较旧的图像很less被find。
基于这种用法,我的解决scheme是逐步将图像压缩成压缩文件。 图像是JPG格式,每个大约20kB,并且不会压缩太多,所以ZIP压缩scheme是没有的。 这仅仅是为了将它们连接成一个文件系统入口,在将它们从驱动器移动到驱动器或者查看文件列表时,这在速度方面极大地有助于NTFS。
比一天早的图像被合并为“每日”邮政编码; 大于一个月的拉链被合并到它们各自的“每月”拉链中; 最后任何一年都不再需要,因此被删除。
此系统运行良好,因为用户可以浏览文件(通过操作系统或许多客户端应用程序),并且所有内容都根据设备名称和时间戳进行命名。 通常,用户知道这两条信息,并且可以快速定位数百万图像中的任何一条。
我知道这可能与你的具体细节无关,但我想我会分享。
也许是基于创builddate的命名scheme – 包括文件名中的所有信息,或者(稍后浏览更好)将其分割成目录。 我可以考虑以下内容,具体取决于您生成图像的频率:
Year/Month/Day/Hour_Minute_Second.png
Year/Month/Day_Hour_Minute_Second.png
等你得到我的观点… =)
我会倾向于创build一个基于date的文件夹结构,例如\ year \ month \ day,并使用文件名的时间戳。 如果需要,时间戳可以有一个额外的计数器组件,如果图像要创build得如此之快以至于在一毫秒内可能会有一个以上的时间戳。 通过对命名分类使用最重要的到最不重要的顺序,查找和维护是一件轻而易举的事情。 例如hhmmssmm [seq] .jpg
你在考虑灾难恢复吗?
这里提出的一些解决scheme最终会损坏文件名(例如,如果物理文件被移动,则会失去跟踪文件名的情况)。 我build议维护一个独特的物理文件名,这样如果你的文件位置主列表被损坏,你可以重新生成一个小的shell,呃,PowerShell,脚本;)
从我读的这里听起来,所有这些文件都将存储在一个文件系统上。 考虑将它们存储在多台机器上的多个文件系统上。 如果您有资源,请确定一个系统,将每个文件存储在两台不同的机器上,以防止电源丢失,更换时间为2天。
考虑在机器或文件系统之间迁移文件需要创build什么样的过程。 用您的系统实现这一function的能力是在线和在线的,可以为您节省相当的头痛。
您可能会考虑使用GUID作为物理文件名而不是增量编号,以防您的增量编号计数器(数据库标识列?)混乱。
如果合适,请考虑使用CDN,如Amazon S3。
虽然我没有提供这样的规模的照片,我以前写了一个小型的画廊应用程序,在400MHz的机器瓦特〜25K图片服务。 512 MB的RAM左右。 一些经验;
不惜一切代价避免关系数据库; 而毫无疑问,数据库在处理数据方面很聪明,它们并不是为这种使用而devise的(我们为这个被称为文件系统的领域提供了专门的,分层的键值数据库)。 虽然我只有一个预感,但我敢打赌,如果你把大块的数据扔到这个窗口,那么数据库caching就会出来。 虽然我可用的硬件在小端,但在图像查找上根本不触及数据库,速度提高了几个数量级。
研究文件系统的行为; 在ext3上(或者当时是ext2 – 不记得了),能够有效查找子目录和文件的限制在256分左右; 所以在任何给定的文件夹中只有许多文件和文件夹。 再次,明显的加速。 虽然我不知道NTFS,但像XFS(使用B树,据我所知)的速度非常快,只是因为它们可以非常快速地进行查找。
平均分配数据; 当我尝试了上述,我试图平均分配数据在所有的目录(我做了一个MD5的URL和用于目录; /1a/2b/1a2b...f.jpg
)。 这种方式需要更长的时间才能达到性能上的限制(无论如何,文件系统caching在这样大的数据集上都是无效的)。 (相反,你可能想知道限制在哪里,然后你想把所有的东西放在第一个可用的目录中。
如果你在windows上如何在exFat filessytem上
http://msdn.microsoft.com/en-us/library/aa914353.aspx
它的devise思想是存储媒体文件,现在就可以使用。
如果他们不是立即需要的,你可以即时生成这些小图像,为什么不在图像生成器上面实现一个LRU内存或磁盘caching呢?
这可以节省您的存储空间,并保持热图像从mem提供?
我只是在zfs上运行一个testing,因为我喜欢zfs,而且我有一个500gig分区,我已经压缩了。 我写了一个脚本,生成50-100k文件,并将它们放置在嵌套目录1/2/3/4/5/6/7/8(5-8层深),让它运行,我想1周。 (这不是一个很好的脚本。)它填满了磁盘,最终有约2500万文件左右。 访问具有已知path的任何一个文件是即时的。 列出具有已知path的任何目录是即时的。
但是,通过查找获得文件列表的数量花了68个小时。
我也跑了一个testing把很多文件放在一个目录中。 在我停下来之前,我在一个目录中find了大约370万个文件。 列出目录得到一个计数需要大约5分钟。 删除该目录中的所有文件需要20个小时。 但是查找和访问任何文件是即时的。
可能会迟到这个游戏。 但是一个解决scheme(如果它符合你的用例)可能是文件名散列。 这是一种使用文件名创build容易重现的文件path,同时创build分布良好的目录结构的方法。 例如,您可以使用文件名的哈希码的字节作为path:
String fileName = "cat.gif"; int hash = fileName.hashCode(); int mask = 255; int firstDir = hash & mask; int secondDir = (hash >> 8) & mask;
这将导致path是:
/172/029/cat.gif
然后你可以通过再现algorithmfind目录结构中的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。 它有一些更多的细节和一些示例代码: 文件名哈希:创build哈希目录结构 。 希望这可以帮助!
你可能想看看ZFS(文件系统,从Sun的卷pipe理器)问候
我看到其他提到一个数据库,但在你的文章中没有提到这一点。 无论如何,我对这一点的看法是:坚持数据库或文件系统。 如果你不得不混合这两个,小心它。 事情变得更加复杂。 但是你可能不得不 在数据库中存储一百万张照片听起来不是最好的主意。
您可能会对以下规格感兴趣,大多数数码相机会按照它来pipe理文件存储: https : //en.wikipedia.org/wiki/Camera_Image_File_Format
基本上,创build一个文件夹,如000OLYMPUS
和照片被添加到该文件夹(例如DSC0000.RAW
)。 当文件名计数器达到DSC9999.RAW
会创build一个新的文件夹( 001OLYMPUS
)并再次添加图像,重置计数器,可能使用不同的前缀(例如: P_0000.RAW
)。
或者,您也可以根据文件名称的某些部分(已经提到好几次)创build文件夹。 例如,如果您的照片名称为IMG_A83743.JPG
,请将其存储在IMG_\A8\3\IMG_A83743.JPG
。 实现起来比较复杂,但会使你的文件更容易find。
根据文件系统(这将需要一些研究),您可能只能将所有图像转储到一个文件夹中,但根据我的经验,这通常会导致性能问题。
不幸的是,在pipe理大量小文件时,文件系统是非常糟糕的(每个目录有很多文件或者深层的目录树,检查重启的时间,可靠性),所以上面涉及ZIP文件的解决scheme最好是使用文件系统。
使用数据库pipe理器是迄今为止最好的select; 简单的例如BDB或GDBM; 即使像MySQL这样的关系型DBMS也会更好。 只有懒惰的人不理解文件系统和数据库(例如那些解雇交易的人)倾向于使用文件系统作为数据库(或者更less一些,反之亦然)。
如何使用包含ID和BLOB的表来存储图像的数据库? 然后,您可以添加新的表(S),只要你想要更多的数据元素与照片关联。
如果您希望进行扩展,为什么不现在扩展? 现在和以后的IMO都可以节省时间。 实现一次数据库层,这是相当容易开始。 或者用文件夹和文件名来实现一些东西,等等,等等,当你开始烧掉MAX_PATH的时候切换到别的东西。