在 mysql 数据库中存储大文件/二进制数据:啥时候可以?

Posted

技术标签:

【中文标题】在 mysql 数据库中存储大文件/二进制数据:啥时候可以?【英文标题】:Storing large files / binary data in a mysql database: when is it ok?在 mysql 数据库中存储大文件/二进制数据:什么时候可以? 【发布时间】:2013-07-29 22:44:56 【问题描述】:

好的,我已经对此进行了搜索,并阅读了一些关于在 [mysql] 数据库中存储二进制数据的观点。一般来说,我认为这是一个坏主意并尽量避免它,支持传统的文件传输并仅将文件的引用存储在数据库中。

但是,我正在开展一个项目,该项目需要与远程/云数据库同步数据库,不仅用于文件,还用于设置和其他用户内容。由于这个和其他原因,我觉得这可能是数据库中二进制存储的合适情况。

我为数据库同步编写了一个通用系统,该系统使用反射和 XML 运行良好。我还(违背我的直觉)将文件存储集成到该系统中。同样,它运行良好 - 我将文件切成 64Kb BLOB 并将它们存储在一个表中,并带有一个 file_id 引用(链接到一个单独的表,其中包含文件名/大小/mime 类型等元数据)。

这使我能够在连接可用时发送点点滴滴,还允许我限制每个请求的大小以保持事情顺利进行。

到目前为止,我还没有发现任何问题,并且已经成功地在两个方向上导入和传输了超过 1gb 的数据(超过大约 10-15 个文件/16000 行),但我担心它的可扩展性 - 它会减慢一旦那里有 20GB 以上的数据,或者如果我的查询结构良好,MySQL 可以处理它吗?

我决定将数据存储在数据库中的另一个原因是,我认为如果空间不足,我可以简单地向 MySQL 添加另一个 HDD/存储设备,以实现有效的扩展/复制等。

我非常感谢任何关于这是一种好方法还是坏方法的观点或 cmet,我是否错过了在生产环境中使用后可能会看到的任何明显问题?

编辑:我忘了提,文件大小范围从 1KB 到 ~1GB

[粗略]结论 首先:非常感谢那些提供深思熟虑的答案的人。在这里选择可接受的答案非常困难,因为每个答案都可以提供一些不错的东西。

最后(尽管我抱有希望),我决定纯 MySQL 存储服务器充其量只是一个好的解决方案(我仍然不禁想知道为什么他们要麻烦包括 BLOB 类型)。

作为替代方案,我在@Nick Coons 文件系统方法和@tadman 建议使用轻量级键/值数据库引擎(如 leveldb)的混合方案之间纠结。如果在这个项目中使用 leveldb 的实用性不是问题,这很可能是我将努力的方法。

在此基础上我接受了tadman的回答;他的回答也最适用于我的情况。

话虽如此,对于那些感兴趣的人来说:到目前为止,我仅使用 MySQL 就取得了相当大的成功。我已经测试了一个存储超过 15gb 二进制数据的表,从大表中插入/检索数据(通过仔细查询)没有任何明显的负面影响。但是,我确信这仍然非常低效,并且提到的任何一种替代方法都会明显更好。

【问题讨论】:

如果您要投反对票,请您至少提供一个解释,以便我避免再次出现同样的错误? 【参考方案1】:

我不得不想知道,当您添加到块、存储、检索和重组的层在定义良好的文件系统结构上也能正常工作时,您为什么还要为数据库而烦恼。 MySQL 希望它的所有数据都放在一个 单个 卷上,因此不需要在任何时候添加另一个驱动器,而且复制大量二进制数据的速度会非常慢日志最终会复制您需要存储的数据量。

最简单的方法往往是最好的方法。将其直接存储在文件系统中可能是最好的方法。如果您需要保留存储位置的索引,也许您会使用 MySQL 之类的数据库,但是有很多方法可以完成同样的任务。技术含量越低越好。例如,不排除SQLite,因为嵌入式数据库在轻读写负载下表现非常好,并且在备份和恢复时具有“只是一个文件”的优势。

话虽如此,您正在做的事情听起来与LevelDB 非常相似,因此在您采用您的方法之前,您必须看看它与那种类型的键值文档存储有何显着不同。

【讨论】:

感谢您的回答,您提出了一些有趣的想法。该项目已经将 WAMP 环境用于各种其他任务,所以我想我不妨利用已经存在的东西。我还看了一下 LevelDB,它看起来很棒,但我认为它在这种情况下不太适合我的需求:“它没有关系数据模型,它不支持 SQL 查询,也不支持用于索引”。我不禁想到这会给我带来更多的问题。首先使用数据库的原因只是我已经为其他同步项设置了基础架构 你关于二进制日志的观点非常令人担忧,所以我想我会考虑转向一个“合适的”文件系统——你认为仍然使用数据库同步来传输是否可以,在另一端组装成二进制之前(然后从数据库中删除二进制)?使用数据库可以让我准确跟踪哪些数据块已收到和尚未收到(可能无法按顺序收到) 数据库非常擅长存储定义明确的关系数据。它们不擅长存储大量任意二进制数据。您可能使用的是混合的 LevelDB 数据存储,其顶部构建了 RDBMS 索引。 LevelDB 擅长存储大量数据,并且可以使用rsync 等标准工具进行复制。 嗨@tadman,感谢您的意见。我再次查看了 levelDB,发现我可以将 MySQL 用于关系端并将 levelDB 键/范围存储为参考值以同时使用两者。这是你想到的那种事情吗?不过,我确实有一个问题,我基本上需要复制我所做的任何事情:我需要一个用于 WAMP 环境中的桌面应用程序的 C# 系统,以及一个在 LAMP 环境(典型的 Web 主机)中的 php 等价物。原谅我的无知,但是 afaik,leveldb 在大多数网络主机上默认不可用? 您可以随时添加许多 PHP LevelDB 扩展,因此这应该不是问题。希望您没有使用某种残暴的共享主机,在这种情况下您对堆栈的控制为零。如果 LevelDB 无法正常工作,您始终可以手动管理文件,但这只是多得多的工作。 LevelDB 将在您添加和删除内容时分块、组织和修剪数据,内置的透明压缩是另一个好处。你可以自己写所有这些,但工作量很大。就像Amazon S3 作为一个嵌入式库。【参考方案2】:

简答:

我不确定是否有一种强硬的方式来回答这个问题。您提到文件从 1KB 到 1GB。如果二进制数据接近 1KB,我不会将二进制数据存储在数据库中,更不用说 1GB。如果是偶然的,我可能会在数据库中存储几个字节的二进制数据,但是任何大量数据,尤其是不需要搜索的数据,都应该存储在文件系统中:

当您将数据存储在数据库中时,无论如何您都是将其存储在文件系统中,您只是在混合中添加了另一层(数据库)。这一层是有成本的,所以应该有一个好处来弥补差额。如果您要存储数据以便可以基于它进行搜索或将其连接到其他数据,那么这是有道理的。但文件数据,无论是否为二进制,通常不会以这种方式使用。

示例实现:

有比将文件数据输入数据库更好的方法来分发文件数据,例如分布式文件系统(检查 GlusterFS、MooseFS,这两种方法都可以通过简单地添加额外的硬盘驱动器来扩展,而 MySQL 不会)。

通常,我将使用数据的 SHA1 哈希作为文件名将文件数据存储在文件系统中。如果hash是98a75af529f07b1ef7be7400f51344b9f07b1ef7,那么我就存放在这个目录结构中:

./98/a7/98a75af529f07b1ef7be7400f51344b9f07b1ef7

即由前两个字符组成的***目录,由后两个字符组成的二级目录,最后是总字符串名称的文件。通过这种方式,我实际上可以拥有数十亿个文件,而不会在一个目录中存放太多文件,以至于系统运行速度太慢。

然后我创建一个包含这些列的数据库表来保存元数据:

file_id,一个自动增量字段 已创建,默认值为 current_timestamp 的字段 prev_id,更多内容如下 hash,文件系统上的 SHA1 哈希 name,文件的文本名称(例如文件在磁盘上的原始名称。

当我需要分层目录结构时,我还会创建一个目录表并将 dir_id 添加到上面的列列表中。

如果我编辑./98/a7/98a75af529f07b1ef7be7400f51344b9f07b1ef7 表示的文件,我实际上并没有更改磁盘上的那个文件,而是创建一个新文件(因为新文件内容将由一个新的 SHA1 哈希表示),然后创建一个新的文件表中的条目,其中 prev_id 等于我编辑的文件的 file_id。换句话说,我现在有了版本控制。

如果我需要以分布式方式使用它,我会设置 MySQL 复制,然后使用 GlusterFS 在多个服务器之间复制文件系统。

【讨论】:

非常感谢,这是一个很好的答案。我特别喜欢你建议的文件存储方法。使用您提到的分布式文件系统也很好,但不幸的是我们没有使用专用服务器(至少目前是这样)。我需要尝试使其与典型的共享网络托管设置尽可能兼容,这当然使得使用任何需要在服务器上安装的东西变得困难。但再次感谢您提供优雅的文件系统方法:)【参考方案3】:

我想你会发现很多关于这个问题的争论,就像我开始研究这个问题时所做的那样。我倾向于存储在文件系统中并维护参考。但是,这并不是说永远没有时间将二进制数据存储在数据库中。

我要说的是,仅仅为了让事物保持同步本身并不是为将二进制数据存储在数据库中而争论不休的理由。当然有一些方法可以使文件系统保持同步,以便数据库保持同步,文件系统也保持同步。

最重要的是,关于这个话题有相当多的争论,你必须选择对你有用的东西。如果您设置的内容有效。用它。进行性能和负载测试以确保其正常工作。如果坚持不下来,就换吧。

【讨论】:

你是对的 - 我发现了很多关于这个的争论.. 但是,这就是我问的原因:) 自发布以来,我一直在做更多的测试,现在有一个包含大约 15gb 的表数据,一些文件大至 1.2gb,到目前为止一切似乎都很好:) 但是,尽管我很想不理会它,但其他答案中提出的观点让我有一种不好的感觉。另外,关于你的第二段,我选择这个只是因为我已经有一个强大的序列化/同步引擎,我可以很容易地添加对象:所以,我想是懒惰和希望的混合:)

以上是关于在 mysql 数据库中存储大文件/二进制数据:啥时候可以?的主要内容,如果未能解决你的问题,请参考以下文章

mysql中的longblob和longtext有啥区别?

mysql bolb对应java啥类型

mysql blob

JDBC处理mysql大数据

oracle 某个表的字段里面的值是<clob>是啥意思

MySQL中的BLOB类型