存储和访问大量相对较小的文件
Storing and accessing a large number of relatively small files
我正在 运行 进行大量非常缓慢的计算,但结果可重复使用(并且经常计算一些新的东西依赖于之前已经执行过的计算)。为了使用它们,我想将结果存储在某个地方(永久)。计算可以由两个标识符唯一标识:实验名称和计算名称,值是一个浮点数组(我目前将其存储为原始二进制数据)。它们需要经常通过实验和计算名称单独访问(读取和写入),有时也仅通过实验名称(即给定实验的所有计算及其结果)。它们有时也被串联起来,但如果读写速度很快,则不需要额外支持此操作。任何 Web 应用程序都不需要访问此数据(仅供需要计算结果的非生产脚本使用,但每次计算它们是不可行的),也不需要事务,但每次写入都需要是原子的(例如关闭计算机不应导致 corrupted/partial 数据)。读取也需要是原子的(例如,如果两个进程试图访问一个计算的结果,但它不在那里,所以其中一个开始保存新结果,另一个进程应该在完成时接收它,或者什么都不接收全部)。远程访问数据不是必需的,但很有帮助。
所以,TL;DR 要求:
- 二进制数据的永久存储(除了标识符不需要存储元数据)
- 基于复合标识符的非常快速的访问(read/write)
- 能够通过复合标识符的一部分读取所有数据
- 并发,原子read/write
- 无需交易、复杂查询等
- 远程访问会很好,但不是必需的
- 整个事情主要是为了节省时间,所以速度很关键
目前我尝试过的解决方案是:
- 将它们存储为单独的文件(每个实验一个目录,每个计算一个二进制文件)——需要手动处理原子性,而且大多数文件系统只支持最多 255 个字符长的文件名(计算名称可能更长)比那个),因此需要额外的映射;我也不确定 ext4(这是我正在使用的文件系统,无法更改它)是否设计用于处理数百万个文件
- 使用 sqlite 数据库(只有一个 table 和一个复合主键)- 起初它看起来很完美,但是当我们得到数百 GB 的数据(数百万 ~100 KB blob,和它们的数量和大小都会增加),它开始变得非常慢,即使在应用了在互联网上找到的优化后也是如此
当然,在 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)
这应该会大大减少数据库负载,并且查询会更快。
我正在 运行 进行大量非常缓慢的计算,但结果可重复使用(并且经常计算一些新的东西依赖于之前已经执行过的计算)。为了使用它们,我想将结果存储在某个地方(永久)。计算可以由两个标识符唯一标识:实验名称和计算名称,值是一个浮点数组(我目前将其存储为原始二进制数据)。它们需要经常通过实验和计算名称单独访问(读取和写入),有时也仅通过实验名称(即给定实验的所有计算及其结果)。它们有时也被串联起来,但如果读写速度很快,则不需要额外支持此操作。任何 Web 应用程序都不需要访问此数据(仅供需要计算结果的非生产脚本使用,但每次计算它们是不可行的),也不需要事务,但每次写入都需要是原子的(例如关闭计算机不应导致 corrupted/partial 数据)。读取也需要是原子的(例如,如果两个进程试图访问一个计算的结果,但它不在那里,所以其中一个开始保存新结果,另一个进程应该在完成时接收它,或者什么都不接收全部)。远程访问数据不是必需的,但很有帮助。
所以,TL;DR 要求:
- 二进制数据的永久存储(除了标识符不需要存储元数据)
- 基于复合标识符的非常快速的访问(read/write)
- 能够通过复合标识符的一部分读取所有数据
- 并发,原子read/write
- 无需交易、复杂查询等
- 远程访问会很好,但不是必需的
- 整个事情主要是为了节省时间,所以速度很关键
目前我尝试过的解决方案是:
- 将它们存储为单独的文件(每个实验一个目录,每个计算一个二进制文件)——需要手动处理原子性,而且大多数文件系统只支持最多 255 个字符长的文件名(计算名称可能更长)比那个),因此需要额外的映射;我也不确定 ext4(这是我正在使用的文件系统,无法更改它)是否设计用于处理数百万个文件
- 使用 sqlite 数据库(只有一个 table 和一个复合主键)- 起初它看起来很完美,但是当我们得到数百 GB 的数据(数百万 ~100 KB blob,和它们的数量和大小都会增加),它开始变得非常慢,即使在应用了在互联网上找到的优化后也是如此
当然,在 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)
这应该会大大减少数据库负载,并且查询会更快。