二进制文件应该存储在数据库中吗?

Jac*_*las 147 database-design blob

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

  1. 使用 blob 存储在数据库中
  2. 使用数据库中的链接存储在文件系统上
  3. 存储在文件系统中但重命名为内容的散列并将散列存储在数据库中
  4. 我没有想到的东西

(1) 的优点是(除其他外)事务的原子性得以保留。代价是您可能会显着增加存储(和相关的流/备份)要求

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

编辑:

看起来一些 RDBMS 以各自的方式涵盖了这一点 - 我很想知道其他人是如何做到的 - 特别是在 postgres 的解决方案中

Tan*_*ena 63

  1. 使用 blob 存储在数据库中

    一个缺点是它会使您的数据库文件非常大,并且可能太大而无法使用现有设置进行备份。一个优点是完整性和原子性。

  2. 使用数据库中的链接存储在文件系统上

    我在这样做时遇到了如此可怕的灾难,而且人们不断提出建议让我感到害怕。一些灾难包括:

    • 一位特权用户,他会重新排列文件并经常断开数据库中路径与它们现在所在位置之间的链接(但不知何故,这成了我的错)。
    • 当从一台服务器移动到另一台服务器时,一些文件的所有权丢失了,因为旧机器管理员帐户的 SID(旧网站正在运行)不是域的一部分,因此复制的文件具有 ACL 可以无法解决,从而向用户显示用户名/密码/域登录提示。
    • 一些路径最终被超过256个字符从C:\一路的.doc,而不是NT的所有版本都能够处理长路径。
  3. 存储在文件系统中但重命名为内容的散列并将散列存储在数据库中

    根据我对上述场景的解释,我工作的最后一个地方是这样做的。他们认为这是组织无法获得大型数据库经验(任何大于 40G 的东西都被规定为“太大”)、公司无法购买大型硬盘驱动器以及无法购买更现代的后备箱之间的折衷方案。解决方案,以及摆脱我上面确定的风险 #1 和 #3 的需要。

我的观点是,在多服务器场景中,作为 blob 存储在数据库中是更好的解决方案,并且更具可扩展性,尤其是在故障转移和可用性问题上。

  • 整个“链接中断”是应用程序问题而不是数据库问题。数据库正在做它的工作(提供纯数据),而应用程序没有(提供混合文件类型)。应用程序应该负责提供文件。通过在数据库中存储一个抽象路由路径,无论文件在服务器内部存储在哪里(ala Symfony2 路由),该路径都可以工作。这将抽象出本机路径,使应用程序更具可移植性、可维护性,并允许在不破坏任何内容的情况下切换到任何类型的文件系统。 (12认同)
  • 我不确定备份大小是否有问题;数据需要备份,但它是存储的。无论我们谈论的是 FS 还是 DB,都会做出相同的差异与完整决定。我确实注意到这是一个可能的论点,而不是你的观点。 (2认同)
  • 我曾经遇到过一个问题,每天数百兆字节被写入*每行*数千次。他们将 GZIP 文件作为 10000 个服务器的二进制文件存储在数据库中,但引入了一个错误,其中每个服务器记录每个服务器的信息,每个警报。那太差了。在那次事件之后,我对“没有(MAX)数据类型,除非它非常合理”变得坚定不移。 (2认同)

gbn*_*gbn 29

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

无论如何,大多数 RDBMS 都对存储 BLOB(例如 SQL Server 文件流)进行了优化

  • @JackPDouglas 服务器管理员和 DBA 也有可能是不同的团队,相关的风险是文件被错误删除,或者没有备份,因为它们被认为是临时文件。 (6认同)
  • @JackPDouglas:你有散列,这是不正确的数据,并且仍然有数据完整性的外部依赖 (4认同)

ik_*_*elf 22

如果使用 oracle,请查看 dbfs 和 Secure Files。

安全文件说明了一切,确保数据库中的所有数据安全。它以高球组织。Secure Files 是 lob 的现代化版本,应该被激活。

dbfs 是数据库中的文件系统。您可以像网络文件系统一样在 Linux 主机上安装它。它真的很强大。请参阅博客它还有很多选项可以根据您的特定需求进行调整。作为一名 dba,给定一个文件系统(基于数据库,安装在 Linux 上),我在其上创建了一个 Oracle 数据库,没有任何问题。(一个数据库,存储在...数据库中)。并不是说这会很有用,但它确实显示了力量。

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

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

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

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


Eva*_*oll 21

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

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

  • 数据库中的文件没有文件句柄这是什么意思?

    • 程序员谈话:您不能寻求 ( fseek),无法通过异步访问 (asyncioepoll)来管理资源,没有sendfile(从内核空间保存副本)。

    • 实际应用:想通过HTTP2/3向客户端发送视频或图片?如果它在数据库中,那么您首先必须查询它。对于返回该文件的任何查询,您必须等待整个查询结束,然后该文件才能移动到下一步。在与 Web 服务器不同的服务器上使用 rdbms 进行生产安装时,您首先必须将文件完全从 rdbms 传输到 Web 服务器,而不是通过流式传输。但是,如果传输层提供了文件系统抽象(甚至 NFS 支持),您可以在文件中途寻找并立即开始将其流回客户端,而无需缓冲任何不必要的文件。这通常由网络服务器完成nginxApache、PureFTP 和 ProFTP。

  • RDBMS 上的双重复制。由于它位于数据库中,因此您可能会编写两次。一次写入预写日志 (WAL),然后再次进入表空间。

  • 没有更新, MVCC意味着什么都不会更新,只会通过修改重新复制,然后旧行被标记为过期(删除)。对文件的任何更新都需要写入整,而不仅仅是整行的文件。文件系统也可以通过数据日志提供此功能,但您很少需要它。

  • 文件读取和传输以减慢查询速度如果文件本身存储在您需要查询的行上,则整行要么必须等待文件传输,要么您必须发出两个单独的查询.

  • 数据库客户端上的内存使用。DB 客户端(libpq、jdbc、odbc、freetds 等)等可能会在内存中缓冲查询。当内存中的缓冲区耗尽时,它可能会启动一个磁盘缓冲区,或者更糟糕的是,它可能会回退到内核以分页到磁盘。

  • 当查询占用太多时间或资源时,查询限制许多数据库提供了终止和获取查询的能力。请记住,文件传输不会在任何实现中逐项列出。该查询是否在 3 秒后被终止?还是只用了 1 秒,而后端用了 2 秒传输文件?不仅仅是“逐项列出”,当 99.9% 的查询返回 1 KB 而另一个查询返回 1 GB 时,您将如何有效地说明查询应该花费多长时间?

  • 无写入时复制或重复数据删除XFS 和 BTRFS 透明地支持写入时复制和重复数据删除。这意味着在任何地方都有相同的图片,或者需要它的第二个副本可以由文件系统透明地处理。但是,如果文件不是独立存在的,而是在一行中或在存储中,则文件系统可能无法对其进行重复数据删除。

  • 诚信很多人在这里都在谈论诚信。您认为在检测文件系统损坏、使用文件系统或文件系统核心实用程序的应用程序方面,什么更好?将文件连续存储或离线存储,任何文件系统损坏都将掩盖数据库。xfs_repair当文件系统或硬盘损坏时,它非常擅长恢复,如果失败,进行数据取证仍然容易得多。

  • 云迁移如果您想将文件存储在 SAN 或云上,您将遇到更多困难,因为现在存储迁移是数据库迁移。例如,如果您的文件存储在文件系统上,您可以很容易地将它们移动到 S3(并且类似的s3fs东西可以是透明的)。

例外

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

  • 当您需要临时编辑文件时。这意味着编辑文件实际上是您事务的一部分。或者,如果事务因关系(表)中的数据完整性问题而失败,则您需要能够回滚对文件的编辑。
  • 当您需要确保文件系统与数据精确版本控制并且您无法承受保持它们同步的任何风险时。
  • 当您的数据库可以真正解析文件,您可以查询它。例如,在 PostgreSQL 中,拓扑可以是使用 PostGIS 的查询。此时,虽然它是一个文件,但它也是用于查询的数据,而不是存储转储。

缓解措施

  • 某些数据库有“外部管理资源”的概念,其中数据库要么私下管理磁盘上的文件,例如

  • 一些数据库以外部方式或可以存储大型二进制对象,例如 Oracle SecureFile。这允许您更新行,而无需重写文件。

  • 某些数据库(如 Oracle)在没有 WAL 日志的情况下执行 MVC,并且不必将文件写入双倍。

  • 一些数据库,如 SQL Server 和 Oracle,提供从文件“流”数据的能力,而无需文件句柄。这可能会或可能不会在与数据库查询不同的连接上运行。但这里的关键是,虽然您可以流式传输文件(理论上),但我找不到任何不是由使用该功能的提供商制造的产品的任何证据。例如,允许您执行此操作的 NGINX/Apache 桥在何处?

  • Oracle 通过内部 LOB 存储(如 SecureFile)提供可选的重复数据删除、压缩和加密。

结论

将文件放入数据库的最坏情况对性能和工具兼容性非常不利。它总是特别依赖于实现。数据库在作为文件系统方面绝不作为文件系统更好。在各个方面,这都是一种妥协,即使您获得了强大的缓解功能(例如 SecureFile 的情况),该工具也非常糟糕,除非您的整个堆栈是由 RDBMS 提供商构建的,否则它实际上只是一个营销点。

保持简单,一般规则是将文件保留在 DB 之外

解决方案

您应该如何存储文件或以这种方式抽象文件系统以有效地为多个租户和用户运行?我偏爱散列文件内容。这在当今很常见,并且效果很好。

  • @GregoryStein 所有的观点仍然有效。考虑使用具有 [SHA256 指令](https://en.wikipedia.org/wiki/Intel_SHA_extensions) 的现代架构来加速哈希处理,该指令应哈希许多 GBPS/秒。或者,使用 blake3 哈希器,它应该能够在软件和并行中执行许多 GBPS (3认同)

Tek*_*Tek 17

不要这样做。

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

当你自己思考时,是不是已经感觉很奇怪和可疑:

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

更好的是,大声说出来。

说到事实:

使用数据库

优点”...但不完全是

  • “原子性”是正确的,但它是一把双刃剑。因为它会拖累缺点。
  • 正直。和上面一样。

我真的不想有偏见,但我认为没有更多要补充的了。如果你仔细想想,专业人士并不是那么好。

如果我忘记了下面的评论,同时继续阅读下面的内容。

缺点:

  • 错误的工作工具
  • 更难维护
  • 减缓
  • 忘记为每个用户存储数百 MB/GB 的数据吧。
  • 备份快速增长的站点将是一场噩梦。
  • 恢复/移动也会很糟糕。

使用文件系统

优点:

  • 更容易维护
  • 快速地
  • 数据库备份与此无关
  • 可以说更具便携性*

缺点

  • 没有任何*

*印刷精美

现在你问自己,坚持你的意思是没有缺点?!怎么来的?

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

主要原因,我会说唯一的原因是因为文件链接

这是数据库不打算解决的问题。如果你仔细想想,这甚至听起来很傻。

“数据库将修复我的文件链接问题。”

在现实中,从逻辑上讲,应用程序实际上应该负责处理和提供链接。

一个办法:

  1. 让您的应用程序使用自定义路由处理 URL 请求。
  2. 将此路线保存到您的数据库。
  3. 在内部每次调用此路由时将其映射到您想要的文件。
  4. 如果您曾经将文件移动到其他地方,只需更改路由的文件名值,并且该路由将始终提供相同的文件,无论它存储在何处或通过网络引用。

这也将抽象出本机路径,使应用程序更可移植、更易于维护,并允许在不破坏任何内容的情况下切换到任何类型的文件系统。

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

https://github.com/symfony/Routing

https://github.com/kriswallsmith/assetic

这两个加在一起真的很强大。

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

Nat*_*lly 16

我认为这里的正确答案很大程度上取决于您的申请以及这些文件的重要性。

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

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

Helpdesk 系统和 wiki 类型的系统是我认为将数据保留在数据库之外很有意义的系统。我相信有些人,比如 Jira,实际上提供了一个选项来选择是否要内联存储文档。

对于中型企业,在线存储票务系统文档可能意味着以兆字节为单位的压缩备份和以千兆字节为单位的压缩备份之间的差异。

我个人更愿意在几分钟内将票务系统重新上线,并在几个小时内处理(通常不太重要的)文件,而不是通过必须恢复来增加我的“它坏了,CTO 喘不过气来”的 RTO并从更大的备份中重播日志。

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

  • 您可以轻松地运行单独的进程来对文档元数据进行编目、执行病毒扫描、执行关键字索引等。
  • 您可以利用工具来协助备份或恢复——rsync、存储快照等——它们比数据库更适合文件
  • 您实际上可以使用支持压缩或重复数据删除的存储(您的 SAN 管理员多年来一直在喋喋不休的东西,也就是全球数据库管理员的祸根)
  • 对于跨多个站点的安装,您可以使用分布式文件系统补充集中式数据库

我认为#2 和#3 的混合组合可能很聪明。保留原始文件名,但计算并存储文档的哈希/校验和,以便您有一些参考点,以便在有人移动或重命名文件时帮助恢复。

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


Chr*_*ers 10

我想在这里添加我的经验以进行权衡。至少在 PostgreSQL 中,就 db 服务器而言,性能影响非常小。大 blob 存储在单独的文件中,而不是在主堆表中,以便将它们移出可能计算大量记录的操作。其他数据库可能会做类似的事情。

主要优点是能够将所有相关数据保存在一个地方以实现原子性和备份目的。这大大降低了出错的可能性。

主要的缺点不是我在上面看到的,那就是前端的内存使用。我不知道每个 db 是如何处理这个的,所以这可能取决于实现,但对于 PostgreSQL,数据以转义的 ASCII 字符串形式出现(可能是十六进制,可能带有内联转义)。然后必须在前端将其转换回二进制。我见过的许多框架都涉及传递值(不是作为参考),然后基于它构造一个新的二进制字符串。我计算出使用 Perl 来做这件事最终会使用很多倍的原始二进制文件的内存来完成。

结论:如果文件只是偶尔被访问,我会存储在数据库中。如果它们被频繁且重复地访问,至少使用 PostgreSQL,我认为成本大于收益。


dat*_*god 7

过去,微软大肆宣传在数据库中存储图像(和类似的 blob 数据类型)的能力。这是 SQL Server 2000 的一个很酷的新功能(我很确定它是 2000,而不是 7.0),很多人都加入了这个潮流。

将 BLOBS 存储在数据库中有利有弊:

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

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

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

您的备份可能会变得非常大,但您最终不会得到孤立的文件/文档/blob/图像。

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

  • 没关系,如果您不拥有服务器,您将为数据库空间与文件空间每 MB 支付更多的费用。将文件放在磁盘上也可以更轻松地进行故障排除 - 您如何在 SSMS 中“从表中选择图像”并验证是否存在正确的图像? (5认同)

小智 7

我的一票都不赞成。将数据存储在 Amazon S3 或 Microsft 的 CDN 等系统中,并将该 URL 存储在数据库中。

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


Phi*_*llo 6

虽然它部分取决于应用程序/环境(包括人),但我会选择 blob。

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

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

假设我们有多个具有单独权限的用户/应用程序,那么任何文件系统存储都为 DB 和 FS 访问权限的差异提供了机会。

我考虑对 BLOB 存储进行的改进是对数据进行分块(如果有意义的话);如果您只需要 20Mb BLOB 中的 512 个字节,这种类似扇区的访问是一个真正的福音,尤其是当您处理远程客户端时(同样,部分更新创建的复制流量要少得多)。