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

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

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

我在此项目中使用的是PHP和MySQL,但对于大多数环境(Ruby on Rails,PHP, .NET)和数据库(MySQL,PostgreSQL)。

评论

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

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

大是一个相对数量,在现代系统中,我(可能还有很多其他人)认为10MB不那么大。

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

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

#1 楼

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


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

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

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

评论


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

–比利·奥尼尔(Billy ONeal)
2012年5月30日17:55

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

–托马斯
2012年5月30日20:09



嗯,CDN的重点是重复。 CDN仅缓存网址的目标-唯一的要求是必须有一个HTTP主机来提供内容,并且内容很少更改。 (实际上,CDN应该如何告诉您从何处拉出图像?)

–比利·奥尼尔(Billy ONeal)
2012年5月30日20:22

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

–托马斯
2012年5月30日在21:17

@Wecherowski-您寻求的是文件版本控制系统。甚至git之类的源代码控制系统也可以用于文件版本控制。例如,OpenVMS历史悠久,但是有第三方工具可以帮助您实现这一目标。这在很大程度上取决于所需的功能。

–托马斯
6月9日14:54



#2 楼

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

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

评论


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

– briddums
2012年5月29日14:57

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

–vartec
2012年5月29日15:01



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

–提莫西·鲍德里奇(Timothy Baldridge)
2012年5月29日下午16:54

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

–尼克·查马斯(Nick Chammas)
2012年5月29日23:29



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

–提莫西·鲍德里奇(Timothy Baldridge)
2012年5月30日下午3:19

#3 楼

是的,这是一个坏习惯。

对数据库的性能影响:


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

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

结论:它将阻碍数据库性能,并且不会提高文件检索性能。

,因为您在说关于Web应用程序—使用现代Web服务器直接从文件系统提供静态文件,可以执行syscall,极大地提高了性能。如果要从数据库获取文件,这当然是不可能的。例如,以该基准测试为例,它显示Ngnix在低端笔记本电脑上以1000个并发连接进行25K req / s。这种负载将炸毁任何类型的数据库。

评论


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

– deizel
2012年5月29日14:32

程序员什么时候才知道性能并不重要?

– reinierpost
2012年5月30日11:57



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

–vartec
2012年5月30日晚上11:58

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

–vartec
2012年5月31日上午8:59

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

–vartec
2012年6月1日上午8:28

#4 楼

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

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

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

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

评论


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

– HeavyE
17年11月8日在16:47

#5 楼

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


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

结论的一个非常简洁的版本是:比较NTFS文件系统和SQL Server 2005,小于256KB的BLOBS可以由SQL Server更有效地处理,而NTFS对于大于1MB的BLOBS则更有效。


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

评论


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

–Ferruccio
16年9月2日在14:47

#6 楼

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

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


我不知道将要保存很长时间的数据存储在数据库之外没有任何好处。 >
如果它在数据库中,我可以

确保它是经过专业管理的

备份

可恢复(其余)的数据)

受保护的

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

我可以轻松地删除(闪回)

我已经锁定了

我已经阅读了一致性...


评论


这种取决于应用程序。如果您是PACS系统(医学图像),则需要数据完整性和安全性。如果您运行的是诸如Snapchat之类的尽力而为服务,那么您可能并不真正关心丢失的数据,而不是关心性能,只要它很少见。

–培根片
6月5日18:10

#7 楼

是的。

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

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

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

评论


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

– Maple_shaft♦
2012年5月29日19:13

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

– Evan P.
2012年5月30日在1:23



#8 楼

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

#9 楼

著名的Tom Kyte写道,他们(Oracle)正在使用Oracle数据库作为文件服务器,并且运行良好,甚至比普通文件系统更快,并且具有完全事务性,没有性能损失并且具有单个备份。

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

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

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

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

评论


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

– ZenUML.com上的Peng
16年1月1日在2:58

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

– DocSalvager
16-10-27在20:44

#10 楼

您可能会遇到以下问题:


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

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


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

我个人不这样做,因为我发现缺点比专业人员多。但是如上所述,这完全取决于您的用例。

#11 楼

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

评论


这如何回答所提问题?

– gna
13年7月28日在8:42

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

–šljaker
13年7月28日在20:52



#12 楼

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

优点:


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


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


#13 楼

这就是“我可以用剃须刀切开苹果吗?”中的一种。是的,你可以。

应该吗?谁能告诉您...

我猜您是在您的工具允许的情况下出现的,或者是唯一可用的工具(至少要在截止日期之前完成)。例如,我用一字螺丝刀卸下了十字螺栓类型。。。对吗?那是正确的工具吗?这是一个不好的选择吗?

这种情况的答案是:数据库不应该存储文件...每当您做错时,从理论上讲,我就不会使用同样的方法平头螺丝刀卸下菲利普斯螺栓,因为这样做的时候,我失去了菲利普斯螺丝起子不会变位并损坏我正在处理的任何东西的好处(因为如果您使用DB作为文件存储解决方案,将会失去很多好处)。 。然而,我适当地计算了风险,一切都很好。

如果您想做正确的事情,则应该使用git来存储文件,并在数据库中保留必要的git哈希值以进行引用到所需的正确文件版本...用同样的方式,我可以走到工具箱并得到该死的十字螺丝刀...