在数据库中存储与数据相关的二进制文件的最佳位置是什么?您是否应该:


用blob存储在数据库中
用链接在数据库中存储在文件系统中
存储在文件系统中,但重命名为内容并将哈希表存储在数据库中
我没想到的是

(1)的优点(尤其是)保留了事务的原子性。代价是您可能会大大增加存储(和相关的流/备份)需求

(3)的目标是在某种程度上保留原子性-如果您可以强制执行写入的文件系统不允许更改或删除文件,并且始终具有正确的哈希作为文件名。想法是在允许插入/更新引用哈希之前将文件写入文件系统-如果此事务在文件系统写入之后但在数据库DML之前失败,则可以,因为文件系统正在“伪造”为所有存储库可能的文件和哈希-里面是否有没有指向的文件都没关系(如果小心,可以定期清理它们)

EDIT:

似乎有些RDBMS以其各自的方式涵盖了这一点-我很想知道其他RDBMS是如何做到的-特别是在针对postgres的解决方案中

评论

这个问题在这里重复:将图像存储在Blob中还是仅将URL存储在其中更好?由于这一点更加出色,所以对这一点予以关闭。请务必阅读两个问题以获得更多见解!

#1 楼



用blob存储在数据库中。

缺点是,它会使数据库文件很大,甚至太大,无法用现有设置进行备份。优点是完整性和原子性。


将文件存储在文件系统中并带有数据库中的链接

我遇到了如此可怕的灾难,令人恐惧的是,人们一直在建议它。其中一些灾难包括:


一个特权用户,该用户将重新排列文件并经常断开DB中路径与它们现在所在位置之间的链接(但不知何故,这是我的错) 。
从一台服务器移动到另一台服务器时,由于旧计算机的管理员帐户(运行旧网站的SID)不在域中,因此某些文件的所有权丢失了,因此复制的文件具有无法解析的ACL,因此向用户显示用户名/密码/域登录提示。
C:\.doc的某些路径最终都超过了256个字符,而且并非所有版本的NT都能够处理长路径。



存储在文件系统中,但重命名为内容的哈希并将哈希存储在数据库中

我上次工作的地方这是基于我对上述情况的解释而完成的。他们认为,这是组织无法获得大型数据库经验(规定大于40G的数据“太大”),企业无法购买大型硬盘驱动器以及无法购买更现代的后背之间的折衷方案。解决方案,以及摆脱我在上文中提到的#1和#3风险的需要。


我的观点是,在多服务器方案中,尤其是在故障转移和可用性方面,以blob形式存储在数据库中是一个更好的解决方案,并且具有更高的可伸缩性。

评论


我不确定备份大小是否有问题;数据需要备份,但是要存储。无论是谈论FS还是DB,都会做出相同的差异与完全决定。我确实注意到,这是一个可能的论点,而不是您的观点。

– Phil Lello
2011年4月29日在16:48

我曾经遇到过一个问题,每天每行数千次写入数百兆字节。他们将GZIP文件作为10000个服务器的二进制文件存储在数据库中,但是引入了一个错误,即每个服务器每个警报记录每个服务器的信息。那太差了。在那次事件之后,我坚决提出“除非有充分的理由,否则不要使用(最大)数据类型”。

–阿里·拉泽吉(Ali Razeghi)
13年1月29日,0:20



整个“链接断开”是一个应用程序问题,而不是数据库问题。数据库正在完成其工作(提供纯数据),而应用程序则没有(提供混合文件类型)。该应用程序应负责提供文件。通过将抽象路由路径存储在数据库中,无论文件内部存储在哪里,该抽象路由路径都将起作用(ala Symfony2路由)。这将抽象出本机路径,使应用程序更可移植,可维护,并允许切换到任何类型的文件系统而不会破坏任何内容。

– Tek
2014年7月31日在18:28



#2 楼

编号1可实现完整的数据完整性。如果您不关心数据质量,请使用其他选项。就这么简单。

多数RDBMS都有优化的存储方式,无论如何都存储BLOB(例如SQL Server文件流)

评论


(3)到底是什么使数据完整性面临风险? (假设您正确使用了交易API)

–杰克·道格拉斯(Jack Douglas)
2011-4-29 13:05



@JackPDouglas:您的哈希值不是正确的数据,并且仍然依赖于数据完整性

– gbn
2011年4月29日在14:02

@JackPDouglas服务器管理员和DBA也有可能是不同的团队,并且存在文件被错误删除或未被备份的相关风险,因为它们被视为临时文件。

– Phil Lello
11年4月29日在16:23

#3 楼

如果要使用oracle,请查看dbfs和安全文件。

安全文件说明了一切,将所有数据安全保存在数据库中。它以高大的形式组织。安全文件是lob的现代化版本,应将其激活。

dbfs是数据库中的文件系统。您可以像在网络主机上一样将其安装在Linux主机上。它是真正强大的。查看博客它也有很多选项可以调整以满足您的特定需求。作为dba,给定一个文件系统(基于数据库,安装在Linux上),我在上面创建了Oracle数据库,没有任何问题。 (数据库,存储在...数据库中)。并不是说它会非常有用,但是它确实显示出了强大功能。

更多的优势是:可用性,备份,恢复,所有读取都与其他关系数据保持一致。

有时给出大小是作为不将文档存储在数据库中的原因。可能必须以任何方式备份该数据,因此这不是不存储在数据库中的充分理由。特别是在旧文档被视为只读的情况下,很容易使数据库的大部分变为只读。在那种情况下,数据库的那些部分不再需要频繁备份。

表中对数据库外部内容的引用是不安全的。它可以被操纵,难以检查并且容易丢失。交易怎么样?该数据库为所有这些问题提供了解决方案。使用Oracle DBFS,您可以将文档提供给非数据库应用程序,他们甚至都不知道正在向数据库中戳。

最后一个大惊喜是,dbfs文件系统的性能通常比常规文件系统。如果文件大于几个块,则尤其如此。

#4 楼

我认为正确的答案在很大程度上取决于您的应用程序以及这些文档的重要性。

对于文档管理系统或对存储文档的可恢复性至关重要的系统(因此,大多数情况,与HR或CRM相关),内联存储文档或使用您喜欢的数据库供应商专有文档技术似乎都是正确的做法。

但是,在许多应用程序中,我认为相反的决定是适当的。

服务台系统和Wiki类型的系统是我认为将数据保留在数据库之外的一种非常有意义的系统。我相信有些人(例如Jira)实际上提供了选择是否要内联存储文档的选项。

对于中型企业,内联存储票务系统的文档可能意味着压缩备份以兆字节为单位,而压缩备份以千兆字节为单位。

我个人更愿意在几分钟内使票务系统恢复在线状态,并与(通常不那么重要的)文件进行数小时的搏斗,而不是增加“它已损坏且CTO喘不过气来”我的脖子” RTO,因为它必须从更大的备份中还原和重播日志。

将文档分开还有其他好处。


您可以轻松地运行对文档元数据进行分类,执行病毒扫描,执行关键字索引等等的独立过程。
您可以利用工具来辅助备份或恢复-rsync,存储快照等-与数据库相比,它们更适合文件操作
您实际上可以使用支持压缩或重复数据删除(您的SAN管理员多年来一直在疲倦的东西,又名全球数据库管理员的祸根)我认为#2和#3的混合组合可能很聪明。保留原始文件名,但计算并存储文档的哈希/校验和,以便您有一些参考点,以防他人移动或重命名文件时进行恢复。

使用原始文件名存储文件意味着应用程序可以从字面上直接从文件系统中提取文件并通过网络发送它们,或者在胖客户端环境中,甚至可以将用户直接指向文件服务器。

#5 楼

不要这样做。

将文件存储在数据库中确实没有什么好处。

当您想您自己:


我应该将文件存储在数据库还是文件系统中?


更好,大声说出来。
<事实:

使用数据库

“ PROS” ...但不完全是:


“ “原子性”是正确的,但这是一把双刃剑。因为它会拖动
缺点。
完整性。与上面相同。

我真的不想被偏见,但我不认为有更多补充。如果您考虑一下,专业人士并不是真的很厉害。

如果我在下面忘了评论,请同时阅读以下内容。

缺点:


工作工具错误
维护困难
速度慢
忘记为每个用户存储数百MB / GB的数据。
备份迅速增长网站将是一场噩梦。
恢复/移动也很麻烦。

使用文件系统

PROS:


更容易维护的方式
快速
数据库备份与此无关
可移植性更高*

缺点:


没有*

*精美的印刷品

现在您要问自己,坚持一下,这意味着没有缺点吗?怎么了?

这里最大的错误是人们试图用锤子拧螺丝。

主要原因是我想说的唯一原因被问到是因为文件链接。

这不是数据库无法解决的问题。如果您考虑一下,它甚至听起来很愚蠢。


“数据库将解决我的文件链接问题。”


实际上,从逻辑上讲,应用程序应实际上负责处理和处理文件。服务链接。

解决方案:


使您的应用程序处理具有自定义路由的URL请求。
将此路由保存到数据库中。
每次调用此路由时,内部都会将其映射到所需文件。
如果将文件移动到其他位置,只需更改该路由的文件名值,该路由将始终无论在Web上的存储或引用位置,都可以使用相同的文件。

关于如何实现它超出了此答案的范围,但是您可以看一看可以说是使用最广泛的Web语言(PHP)的一般示例:

https://github.com/symfony/Routing

https://github.com/kriswallsmith/assetic

两者都非常强大。

评论


您可能对此感兴趣:research.microsoft.com/apps/pubs/default.aspx?id=64525 Microsoft进行的一项研究表明,在数据库中存储Blob实际上比在文件系统中存储Blob快(对于某些Blob大小)至少)。这与我的测试一致,该测试表明对于中等大小的Blob(<〜1MB),例如Postgres也比文件系统快。对于Oracle,它的性能大致相同,但是我尚未测试新的安全文件存储格式(但是他们声称它比旧的存储格式快)

– a_horse_with_no_name
2014年8月1日下午5:56

我看到了,这就是为什么我谈论大文件的原因。另外,OP没有指定数据库供应商,因此供应商之间的性能可能有所不同,因此我的建议更为笼统。

– Tek
2014年8月1日13:37



#6 楼

不要将文件存储在数据库中。

可以在市场上运行任何RDBMS的每个人都毫无例外地已经拥有专门用于存储文件的数据库,而RDBMS本身正在使用它!该数据库是文件系统。现在,我们来讨论在数据库中存储文件的一些潜在弊端,以及在数据库中存储文件的一些特定缓解因素。



文件没有文件可用在数据库中。这是什么意思?


程序员对话:您无法寻求(fseek),无法通过异步访问管理资源(asyncioepoll),没有sendfile (可以节省内核空间中的副本)。
实际应用:是否要通过HTTP2 / 3将视频或图片发送到客户端?如果它在数据库中,则首先必须查询它。对于任何查询返回该文件的查询,您都必须等待整个查询结束,然后该文件才能继续进行下一步。在与Web服务器不同的服务器上使用rdbms进行生产安装时,首先必须将文件完全从rdbms传输到Web服务器,而不是通过流传输。但是,如果传输层提供了文件系统抽象(甚至NFS都支持),则可以在文件中途进行搜索,然后立即开始将其流式传输回客户端,而无需缓冲任何多余的文件。这通常由网络服务器nginx,Apache,PureFTP和ProFTP完成。


RDBMS上的双副本。由于它确实存在于数据库中,因此您可能会编写两次。一次进入预写日志(WAL),然后再次进入表空间。
没有更新,MVCC意味着什么都不会更新,只有经过修改才能重新复制,然后旧行被标记为过期(已删除)。对文件的任何更新都将需要写入整行,而不仅仅是文件整行。文件系统也可以通过数据日志来提供此功能,但是您很少需要它。
文件读取和传输可以减慢查询速度如果文件本身存储在需要查询的行中,则整行将必须等待文件传输,否则您将不得不发出两个单独的查询。
DB客户端上的内存使用情况。 DB客户端(libpq,jdbc,odbc,freetds等)可能会将查询缓冲在内存中。当内存中的缓冲区耗尽时,它可能会启动一个磁盘缓冲区,甚至更糟的是,它可能会退回到要分页到磁盘的内核。
查询限制许多数据库提供了在查询时杀死并获得查询的功能。要么浪费太多时间,要么浪费资源。请记住,文件传输在任何实现中都不会逐项列出。 3秒后该查询是否被杀死?还是花了1秒,而后端花了2秒来传输文件?不仅是“ itemized”,当99.9%的查询返回1 KB而另一个返回1 GB时,您将如何有效地说明查询应花费多少时间?
无写时复制或删除-deduplication XFS和BTRFS透明地支持写时复制和重复数据删除。这意味着文件系统可以透明地处理到处都具有相同图片或需要第二张图片的情况。但是,如果文件不是独立存在的,而是位于行中或位于存储中,则文件系统很可能无法对其进行重复数据删除。
诚信这里有很多人在谈论诚信。您认为在检测文件系统损坏,使用文件系统或文件系统核心实用程序的应用程序方面有什么更好的选择?连续或离线存储文件,任何文件系统损坏都将被数据库遮盖。 xfs_repair非常擅长在文件系统或硬盘驱动器损坏时进行恢复,如果失败,则进行数据取证仍然会容易得多。
如果您想将文件存储在SAN或云中,则可以进行云迁移。在云中,您将面临更多困难,因为现在存储迁移是数据库迁移。例如,如果您的文件存储在文件系统上,则可以很容易地将它们移动到S3(并且类似s3fs的文件可以是透明的)。

异常

存储数据库中的文件有一些有效的用例,


当您需要过渡地编辑文件时。这意味着编辑文件实际上是您的交易的一部分。或者,如果事务因关系(表)中的数据完整性问题而失败,则您需要具有对文件进行回滚编辑的能力。保持同步不会带来任何风险。
数据库实际上可以解析文件并进行查询时。例如,在PostgreSQL中,拓扑可以是使用PostGIS的查询。此时,虽然它是一个文件,但它也是查询的数据,而不是存储转储。

缓解措施




某些数据库有一个概念“外部管理的资源”,其中数据库通过大对象基础结构私下管理磁盘上的文件,例如


PostgreSQL在事务期间为资源提供文件句柄。
SQL Server 2017的文件流基础结构提供了在事务期间持续的临时访问,您可以使用该临时访问获取文件路径并打开文件句柄。
Oracle提供了BFILE(这与其内部LOB无关)名为SecureFile的东西


一些数据库离线存储大型二进制对象,或者可以像Oracle SecureFile那样存储大型二进制对象,这使您可以更新行而无需重写文件。
像Oracle这样的一些数据库在没有WAL日志的情况下执行其MVC,并且不必重复写入文件。
像SQL Server和Oracle这样的一些数据库提供了从数据库“流”数据的功能。文件而没有文件句柄,这可能会或可能不会在与databaes查询不同的连接上运行。但是这里的关键是,尽管您可以流式传输文件(理论上),但我找不到任何证据非使用该功能的提供商提供的任何产品,例如,NGINX / Apache在哪里
Oracle通过内部LOB存储(例如SecureFile)提供了可选的重复数据删除,压缩和加密。

结论

在数据库中放置文件的情况非常不利于性能以及与工具的兼容性。它总是异常依赖于实现。数据库绝对不是文件系统,而是文件系统。从各个方面来说,这都是一个折衷,即使您获得了强大的缓解功能(例如SecureFile的情况),该工具也是如此糟糕,以至于它实际上只不过是一个营销点,除非您的整个堆栈都是由RDBMS提供程序构建的。

保持简单,一般规则是将文件保留在数据库之外。

解决方案

您应该如何存储文件,或以这种方式抽象文件系统以对多个租户和用户有效起作用?我偏爱散列文件内容。这些天来这很普遍,而且效果很好。

评论


散列文件数据听起来很聪明。即使您没有太多文件要检查,即使文件名已更改,也可以始终将其与db条目匹配。干杯。

–timuçin
12月11日15:52

#7 楼

我想在此添加有关折衷的经验。至少在PostgreSQL中,就数据库服务器而言,对性能的影响很小。大的Blob存储在单独的文件中,而不是存储在主堆表中,以便将它们移出可能占用大量记录的操作方式。其他数据库可能会做类似的事情。

主要优点是能够将所有相关数据保留在一个地方,以实现原子性和备份目的。这大大减少了出现问题的机会。

最大的缺点不是我在上面看到的一个缺点,那就是前端的内存使用情况。我不确切地知道每个数据库是如何处理的,因此这可能取决于实现,但对于PostgreSQL,数据以转义的ASCII字符串(可能为十六进制,可能带有内联转义)输入。然后必须在前端将其转换回二进制。我已经看到许多这样做的框架涉及传递值(不作为参考),然后基于该值构造一个新的二进制字符串。我计算得出,使用Perl进行此操作最终会使用原始二进制文件的内存来完成很多操作。

结论:如果仅偶尔访问文件,则将其存储在db中。如果至少是在PostgreSQL中经常或反复访问它们,我认为成本会超过收益。

#8 楼

过去,Microsoft大力宣传了在数据库中存储图像(和类似的Blob数据类型)的功能。这是SQL Server 2000的一项很酷的新功能(我很确定它是2000,而不是7.0),许多人对此大为追捧。

在数据库中存储BLOBS具有优点和缺点:

一方面,您所有的数据和相关图像或文档都可以在一个地方存储和访问。应用程序用户不需要特殊的网络权限,因为SQL负责提供图像/文件/文档。

另一方面,数据库的大小可能会很大,具体取决于大小和数量您正在存储的BLOBS。这会影响备份,存储要求,对时间敏感的恢复操作等。

SQL Server 2008引入了文件流传输。数据库包含指向文件的指针,文件驻留在服务器上而不是数据库中,但是当您备份数据库时,文件也会被备份。

您的备份可能会很大,但最终不会出现孤立的文件/文档/斑点/图像。

我个人的喜好是让数据库存储指针/网络位置,并让文件服务器处理文件。无论如何,文件服务器都针对此类任务进行了更好的优化。

评论


没关系,如果您不拥有服务器,则数据库空间和文件空间的价格将为每MB付出更多。同时,将文件放在磁盘上也使故障排除变得更加容易-如何在SSMS中从表中选择图像并验证是否存在正确的图像?

–亚伦·伯特兰(Aaron Bertrand)
2011年9月26日15:07

#9 楼

尽管它部分取决于应用程序/环境(包括人员),但我还是很喜欢。

保留数据库中的所有内容意味着复制适用于文件数据。您需要一种单独的机制来同步FS文件。

在某些应用程序中,无论如何都不应修改文件系统。例如,在生产网站上,我避免使用文件系统处理任何非一次性数据(站点生活在SCM下,数据库中的数据)。

假设我们有多个用户/应用程序具有单独的权限,那么任何文件系统存储都会为DB和FS访问权限的差异提供一个机会。如果您仅需要20Mb BLOB中的512字节,则这种类似于扇区的访问将是一次真正的福音,尤其是当您正在处理远程客户端时(同样,部分更新会减少复制流量)。

#10 楼

我的投票不会。将数据存储在Amazon S3或Microsft的CDN之类的系统中,并将该URL存储在数据库中。

通过这种方式,您可以始终访问数据而无需处理庞大的数据库,从而获得可靠性。

#11 楼

对于postgres:

实际上是很直接的。有一个BYTEA类型可用于存储二进制字符串。默认情况下,没有内置工具(如针对MS或Oracle提到的工具)。因此,存储大量大文件并进行检索可能会很乏味。您还需要在应用程序内进行文件转换(例如使用ByteStream或类似文件,但不知道如何将其与特定的MS / Oracle文件<->数据库解决方案一起使用)。还有一个lo类型,它有助于管理BLOB,因为这些类型的某些内部管理可能无法跟踪引用。

#12 楼

分享我对SQL Server女士和大量文件的经验。我们将文件保存在文件服务器上。数据库有两个表,一个表用于文件夹和访问凭据,一个表用于文件名。维护数据库和文件很容易。您甚至可以跨服务器轻松地移动文件,只需修改文件夹表即可。