7

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

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

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

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

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

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

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

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

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

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

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

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

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

4

3 回答 3

3

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

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

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

于 2013-07-30T01:21:20.687 回答
3

简短的回答:

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

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

示例实现:

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

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

./98/a7/98a75af529f07b1ef7be7400f51344b9f07b1ef7

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

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

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

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

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

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

于 2013-07-30T07:40:55.613 回答
2

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

我会说,仅仅保持同步并不是在数据库中存储二进制数据的理由。当然有一些方法可以使文件系统保持同步,以便数据库保持同步,文件系统也保持同步。

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

于 2013-07-30T01:49:55.457 回答