【问题标题】:Storing a large number of images存储大量图像
【发布时间】:2009-01-15 10:57:43
【问题描述】:

我正在考虑开发我自己的基于 PHP 的图库来存储大量图片,可能有数万张。

在数据库中,我将指向图像的 url,但问题是:我知道将它们全部放在服务器中的同一目录中是不切实际的,因为它会减慢爬网的访问速度,所以,您将如何存储所有这些?某种基于 jpeg/png 名称的树?

你会推荐我什么分割图像的规则?

(它将专注于在cheapo dot coms中使用,因此不可能对服务器进行修改)

【问题讨论】:

    标签: image tree filesystems


    【解决方案1】:

    过去我们也遇到过类似的问题。并找到了一个不错的解决方案:

    • 为每张图片提供一个独特的指南。
    • 为每个图像创建一个数据库记录,其中包含子图像的名称、位置、guid 和可能的位置(缩略图、缩小尺寸等)。
    • 使用 guid 的前(一个或两个)字符来确定顶级文件夹。
    • 如果文件夹中的文件过多,请重新拆分。更新参考资料,您就可以开始了。
    • 如果文件数量和访问量过多,您可以将文件夹分散到不同的文件服务器上。

    我们已经体验过,使用 guid,您可以获得或多或少统一的划分。它就像一个魅力。

    可能有助于生成唯一 ID 的链接:

    【讨论】:

    • 由于性能的原因,数据库调用通常非常昂贵,尤其是对于像图像这样的二进制数据。
    • 更不用说从数据库中提供图像意味着您几乎总是发送数据,就好像您可以从文件系统提供服务一样,您可以让浏览器/服务器处理图像的缓存
    • @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 列,然后将它们存储在以添加到表格中的图像的月、年甚至月、日、年命名的文件夹中。

        例子

        1. 2009
        2. -01
        3. --01
        4. --02
        5. --03
        6. --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将成为负载非常低的瓶颈。
                猜你喜欢
                • 2011-04-05
                • 1970-01-01
                • 1970-01-01
                • 2012-01-01
                • 2018-09-04
                • 1970-01-01
                • 1970-01-01
                • 2019-09-08
                相关资源
                最近更新 更多