你会将二进制数据存储在数据库中还是文件系统中?

47

这是一个之前就有人问过的问题(large-text-and-images-in-sql),但主要是针对将被更改的数据。在我的情况下,数据将被存储并且永远不会更改。把所有东西放在一起似乎是明智的选择。

是否有任何原因我不应该在数据库中存储静态二进制数据?

假设这是一个合理的做法,把这样的数据存储在单独的表中是否有任何优势?(您现在可能已经意识到我不是数据库专家...)

澄清: 可能不会超过10-20个用户,但这些用户将在美国和英国。无论如何,二进制数据都必须传输。

11个回答

36
将数据存储在数据库中的优点是利用数据库安全机制和降低维护成本(备份等)。缺点是增加数据库负载并消耗连接(对于按连接计费的数据库服务器可能很昂贵)。如果您使用的是 SQL Server 2008,那么 FILESTREAM 可能是一个不错的替代方案。顺便说一句,对于 Web 应用程序(或任何其他可能需要流式传输数据的应用程序),通常更明智的做法是将数据存储在数据库外部。

2
我不确定它如何降低维护/备份成本。如果有的话,它会增加这些成本,因为通常备份数据库比备份文件系统更昂贵和要求更高。你能详细说明一下吗? - jrwren
5
我的意思是,为了确保数据库备份中包含的数据的完整性,你不需要单独备份文件并手动将它们与数据库备份同步。根据情况,这可能更加简单和便宜。 - Mehrdad Afshari

12

关于执行带有LOB的“select * from table”语句可能导致巨大的内存和/或带宽问题的说法是不存在的。返回的只是指向相关LOB的指针。由于声誉不够,无法将评论放入上下文中,但查看此内容的人应该知道这不是问题。


1
@Matthew 我认为他指的是大型对象 - Franklin Yu
如果您正在使用ORM,ORM可能会提取所有二进制数据。例如,EF将需要您将Blob保留在单独的惰性加载表中,或者仅提取您需要的列(在EF中有点笨拙)。 - Dave Cousineau

9
如果您存储BLOB,最大的缺点是内存消耗。 您能想象从每个记录中选择一个45k图像会发生什么吗?
正如Mehrdad所说,也有优点。 因此,如果您决定采用这种方法,应尝试设计数据库,使大多数查询返回少量带有BLOB数据的结果。 例如,为此目的创建一对一关系。

+1 好观点 - 或许将 blob 存储在单独的表中,通过 id 进行提取是一个不错的选择。 - paul
说实话,我一直害怕使用BLOB,因为我不擅长SQL。但是如果必须使用,我可能会为每个BLOB创建一个单独的一对一关系。基本上像使用文件引用一样使用它。除了这些将存储在数据库中。注意:请勿在Web应用程序中执行此操作。 - Vasil
9
在我看来,这不是一个有力的论点。除非你需要在应用程序中使用表的每个列,否则在大多数情况下,执行“select * from x”都是一个坏主意。将Blob存储在单独的表中甚至更糟,因为它将需要连接并使请求更加复杂。 - Arseni Mourzenko
@Arseni Mourzenko:在我看来,将BLOBS放在单独的1对1表中与常规情况相比并没有显著地“使请求复杂化”。大多数非平凡的DB(即使没有BLOBS)都有许多子表需要连接,即使是简单的CRUD操作也需要这些表,更不用说这些表通常是1对多的。此外,对于不涉及BLOBS的请求(这可能是相当常见的,例如仅获取搜索结果网格的元数据),将BLOBS放在单独的表中可以显著地减少DB服务器必须读取的数据页数。 - Tom

7

从原则上来看,关系型数据库主要用于存储结构化数据。如果您无法在数据元素上进行查询条件或连接,则该数据元素可能不适用于数据库。我认为图像BLOB不应用于WHERE子句中,因此请将其保留在数据库之外。而CLOB则可以用于查询。


4
我们可能不会在WHERE子句中使用电话号码,因为很少通过电话号码搜索任何内容(除非您正在处理反向查找系统)。话虽如此,我们仍然将电话号码存储在数据库中,而不是外部文件中,即使它很少用作连接或过滤条件。我的意思是,这个原因不足以排除在关系型数据库中保存图像的可能性。 - Seb
2
但是你可以在电话号码上进行查询条件,或者将其用于连接,这是使用BLOB列无法合理完成的。 - Nils Weinander

7
我熟悉一个相当大的开源项目,该项目在初始阶段决定将图像存储在MySQL数据库中,但自那以后,这被证明是他们一直在应对的前三个坏主意之一。(加剧了“无情重构”的事实,但这是另外一个故事。)
造成的严重问题包括:
1. 超出最大有效数据库大小(mysql)。 (图像所需的总空间至少比其他所有空间多两个数量级)。 2. 图像文件失去了“文件性”。 没有日期大小等信息,除非将其作为日期(需要管理代码)进行冗余存储。 3. 任意字节序列并非始终易于处理,无论是用于存储还是操作。 4. “我们永远不需要从外部访问图像”是一个危险的假设。 5. 脆弱性。 因为整个安排是不自然和敏感的,并且您不知道它将在哪里咬您(有助于反重构心态)。
好处?我想不到任何好处,除非当时可能是最省力的路径。

我假设你的错误决定是存储二进制大对象(BLOB)。是这样吗? - paul
2
一个重要的优点是数据的一致性:使用适当的键,“文件”无法在没有元数据的情况下被删除,反之亦然。对于磁盘文件,不存在这样的限制,添加/删除文件及其元数据是必须设计、实施和使用的单独应用程序(或函数)。 - NVRAM
是的,您仍然需要编写一个具有适当验证的应用程序 - 但无论如何都是如此。我不会称这种差异为“实质性”。真正实质性的是,当图像仅可通过数据库调用获得时,使用其他应用程序和实用程序获取图像所需的所有额外工作量,而大多数图像处理软件并不带有数据库持久性。因此,仅查看图像就需要使用一个应用程序提取它,使用另一个应用程序查看它,然后确保在完成后将其放回正确的位置。还有,别想在资源管理器中浏览图像。 - dkretz

5
我认为这取决于您正在构建的应用程序。如果您正在构建CMS系统,并且数据的使用将在Web浏览器中显示图像,那么将图像保存到磁盘而不是放入数据库可能是有意义的。尽管老实说,我会两者都做,这样可以在不必复制文件的情况下添加服务器到农场中。
另一个用例可能是复杂对象,例如工作流程,甚至是具有许多相互依赖性的业务对象。您可以将它们序列化为二进制或基于文本的格式,并将它们保存在数据库中。然后您就可以获得DB的好处:原子操作、备份等等...
我认为人们不应该首先使用select *查询。您需要提供两种获取数据的方法,一种方法返回摘要信息,第二种方法返回blob。我无法想象为什么您需要一次返回成千上万张图片。

对于这个想法点赞。关于 select * from 的部分,你不一定要手动编写该查询。一些 ORM 默认使用这种类型的查询,因此如果有人不小心……痛苦啊。 - Vasil
嘿,你知道哪个ORM使用这些查询吗?我想远离它们。我知道nHibernate不会使用它们。 - JoshBerke
我在一些PHP框架中看到过,但是我记不清了。但既然它们在Web应用程序中,他们可能认为select *比select foo、bar、sausage少传输数据。我敢打赌他们从来没有考虑过BLOBs。 - Vasil
关于 select * 的脆弱性... - JoshBerke
从 PHP 的角度来看,它看起来并不是很脆弱 :). - Vasil
如果你的代码通过索引而不是名称访问列,那么它会变得很脆弱。 如果你在第2个索引位置添加一个列,所有新列之后的列都将具有不同的索引,情况可能会变得混乱。 - ZombieSheep

3

那些想将图像(或其他二进制文档)存储在数据库中的人,我并不太满意。数据库是用于存储[大多数情况下?]可索引的离散数据,而不是毫无意义的二进制数据块(BLOB)。如果你亲自使用过二进制数据块(BLOBs),你已经知道这一点。

你应该在文件系统中存储文件的引用。最佳实践是使用文件名,而不是绝对(甚至相对)路径。


就“SELECT *”而言,在大多数情况下我认为它是合理的。我构建了一个ORM,它在各个地方都使用它,但你可以覆盖它。如果你真的关心性能,你可以完全绕过ORM,使用ORM在后台使用的查询构建器。重点是这个对话与“SELECT *…”无关。它与良好的数据库设计有关。 - Ryan Williams
如果只存储了文件名而没有路径,您如何检索文件?您会有一个放置所有文件的文件夹吗?如果我的数据库中有数百万个文件怎么办? - Vincnetas
在应用程序的某个配置中,您应该存储文件存储目录的路径。如果您担心在同一目录中有太多文件,则可以动态构建路径。通常,您可以使用ID来实现此目的,例如/path/to/files/{ID here}/filename.ext。您只需要存储文件名即可。 - Ryan Williams

2

难道这不是LOB或CLOB等设计目的吗?

我们使用CLOB来存储一个大型航空系统信用卡交易的加密信息。

然而,内存消耗是最大的罪魁祸首。

希望对你有帮助。

祝好!


2
我们在系统中存储附件,无法更改附件,因此我认为我们对“将被存储且永远不会更改”的数据有相同的理解。我们特别决定不将其存储在数据库中。我们出于两个原因做出了这样的决定,即简单性和备份/恢复时间。
首先是简单性:在我们的情况下,这些附件是从最终用户的浏览器上传的,将它们直接写入目录(在DB服务器上)比通过SQL管道流式传输它们更简单。数据库中有它们的记录,但是数据库仅包含有关附件的元信息以及磁盘上文件的名称(在我们的情况下是GUID)。
备份/恢复方面:这些大型二进制对象(BLOB)可能成为数据库的最大部分之一。每当您运行完整备份时,您都会一遍又一遍地复制这些位,即使您知道它们永远不会更改。对我们来说,只需拥有(更小的)备份,然后将附件目录复制到辅助服务器作为备份即可。

1

一些数据库(例如PostgreSQL)会自动压缩字段,这样在直接从数据库中读取时可能更快。而且,程序可以一次性读取所有的字段和图像。


1
是的,如果我曾经使用二进制大对象,那么它将是PostgreSQL。这样可以节省带宽。但是数据在某个时刻必须以未压缩的形式传输到应用程序的进程中。 - Vasil
3
许多的文件块(包括图片、mp3等)本质上已经进行了预压缩。 - dkretz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接