【问题标题】:Storing trillions of document similarities存储数万亿的文档相似性
【发布时间】:2014-04-08 08:53:19
【问题描述】:

我编写了一个程序来计算一组 200 万个文档之间的相似性。该程序有效,但我无法存储结果。我不需要经常访问结果,但偶尔需要查询它们并提取子集进行分析。输出基本上是这样的:

1,2,0.35
1,3,0.42
1,4,0.99
1,5,0.04
1,6,0.45
1,7,0.38
1,8,0.22
1,9,0.76
.
.
.

第 1 列和第 2 列是文档 ID,第 3 列是相似度分数。由于相似度分数是对称的,我不需要全部计算它们,但这仍然给我留下 2000000*(2000000-1)/2 ≈ 2,000,000,000,000 行记录。

包含 100 万行记录的文本文件已经是 9MB。推断,这意味着我需要 17 TB 来存储这样的结果(在纯文本文件中)。

有没有更有效的方法来存储这些类型的数据?我可以为每个文档设置一行,并去掉第一列中重复的文档 ID。但这只会到此为止。文件格式或特殊的数据库系统呢?这一定是“大数据”中的通病;我看过报告类似分析的论文/博客,但没有人讨论存储等实际维度。

【问题讨论】:

  • 您对与其他人更相似的文档感兴趣吗?您能否仅通过保存相似度得分高于 0.50(或类似的值)的记录来获得相同的结果?换句话说,有没有另一种看待问题的方法?
  • 我已经尝试了一些类似的技巧来减少列表,它有点帮助。但我仍然对如何存储这样的大量数据这一更普遍的问题感兴趣,尤其是当它具有非常简单的结构时。
  • 压缩可能会在所需空间方面有所帮助。取决于您使用的数据库。这涵盖了 Oracle dbaora.com/table-compression-in-oracle-11g 的一些基本语法因为您将比较结果存储在数据库中,并且没有以任何方式使用数据库进行比较,所以我真的看不出有任何方法可以存储所有它。压缩至少会减少数据占用的空间。
  • 我认为你可以使用 unsigned short (2B) 而不是 double (8B) 作为相似部分,因为它在 0.0 和 1.0 之间,我认为你使用 0.01 精度,将它们定义在 0 和100.
  • 真的需要存储这么多信息吗?实际上,您甚至可以在太阳去世(或小熊队赢得世界大赛)之前比较所有这些文件吗?存储有用的信息,而不是即时比较两个感兴趣的文档吗?有时,人们开始着手这些项目,并完全沉浸在技术细节中,而不看大局。也许您可以为每个文件存储一行“摘要”,并使用它来决定对哪些进行详细比较。

标签: sql performance csv bigdata


【解决方案1】:

免责声明:我没有任何实践经验,但这是一个有趣的练习,经过一番思考,这就是我想出的:

由于您有 2.000.000 个文档,因此您有点卡住文档 ID 的整数;这使得 4 个字节 + 4 个字节;比较似乎在 0.00 和 1.00 之间,我猜一个字节可以通过将 0.00-1.00 编码为 0..100 来实现。

所以你的表是:id1、id2、relationship_value

这使得每条记录正好有 9 个字节。因此(没有任何开销)需要 ((2 * 10^6)^2)*9/2bytes,大约 17Tb。

当然,前提是您只有一张基本桌子。由于您不打算经常查询它,我猜性能并不是什么大问题。因此,您可以通过“水平”存储值来实现“创造性”。 为简化起见,您可以将值存储在 200 万乘 200 万的正方形中,每个“交点”都是一个字节,表示它们的坐标之间的关系。这将“仅”需要大约 3.6Tb,但维护起来会很痛苦,而且它也没有利用关系是对称的这一事实。

所以我建议使用混合方法,即包含 2 列的表。第一列将保存“左”文档 ID(4 个字节),第二列将保存所有文档值的字符串,该字符串以使用 varbinary 的第一列中的 id 上方的 id 开头。由于 varbinary 只占用它需要的空间,这有助于我们赢回由关系对称性提供的一些空间。

换句话说,

  • 记录 1 将具有 (2.000.000-1) 个字节的字符串作为第二列的值
  • 记录 2 将具有 (2.000.000-2) 个字节的字符串作为第二列的值
  • 记录 3 将具有 (2.000.000-3) 个字节的字符串作为第二列的值

这样,您应该能够避免使用 2Tb(包括开销)来存储信息。对其添加压缩,我很确定您可以将其存储在现代磁盘上。

当然,系统远非最佳。事实上,查询信息需要一些耐心,因为您无法处理基于集合的事物,并且您几乎必须逐字节扫描事物。这种方法的一个很好的“好处”是,您可以通过在每个记录的字符串中添加一个新字节 + 最后添加 1 个额外记录来轻松添加新文档。这样的操作成本很高,因为它会导致页面拆分;但至少可以不必完全重写表格。但随着时间的推移,它会导致相当多的碎片,您可能希望不时重建表以使其再次更加“对齐”。啊.. 技术细节。

选择和更新需要创造性地使用 SubString() 操作,但不要太复杂..

PS:严格来说,对于 0..100,您只需要 7 个字节,所以如果您真的想从中挤出最后一位,您实际上可以在 7 个字节中存储 8 个值并节省另一个 ca 300Mb,但它会让事情变得更加复杂......再说一次,数据无论如何都不会是人类可读的=)

PS:这种思路完全是为了减少所需的空间量,同时在更新数据方面保持实用性。我并不是说它会很快。事实上,如果您要搜索所有关系值为 0.89 或更高的文档,系统将不得不扫描整个表,即使使用现代磁盘也需要一段时间.

请注意,所有这些都是半小时头脑风暴的结果;我实际上希望有人可以用更简洁的方法加入进来=)

【讨论】:

  • 一些简单的优化:如果一切都井井有条,没有间隙,您可以跳过第一个字段并将其作为文件名的一部分(例如,doc_1.txt)。比较值可能只有两个字节(“76”而不是“0.76”)。如果有些是 1.00,可能是 3。您甚至可以将 0..100 存储在单个 unsigned int char 中。将第二个文档编号保存为 3 或 4 字节的二进制文件可以节省空间。这必须是人类可读的吗?如果没有,您可能会将其压缩到每条记录 4 个字节,固定长度记录(无 \n)。
  • 我想出了一个类似的矩阵方法,row# 是文档 1,col# 是文档 2,每个条目 7 位 = 大约 1.75TB。如果没有更新,我可能会使用带有自定义提取/创建例程的平面文件方法。我根本没有看过它,但在思考这个问题时遇到了它,HDF 可能会很有趣。让我们知道您的决定。
  • @PhilPerry:将信息存储在“每个文档一个文件中”确实也是一种选择。这样,您在添加新信息时使用文件系统进行“增长/分页”。请记住,您将失去 db 提供的 ACID 等好处;但我想如果你可以编写一个程序来创建 2M 文档之间的相似性,那么编码这也不会太难。使用例如NTFS 你甚至可以打开文件压缩,而不必担心它。我更喜欢将它存储在 MSSQL 数据库中并使用 SQLCLR 来查询它,但“只是文件”确实听起来很吻。
猜你喜欢
  • 1970-01-01
  • 2020-09-02
  • 1970-01-01
  • 2014-06-09
  • 2020-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
相关资源
最近更新 更多