存储和访问大量相对较小的文件

Storing and accessing a large number of relatively small files

我正在 运行 进行大量非常缓慢的计算,但结果可重复使用(并且经常计算一些新的东西依赖于之前已经执行过的计算)。为了使用它们,我想将结果存储在某个地方(永久)。计算可以由两个标识符唯一标识:实验名称和计算名称,值是一个浮点数组(我目前将其存储为原始二进制数据)。它们需要经常通过实验和计算名称单独访问(读取和写入),有时也仅通过实验名称(即给定实验的所有计算及其结果)。它们有时也被串联起来,但如果读写速度很快,则不需要额外支持此操作。任何 Web 应用程序都不需要访问此数据(仅供需要计算结果的非生产脚本使用,但每次计算它们是不可行的),也不需要事务,但每次写入都需要是原子的(例如关闭计算机不应导致 corrupted/partial 数据)。读取也需要是原子的(例如,如果两个进程试图访问一个计算的结果,但它不在那里,所以其中一个开始保存新结果,另一个进程应该在完成时接收它,或者什么都不接收全部)。远程访问数据不是必需的,但很有帮助。

所以,TL;DR 要求:

目前我尝试过的解决方案是:

当然,在 sqlite 失败后,第一个想法就是转移到像 postgres 这样的 "proper" 数据库,但后来我意识到,也许在这种情况下,关系数据库并不是真正可行的方法(尤其是因为速度在这里很关键,我不需要他们的大部分功能) - 特别是 postgres 可能不是要走的路,因为最接近 blob 的是 bytea,它需要额外的转换(所以性能命中是有保证的).然而,在研究了一些关于键值数据库(这似乎适用于我的问题)之后,我发现我检查的所有数据库都不支持复合键,并且通常对键有长度限制(例如 couchbase 刚刚250 字节)。那么,我应该只使用普通的关系数据库,尝试使用 NoSQL 数据库之一,还是完全不同的数据库,例如 HDF5?

所以,我最终还是使用了关系数据库(因为只有在那里我才能使用复合键而不会受到任何攻击)。 我执行了一个基准测试来比较 sqlite 与 postgres 和 mysql - 500 000 次插入 ~60 KB blob,然后通过整个键进行 50 000 次选择。这不足以将 sqlite 减慢到我遇到的不可接受的水平,但设置了一个参考点(即 sqlite 的速度 运行 这几条记录对我来说是可以接受的)。我假设在使用 mysql 和 postgres 添加更多记录时我不会遇到巨大的性能损失(因为它们被设计用于处理比 sqlite 大得多的数据),并且当最终使用其中之一时,原来是真的。

设置(默认设置除外)如下:

  • sqlite:日志模式=wal(并行访问需要),隔离级别自动提交,值为BLOB
  • postgres:隔离级别自动提交(无法关闭事务,并且在一个巨大的事务中做所有事情对我来说不是一个选项),值为 BYTEA(遗憾的是包括我写的双重转换)
  • mysql: engine=aria, 事务禁用, 值为 MEDIUMBLOB

如您所见,我能够自定义 mysql 更多内容以适应手头的任务。下面的结果很好地反映了这一点:

                     sqlite     postgres        mysql
selects           90.816292   191.910514   106.363534
inserts         4367.483822  7227.473075  5081.281370

Mysql 与 sqlite 的速度相似,而 postgres 的速度要慢得多。

改进数据库解决方案的一种方法是外部化数据 blob。

您可以使用 SeaweedFS https://github.com/chrislusf/seaweedfs 作为对象存储,上传 blob 并获取文件 ID,然后将文件 ID 存储在数据库中。 (我正在研究 SeaweedFS)

这应该会大大减少数据库负载,并且查询会更快。