存储数百万个数组并执行 IN 检查的有效方法
Posted
技术标签:
【中文标题】存储数百万个数组并执行 IN 检查的有效方法【英文标题】:Efficient way to store millions of arrays, and perform IN check 【发布时间】:2014-03-18 06:16:48 【问题描述】:大约有 3 百万个数组 - 或 Python 列表\元组(并不重要)。每个数组由以下元素组成:
['string1', 'string2', 'string3', ...] # totally, 10000 elements
这些数组应该存储在某种键值存储中。现在假设它是一个 Python 的 dict,为了简单的解释。
所以,300 万个键,每个键代表一个 10000 个元素的数组。
Lists\tuples 或任何其他自定义的东西 - 这并不重要。重要的是数组应该包含字符串 - utf8 或 unicode 字符串,每个字符串从 5 到大约 50 个字符。还有大约 300 万个可能的字符串。如果确实需要,可以将它们替换为整数,但为了更有效的进一步操作,我更喜欢使用字符串。
虽然很难为您提供数据的完整描述(它既复杂又奇怪),它类似于同义词 - 假设我们有 300 万个单词 - 作为 dict 键 - 每个单词都有 10k 个同义词- 或列表的元素。
这样(不是真正的同义词,但它会给你的想法):
'computer': ['pc', 'mac', 'laptop', ...], # (10k totally)
'house': ['building', 'hut', 'inn', ...], # (another 10k)
...
元素 - “同义词” - 如果需要,可以排序。
稍后,在填充完数组后,会出现一个循环:我们遍历所有键并检查其值中是否包含某个 var。例如,用户输入单词“computer”和“laptop”——如果单词“laptop”是单词“computer”的同义词,我们必须快速回复。这里的问题是我们必须检查它数百万次,可能是 2000 万次左右。想象一下,我们有很多用户输入一些随机词——“计算机”和“汽车”、“电话”和“建筑”等等。它们可能“匹配”,也可能不匹配'匹配'。
所以,简而言之 - 我需要的是:
高效存储这些数据结构, 能够快速检查某个项目是否在数组中。我应该能够将内存使用量保持在 30GB 以下。此外,我应该能够在 Xeon CPU 上在不到 10 小时内执行所有迭代。
有大约 0.1% 的错误答案是可以的——无论是正面的还是负面的——尽管最好减少它们或根本没有它们。
这里最好的方法是什么?算法,代码链接,任何东西都非常感谢。另外 - 我的一个朋友建议使用布隆过滤器或玛丽莎在这里尝试 - 他是对的吗?我没有和他们一起工作。
【问题讨论】:
您需要将它们保存在 RAM 中吗?我会研究类似 HDF5 文件的东西,它们是专为这种用途而设计的。 (好吧,HDF5 不适合你,因为它适用于数字数据,但你明白了) @RicardoCárdenes 很好,使用 HDD 的单节点集群运行速度太慢了。磁盘操作非常缓慢。我怀疑即使是 SDD 也会有很大帮助。此外,我不想使事情复杂化并创建一个多节点集群。如果可以避免这样做并将所有内容都保存在 RAM 中,那么它应该是这样的。 “10 小时内完成所有迭代”是什么意思?另外,“按30GB以下的内存使用量”,您是指整个数据结构吗?这会很困难,因为只存储字符串,不考虑映射到哪个映射,在不压缩的情况下将占用超过 100GB 的空间。 你在 NSA 工作吗? ;-) 【参考方案1】:我会将每个唯一字符串映射到一个数字 ID,然后将 bloom filter 与每个元素大约 20 位相关联,以实现
有alternatives,但它们只会将大小从1.44·n·ln2(1/ε)减小到n·ln2 (1/ε) 每个键,在您的情况下 ε=0.001 所以理论限制是每个键 99658 位或每个键 10 位的数据结构元素,即 298,974,000,000 位或 38 GB。
因此,对于具有您所需的性能和条目数的数据结构而言,30GB 低于理论限制,但在球场范围内。
【讨论】:
【参考方案2】:为什么要维护自己的内存中数据结构?为什么不为此目的使用常规数据库?如果这太慢了,为什么不使用内存数据库?一种解决方案是使用内存中的sqlite3
。检查此 SO 链接,例如:Fast relational Database for simple use with Python
您通过将':memory:'
传递给connect
方法来创建内存数据库。
import sqlite3
conn = sqlite3.connect(':memory:')
您的架构是什么?我可以想到一个宽模式,将字符串作为id
键(例如,您的示例中的“计算机”、“房屋”以及大约 10000 个附加列:“field1”到“field10000”;您的每个元素之一大批)。构建架构后,将数据迭代插入数据库将很简单:每行数据有一个 SQL 语句。根据您的描述,insert
部分是一次性。对数据库没有进一步的修改。
最大的问题是检索(更关键的是检索速度)。检索像computer
这样的单个键的整个数组也是一个简单的SQL 语句。可扩展性和速度是我不知道的东西,这是你必须试验的东西。内存数据库仍然希望加快检索部分。然而,我相信这是您可以实施和测试的最便宜和最快的解决方案(比多节点集群便宜得多)
我为什么建议这个解决方案?因为您想到的设置与快速增长的数据库支持的互联网初创公司的设置非常相似。所有优秀的初创公司每天都有相似数量的请求;使用某种带有缓存的数据库(如果一个简单的数据库不能扩展到数百万个请求,那么缓存将是寻找您的问题的下一步。同样,它更容易并且更便宜 em> 比购买 RAM/节点)。
【讨论】:
如果某些单词的同义词少于 10,000 个,这会浪费内存,这似乎很可能。 从他的描述看来,数据的结构似乎恰好有 10k 条记录。如果更少,细节将很重要。如果大量记录很大,我们仍然可以保持内存效率 好吧,谢谢你的想法。虽然我认为redis会更快,不是吗?它必须更快,并且必须具有更小的开销,只是因为它缺少 RDBMS 功能,例如过滤、排序、连接等。但是即使使用 Redis,它也不会非常快速和紧凑 - 需要找到一种方法来减少数组大小,因此布隆过滤器可能会有所帮助以上是关于存储数百万个数组并执行 IN 检查的有效方法的主要内容,如果未能解决你的问题,请参考以下文章