存储大量图像
Posted
技术标签:
【中文标题】存储大量图像【英文标题】:Storing a large number of images 【发布时间】:2009-01-15 10:57:43 【问题描述】:我正在考虑开发我自己的基于 php 的图库来存储大量图片,可能有数万张。
在数据库中,我将指向图像的 url,但问题是:我知道将它们全部放在服务器中的同一目录中是不切实际的,因为它会减慢爬网的访问速度,所以,您将如何存储所有这些?某种基于 jpeg/png 名称的树?
你会推荐我什么分割图像的规则?
(它将专注于在cheapo dot coms中使用,因此不可能对服务器进行修改)
【问题讨论】:
【参考方案1】:过去我们也遇到过类似的问题。并找到了一个不错的解决方案:
为每张图片提供一个独特的指南。 为每个图像创建一个数据库记录,其中包含子图像的名称、位置、guid 和可能的位置(缩略图、缩小尺寸等)。 使用 guid 的前(一个或两个)字符来确定***文件夹。 如果文件夹中的文件过多,请重新拆分。更新参考资料,您就可以开始了。 如果文件数量和访问量过多,您可以将文件夹分散到不同的文件服务器上。我们已经体验过,使用 guid,您可以获得或多或少统一的划分。它就像一个魅力。
可能有助于生成唯一 ID 的链接:
http://en.wikipedia.org/wiki/Universally_Unique_Identifier http://en.wikipedia.org/wiki/Sha1【讨论】:
由于性能的原因,数据库调用通常非常昂贵,尤其是对于像图像这样的二进制数据。 更不用说从数据库中提供图像意味着您几乎总是发送数据,就好像您可以从文件系统提供服务一样,您可以让浏览器/服务器处理图像的缓存 @Gamecat 恕我直言,比生成 UUID 更好的是简单地散列文件名并将其开头用作目录名。这样你就不需要数据库,因为你总是可以重新计算哈希,这比访问数据库要快得多。 (我看到你提到了 SHA-1,但没有明确建议)。 @maaartinus,你可能是对的。但是我们已经有一个数据库(用于 CMS),我们只需要链接图片就可以了,这对我们来说非常有用。 如果你有一个整数唯一ID,一个简单的方法就是把它分成三个级别:xxx/yyy/filename.jpg。这样您就可以使用唯一 ID。例如,如果 id 是 100789,它将被存储为 100/789/filename.jpg。然后,每个级别最多有 1,000 个目录。总共有 1,000,000 个文件。而且,您可以根据分辨率拥有多个文件名:thumbnail.jpg、small.jpg 等。【参考方案2】:几年前我从事电子文档管理系统的工作,我们几乎按照 Gamecat 和 wic 的建议做了。
也就是说,为每个图像分配一个唯一的 ID,并使用它来导出图像文件的相对路径。我们使用的MOD和wic建议的类似,但是我们每层允许1024个文件夹/文件,有3个层级,所以我们可以支持1G的文件。
但是,我们从文件中删除了扩展名。数据库记录包含 MIME 类型,因此不需要扩展。
我不建议将完整的 URL 存储在数据库记录中,只存储图像 ID。如果您存储 URL,您将无法在不转换数据库的情况下移动或重组您的存储。相对 URL 可以,因为这样至少可以移动图像存储库,但如果您只存储 ID 并派生 URL,您将获得更大的灵活性。
另外,我不建议允许从网络直接引用您的图像文件。而是提供服务器端程序(例如 Java Servlet)的 URL,并在 URL 查询 (http://url.com/GetImage?imageID=1234
) 中提供图像 ID。
servlet 可以使用该 ID 来查找 DB 记录、确定 MIME 类型、导出实际位置、检查安全限制、日志记录等。
【讨论】:
优点。 servlet 请求是否仍然允许缓存?我正在寻找一个类似的问题,但在我的应用程序中,传输时间很关键,所以我一直在寻找在客户端缓存图像的方法。我在做梦吗? @MikeJ:您可以创建一个单独的类来访问图像。该类将知道如何从 id 派生路径等。它还可以包含一个缓存,可能作为您自己管理的哈希表,或者可能是一个罐装缓存类。 Servlet 将从该对象而不是磁盘中获取图像。【参考方案3】:我通常只使用数字数据库 id (auto_increment),然后使用模数 (%) 运算符来确定文件的放置位置。简单且可扩展。例如,id 为 12345 的图像的路径可以这样创建:
12345 % 100 = 45
12345 % 1000 = 345
结束于:
/home/joe/images/345/45/12345.png
或者类似的东西。
如果您使用的是 Linux 和 ext3 以及文件系统,您必须注意目录中可以拥有的目录和文件的数量是有限制的。目录的限制是 32000,因此您应该始终努力保持目录数量较少。
【讨论】:
同时拥有 '345' 和 '45' 的目的是什么?似乎您的每个一级目录(如“345”)都只有一个子目录(在本例中为“45”)。【参考方案4】:我知道将它们全部放在服务器中的同一目录中是不切实际的,因为它会减慢爬网的访问速度。
这是一个假设。
我设计的系统将数百万个文件平放在一个目录中,效果很好。它也是最容易编程的系统。大多数服务器文件系统都毫无问题地支持这一点(尽管您必须检查您使用的是哪一个)。
http://www.databasesandlife.com/flat-directories/
【讨论】:
感谢分享。 OP提到了PHP,一个实际问题是FTP访问包含大量文件的目录可能会超时。 我认为重要的是,正如您在博客文章中所做的那样,一些 文件系统支持单个文件夹中的大量文件。根据我的经验,一些(其他)文件系统在其规定的大型文件规范之外工作,但并非所有文件操作都可以工作。如果您要在一个文件夹中存储大量文件,请先进行测试!也就是说,为什么不使用某种哈希值对文件夹结构进行树平衡?【参考方案5】:在保存与 auto_increment id 关联的文件时,我使用类似以下的内容,它创建三个目录级别,每个级别由 1000 个目录组成,每个第三级目录中有 100 个文件。这支持约 1000 亿个文件。
如果 $id = 99532455444 那么以下返回 /995/324/554/44
function getFileDirectory($id)
$level1 = ($id / 100000000) % 100000000;
$level2 = (($id - $level1 * 100000000) / 100000) % 100000;
$level3 = (($id - ($level1 * 100000000) - ($level2 * 100000)) / 100) % 1000;
$file = $id - (($level1 * 100000000) + ($level2 * 100000) + ($level3 * 100));
return '/' . sprintf("%03d", $level1)
. '/' . sprintf("%03d", $level2)
. '/' . sprintf("%03d", $level3)
. '/' . $file;
【讨论】:
【参考方案6】:查看 XFS 文件系统。它支持无限数量的文件,Linux 支持它。 http://oss.sgi.com/projects/xfs/papers/xfs_usenix/index.html
【讨论】:
【参考方案7】:您可以在表格中有一个 DateTime 列,然后将它们存储在以添加到表格中的图像的月、年甚至月、日、年命名的文件夹中。
例子
-
2009
-01
--01
--02
--03
--31
这样你最终的文件夹深度不会超过 3 个。
【讨论】:
【参考方案8】:我目前正面临这个问题,而 Isaac 所写的内容让我对这个想法产生了兴趣。我的功能有点不同。
function _getFilePath($id)
$id = sprintf("%06d", $id);
$level = array();
for($lvl = 3; $lvl >= 1; $lvl--)
$level[$lvl] = substr($id, (($lvl*2)-2), 2);
return implode('/', array_reverse($level)).'.jpg';
我的图片只有数千个,所以我最多只有 999999 个限制,所以它会将其拆分为 99/99/99.jpg 或 43524 拆分为 04/35/24.jpg
【讨论】:
【参考方案9】:使用文件系统的层次结构。使用 001/002/003/004.jpg 之类的东西来识别您的图像会非常有帮助。不过,分区是另一回事。可能是随机的、基于内容的、基于创建日期的等等。这实际上取决于您的应用程序是什么。
【讨论】:
【参考方案10】:您可以查看 Apple iPod 用于存储其多媒体内容的策略。有一层深度的文件夹和具有相同宽度标题的文件。我相信 Apple 人员在测试他们的解决方案上投入了大量时间,因此它可能会给您带来立竿见影的好处。
【讨论】:
我不太清楚你在这里的意思。能举个例子吗?【参考方案11】:如果您处理的图片是数码照片,您可以使用 EXIF 数据对它们进行排序,例如按拍摄日期。
【讨论】:
【参考方案12】:您可以将图像作为 blob 存储在数据库中(varbinary 用于 mssql)。这样您就不必担心存储或目录结构。唯一的缺点是您不能轻松浏览文件,但无论如何这在平衡的目录树中会很困难。
【讨论】:
IMO 这是一个糟糕的建议。 1.你的数据库很快就会变得庞大,它会带来其他问题。 2. 另一方面,无法使用缓存代理服务器(如 nginx 或 HAproxy)来缓存图像,这对于静态内容来说非常快。 3. DB将成为负载非常低的瓶颈。以上是关于存储大量图像的主要内容,如果未能解决你的问题,请参考以下文章