将大文件(10 MB)存储在数据库中是不好的做法吗?


188

我当前正在创建一个Web应用程序,允许用户存储和共享1 MB-10 MB的文件。

在我看来,将文件存储在数据库中将大大降低数据库访问速度。

这是一个有效的问题吗?将文件存储在文件系统中并将文件名和路径保存在数据库中是否更好?使用数据库时,是否有与存储文件有关的最佳实践?

我正在该项目的PHP和MySQL中工作,但是对于大多数环境(Ruby on RailsPHP.NET)和数据库(MySQL,PostgreSQL)都是相同的问题。


9
有关DBA.SE的相关问题:文件-是否在数据库中?
Nick Chammas

11
令人惊讶的是,没有人发布过针对此问题的MS研究(针对SQL Server 2008):对BLOB或不对BLOB:数据库或文件系统中的大对象存储
2012年

2
是相对数量,10MB在现代系统中,我(可能还有许多其他人)并不认为大。

27
根据FAQ,这是主题-它适合项目符号“设计模式”(斜线反模式)和“软件体系结构”。为什么关闭了?
2012年

21
我认为现在的问题没有任何模糊性。我不知道为什么关闭它。
reinierpost,2012年

Answers:


139

支持将文件存储在数据库中的原因:

  1. ACID一致性包括更新的回滚,当文件存储在数据库外部时,更新会很复杂。这不能被轻易掩盖。使文件和数据库同步并能够参与事务非常有用。
  2. 文件与数据库一起使用,不能从数据库中孤立出来。
  3. 备份自动包括文件二进制文件。

禁止在数据库中存储文件的原因:

  1. 二进制文件的大小在数据库之间有所不同。在SQL Server上,例如,当不使用FILESTREAM对象时,它为2 GB。如果用户需要存储更大的文件(例如电影),则必须跳个圈,以实现神奇的效果。
  2. 增加数据库的大小。您应该铭记的一个一般概念:维护数据库所需的知识水平与数据库的大小成正比。即,大型数据库的维护比小型数据库更为复杂。将文件存储在数据库中可以使数据库更大。即使说每天完整备份已足够,并且数据库较大,您可能也无法再这样做。您可能需要考虑将文件放在不同的文件组中(如果数据库支持),请调整备份以将数据备份与文件备份等分开。所有这些都是不可能学习的,但是可以增加维护的复杂性,这意味着业务成本。较大的数据库还会尝试将尽可能多的数据填充到内存中,因此会占用更多内存。
  3. 如果您使用系统特定的功能(例如SQL Server的FILESTREAM对象)并且需要迁移到其他数据库系统,则可移植性可能是一个问题。
  4. 将文件写入数据库的代码可能是个问题。我在几天前没有咨询过的一家公司将Microsoft Access前端连接到他们的数据库服务器,并使用Access的功能使用其Ole Object控件上载“任何内容”。后来,他们更改为使用仍然依赖Ole的其他控件。后来,有人更改了接口以存储原始二进制文件。提取那些Ole对象是一个新的境界。当您将文件存储在文件系统上时,没有涉及包装/调整/更改源文件的附加层。
  5. 将文件提供给网站更加复杂。为了使用二进制列来执行此操作,必须编写一个处理程序以从数据库中流式处理二进制文件。如果您存储文件路径,但你没有,你也可以做到这一点,即使这样做。同样,添加处理程序不是不可能的,但会增加复杂性,这是另一个失败点。
  6. 您无法利用云存储。假设您有一天想将文件存储在Amazon S3存储桶中。如果存储在数据库中的是文件路径,则可以在S3中将其更改为路径。据我所知,任何DBMS在任何情况下都是不可能的。

IMO认为文件在数据库中的存储是否为“不良”,需要有关情况和要求的更多信息。文件的大小和/或数量是否总是很小?是否没有使用云存储的计划?这些文件是否可以在网站或Windows应用程序之类的二进制可执行文件中提供?

总的来说,我的经验发现,即使考虑到缺少ACID和存在孤儿的可能性,存储路径对于企业来说也较便宜。但是,这并不意味着互联网上不会出现因缺乏ACID控制而导致文件存储出错的故事,而是意味着总体上该解决方案更易于构建,理解和维护。


为什么不能使用CDN?我听说过的几乎所有CDN都是受支持的方案。
Billy ONeal,2012年

@BillyONeal-您不能使用CDN 并将文件存储在数据库中。除非您对复制感到满意,否则您将无法同时拥有两者。
托马斯

3
嗯,CDN的重点是重复。CDN只是缓存网址的目标-唯一的要求是要有一个HTTP主机来提供内容,并且内容很少更改。(CDN到底应该如何告诉您从何处拉出了图像?)
Billy ONeal 2012年

3
@BillyONeal-但是,我认为这对我来说是不好的选择,我已经调整了答案。具体来说,如果您想使用云存储(然后将CDN与您的云存储一起使用),则无法使用数据库存储解决方案本机进行。您必须编写一个同步例程才能从数据库中提取文件,然后将其发送到您的云存储提供商。
汤玛斯(Thomas)

@BillyONeal-在某种程度上,您的评论是最好的答案。您可以拥有数据库存储的所有好处,但是没有任何问题。
B

89

在许多情况下,这是一个坏主意。它将使数据库文件膨胀,并导致多个性能问题。如果将斑点粘贴到具有大量列的表中,则情况更糟。

然而!某些数据库(例如SQL Server)具有FILESTREAM列类型。在这种情况下,您的数据实际上存储在数据库服务器上的单独文件中,并且表中仅保存该文件的ID。在这种情况下,我没有太多理由不将数据保留在SQL Server中。这些文件将自动包含在服务器备份中,并且数据库和文件永远不会不同步。Tony建议存储文件名的问题在于数据库和文件系统可能不同步。数据库在磁盘上删除文件后将声明文件存在。如果某个进程正在修改数据库,然后崩溃,则文件和数据库将不匹配(即,ACID与数据库外部的文件不匹配)。


21
我不同意以下声明:如果进程正在修改数据库,然后崩溃,则文件和数据库将不匹配。如果将整个过程包装在事务中(创建文件,验证文件,更新db)并抛出错误消息当出现问题时,使它们保持同步非常容易。
Briddums 2012年

3
我对此表示赞同:考虑场景:将文件存储到文件系统(不删除旧文件),更新数据库,成功删除旧文件,回滚删除新文件。最坏的情况-如果进程被中断,则说明您有孤儿文件。但是,您始终拥有正确版本的DB引用的文件。
vartec

2
File / DB方法的其他潜在问题:1)您必须以写时复制方式进行更新。如果您的进程在更新期间崩溃,则数据库状态将回滚,而文件不会。2)然后,这需要对旧文件进行某种垃圾回收。3)将所有内容存储在数据库中意味着备份后数据库和文件的版本是同步的。将您的数据库恢复到2周前的状态...现在那个时候文件的内容在哪里?
蒂莫西·鲍德里奇

3
@briddums-不,因为SQL Server直接集成到文件系统中并代表OS管理这些文件。我还没有亲自使用过它们,但是文档使它看起来像FILESTREAM及其后代FileTable可以为您提供两全其美的功能:文件紧密地绑定到数据库并关联数据(允许您集中管理数据)而不会膨胀数据库。数据库。
Nick Chammas

1
我同意尼克。我们已经用FILESTREAM列替换了Disk + DB系统,并且再也没有回头。能够通过FK将文件绑定到其他表真是太好了。因此,您实际上可以说“每个人必须拥有一个或多个与之关联的HR文档”,或类似的东西。
蒂莫西·巴尔德里奇

35

是的,这是一个坏习惯。

性能对数据库的影响:

  • 如果SELECT使用任何BLOB列进行操作,则始终将进行磁盘访问,而如果没有BLOB,则有机会直接从RAM中获取数据(将优化高吞吐量DB以适合RAM中的表);
  • 复制将很慢,复制延迟会很高,因为它必须将BLOB推送到从属服务器。高复制延迟将导致各种竞争状况和其他同步问题,除非您明确考虑到这一点。
  • 数据库备份/还原将花费更长的时间;

速度优势- !尽管某些较旧的文件系统无法处理包含数百万个文件的目录,但大多数现代文件系统都没有问题,实际上使用的数据结构与BD(通常为B树)相同。例如ext4(默认Linux文件系统)使用Htree

结论:这将影响数据库性能,并且不会提高文件检索性能。

此外,由于你在谈论的Web应用程序-利用现代网络服务器,它可以做到直接从文件系统提供静态文件sendfile()系统调用巨大的性能提升。如果要从数据库获取文件,这当然是不可能的。以这个基准测试为例,该测试显示Ngnix在低端笔记本电脑上以1000个并发连接进行25K req / s。这种负载将炸毁任何类型的数据库。


6
+1。让您的Web服务器发挥最大作用,从磁盘提供文件。不要让问PHP,因为PHP将不得不问的MySQL等
deizel

3
程序员什么时候才知道性能并不重要?
reinierpost,2012年

2
@reinierpost:大声笑。大概是当我们获得文科专业的时候;-)
vartec

1
@BillyONeal:为什么要假设必须为静态和动态内容使用同一台服务器?至于跨服务器同步文件,有专门为此目的设计的工具,其效率比数据库高得多。将数据库用作文件服务器就像用螺丝刀敲钉子一样。
vartec

1
@BillyONeal:我同意有一些可行的“解决方案”,我已经看到很多业余PHP设置以及MySQL中的图像。但是,在这种设置中,DB将永远不支持为BLOB提供高流量。
vartec

18

我会很务实,并遵循“不要优化”的原则。提供当前有意义的解决方案,并为您提供适当实施的开发资源。有很多潜在的问题。但是这些并不一定会成为真正的问题。例如,如果您有100个用户,则可能不会有问题。如果您有100,000或10,000,000用户,则可能是一个问题。但是,在后一种情况下,应该有更多的发展资源来处理所有问题的基础。

但是将数据存储在数据库中确实可以使您免于处理其他问题,例如,文件应存储在何处,应如何备份等。由于您正在编写Web应用程序,因此出于安全原因,这将是一个很好的主意为了确保承载应用程序的进程没有对该文件系统的写访问权,因此您需要配置服务器,以便该进程对存储数据的文件夹具有读/写访问权。

我个人选择将数据存储在数据库中,但要确保直到真正需要它们时才读取BLOBS,即在包含博客的那些表上不执行“ SELECT * FROM ...”。而且,如果确实遇到性能问题,我将确保该设计可以轻松地将数据移出数据库,移入文件系统。例如,将文件信息存储在单独的“ 文件”表中,从而使文件信息远离其他业务实体。

假设您有一个File类来表示在数据库中读取的文件,那么以后将其移出对编码的影响将很小。


这是一个极好的建议。不要开始解决您没有的问题。
HeavyE

16

微软几年前发布了一份有关此的白皮书。它专注于SqlServer,但是您可能会在其中找到一些有趣的信息:

去BLOB还是不去BLOB?数据库或文件系统中的大对象存储?

他们结论的一个非常简洁的版本是:

在比较NTFS文件系统和SQL Server 2005时,SQL Server可以更有效地处理小于256KB的BLOBS,而大于1MB的BLOBS可以更有效地处理NTFS。

我建议您针对特定用例编写一些小型测试。请记住,您必须提防缓存效果。(我第一次惊讶于磁盘保存速度似乎比物理上更高的吞吐量!)


4
您应该知道,当在单个目录中放置约100K个文件时,NTFS的行为就变得异常。文件访问速度降低了很多(至少一个数量级),并且文件打开操作随机(显然)开始失败。我在Windows 2008和Windows 7系统上遇到了这种影响。当我在多个目录中重新分配文件时,一切恢复正常。从那以后,我不知道情况是否有所改善。
Ferruccio

11

将文件存储在数据库外部的古老传统智慧可能不再成立。原则上,我倾向于完整性而不是速度,而对于现代DBMS,您可以同时拥有两者。

汤姆·凯特(Tom Kyte)似乎同意

我知道将要长时间保存在数据库外部的数据没有任何优势。

如果它在数据库中,我可以

确保它是专业管理的

支持

可恢复的(与其余数据一起)

固定的

可扩展的(尝试将100,000个文档放在一个目录中,现在,将它们放在表中-“缩放”一个-不是目录)

我可以轻松取消删除(闪回)

我有锁

我读过一致性...


8

是。

如果从文件系统提供文件,则Web服务器可以使用BSD或Linux上的内核代码(例如sendfile())将文件直接复制到套接字。这是非常快速和高效的。

从数据库中提供文件服务意味着您必须将数据从数据库服务器的磁盘复制到数据库服务器内存,然后从数据库服务器的内存复制到数据库服务器的网络端口,然后从网络复制到Web服务器进程,然后再复制到数据库服务器。传出网络连接。

除非确实有很好的理由,否则最好从文件系统提供静态文件。


的确如此,但是我看不到用户在问题中指出他将在数据库中提供静态文件。很好的可能是动态文件或用户上传的文件,如果这些文件存储在与数据库分开的文件系统上,则现在必须同步并具有单独的备份/还原过程。
maple_shaft

1
我的理解是,问题在于服务于用户上传的文件。“我目前正在创建一个允许用户存储和共享文件的网络应用程序,在我看来,将文件存储在数据库中”。我认为在数据库中进行包含许多兆字节Blob的DB转储确实不那么方便。另外:是的,很难处理文件。同步,存档都比较困难。然而,这不是多大比较困难,而牺牲在线性能,以节省几行你每晚的备份脚本是一个很大的错误。
埃文·P。

5

著名的汤姆·凯特(Tom Kyte)写道,他们(甲骨文公司)正在使用甲骨文数据库作为文件服务器,并且它的运行状况非常好,甚至比普通文件系统还要快,并且具有完全的事务性,没有性能损失并且具有单个备份。

是的,但是请注意,它们是Oracle DB的生产者,对于其他任何用户,都存在成本问题。使用商业数据库(例如Oracle)来存储文件根本没有成本效益。

但是,例如使用PostgreSQL,您可以仅运行另一个数据库实例仅用于blob存储。然后,您将获得全面的交易支持。但是事务性消耗数据库空间。数据库需要为多个并发事务存储多个blob实例。在PostgreSQL上,这是最痛苦的,因为此数据库存储为事务而创建的blob的副本,即使不再需要它们也要存储,直到VACUUM处理完成为止。

另一方面,对于文件系统存储,当有人修改文件时,您必须非常小心,因为可以回滚事务,并且必须保留文件副本,直到不再显示旧版本为止。

在仅添加和删除文件且对文件的事务访问不成问题的系统中,文件系统存储将是恕我直言的最佳选择。


嗨,当您说“使用... Oracle来存储文件根本没有成本效益”时,如果我们已经在使用Oracle来存储其他非文件数据呢?这仍然会降低成本吗?
小鹏-ZenUML.com

RE:“当有人修改文件时,您必须非常小心”……作为前Oracle DBA,我必须建议将大文件保留在数据库之外,并且您绝不允许修改文件。人们会犯错误。管理这些文件的回滚(撤消)的唯一实用方法是为它们实现写时复制系统。因此,所有版本均得到维护和存档。最古老的可移动关闭到远程存储,后处理,以小的变化合并到一个归档等
DocSalvager

5

通常最好将大型BLOB存储在单独的表中,并仅在主表中保留对BLOB的外键引用。这样,您仍然可以从数据库中检索文件(因此不需要任何特殊代码),并且避免了围绕外部数据库依赖项的问题(保持数据库和文件系统同步等),但是您仅会产生开销如果您显式加入该表(或进行单独的调用)。10MB并不是很大,大多数现代商业数据库都不会有问题。我将文件存储在文件系统中的唯一原因是减少数据库带宽。如果您的数据库将要处理大量这些文件,那么您可能需要拆分工作量,仅存储某种文件描述符。然后,您可以单独调用以从另一台服务器加载文件,


4

您可能会遇到以下一些问题:

  • SELECT *即使您不需要blob,使用涉及大blob的行进行a 也会花费很长时间(当然,您应该进行特定的选择,但是有时应用程序是这样编写的)
  • 进行备份可能需要更长的时间。根据您的需要,您可能需要在备份时锁定表,因此您可能希望将备份时间保持在较低水平
  • 恢复也将花费更多时间。
  • 如果空间不足,则必须考虑某种方法(也许将整个数据库移至新服务器)来解决此问题。将文件存储在文件系统上,您始终可以挂载另一个硬盘驱动器并设置软链接。
  • 仅查看文件进行调试或其他信息并不容易。这也包括可能无法访问数据库但需要来自各种文件的某些信息的脚本。

当然,您还会获得一些好处:

  • 备份数据和文件同步状态
  • 无法在数据库不知道的情况下删除文件
  • 您不必从磁盘读取文件,但可以在一个sql语句中完成此操作
  • 您可以下载数据库,将转储包含到开发环境中,并在那里拥有所有依赖项

就我个人而言,我不这样做,因为我发现缺点比优点多得多。但是如上所述,它完全取决于您的用例。


1

一些Enterpirse内容管理系统(例如SiteCore)正在使用一个数据库存储页面数据,并使用另一个数据库存储文件。他们正在使用MS SQL Server。


这如何回答所提问题?
蚊蚋

如果进行一些研究,您会发现SiteCore是最受欢迎的企业内容管理系统之一。SiteCore支持大量并发用户,并且可以很好地扩展,因此,如果操作正确,将文件存储在单独的数据库中并不是一个坏习惯。
šljaker

1

对于实际实施,以下是您可能需要关注的问题:

优点:

  1. 所有文件内容都肯定与您的表同步。正如上面的评论所述,备份数据非常方便,因为您无需使数据与文件系统保持同步。
  2. 通过编码,您可以直接从SQL select获取文件内容。
  3. 从查询中,您甚至可以从SQL语句中显式过滤文件内容或其大小。

缺点:

  1. 与结构上在语义上相同但不存储文件内容的数据库相比,您的数据库在进行查询时往往会消耗更多的内存。
  2. 自动备份会导致性能问题,但是影响不大。假设您的数据库服务器每6小时备份一次,而您拥有的那些数据库每条记录将存储10 MB文件。这种情况不是您想要的。
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.