我有一个问题,我正在寻找一些指导来解决最有效的方法。我有2亿个数据字符串,大小从3个字符到70个字符不等。字符串由字母数字和几个特殊字符组成,如短划线和下划线。我需要能够快速搜索整个字符串或字符串中的任何子字符串(最小子字符串大小为3)。这里快速定义为不到1秒。
作为我的第一次切入,我做了以下事情:
创建了38个索引文件。索引包含以特定字母开头的所有子字符串。第一个4mb包含100万个散列桶(哈希链的开始)。索引的其余部分包含来自散列桶的链接列表链。我的散列分布非常均匀。 100万个散列桶保存在RAM中并镜像到磁盘。
当一个字符串被添加到索引时,它被分解为其非重复的(在其自身内)3-n字符子串(当n是字符串-1的长度时)。因此,例如,“apples”存储在“A”索引中,如pples,pple,ppl,pp(子串也存储在“L”和“P”索引中)。
搜索/添加服务器作为守护进程运行(在C ++中)并且像冠军一样运行。典型的搜索时间不到1/2秒。
问题出在流程的前端。我通常一次添加30,000个密钥。这部分过程需要永远。通过基准测试,将180,000个可变长度键的空索引的加载时间约为3 1/2小时。
除了非常长的加载时间外,此方案有效。
在我坚持优化(或尝试)之前,我想知道是否有更好的方法来解决这个问题。前面和后面的通配符搜索(即:DBMS中的'%ppl%'字符串非常慢(例如MySQL的小时数)对于这么大的数据集。所以看起来DBMS解决方案是不可能的。我不能使用全文搜索,因为我们不是处理普通单词,而是可能或可能不是由真实单词组成的字符串。
答案 0 :(得分:1)
根据您的描述,数据加载需要花费所有时间,因为您正在处理I / O,将膨胀的字符串镜像到硬盘。这肯定是一个瓶颈,主要取决于您读取和写入磁盘数据的方式。
使用mmap
和一些LRU策略可以实现执行时间的可能改进。我很确定复制数据的想法是让搜索更快,但是因为你正在使用 - 似乎只有一台机器,你的瓶颈会从内存搜索转向I / O请求。
另一个你可能不感兴趣的解决方案 - 它也很恶劣有趣和令人不安(: - ,是在多台机器之间分割数据。考虑到你构建数据的方式,实现本身可能花一点时间,但这将非常简单。你有:
hash_id(bucket) % num_machines
; 另一个好处是,正如你所说,数据是均匀分布的 - ALREADY \ o /;这通常是分布式实现中最挑剔的部分之一。此外,这将是高度可扩展的,因为您可以在数据增长时添加另一台计算机。
答案 1 :(得分:1)
不是一次性完成所有事情,而是在38次通过中解决问题。
阅读180,000个字符串中的每一个。在每个字符串中查找“A”,并仅将内容写入“A”哈希表。完成后,将“A”哈希表的完整结果写入磁盘。 (有足够的RAM将整个“A”哈希表存储在内存中 - 如果不这样做,请制作较小的哈希表。即,在起始字母对上有38 ^ 2个哈希表,并且有1444个不同的表。你可以甚至动态地改变哈希表被键入的字母数量取决于它们的前缀有多常见,因此它们都是适度的大小。跟踪这些前缀的长度并不昂贵。)
然后读取180,000个字符串中的每一个,寻找“B”。等
我的理论是,由于你的大规模桌子的缓存颠簸,你的速度会比你慢。
接下来可能有用的是限制字符串在哈希处理的时间,以缩小表的大小。
如果将散列的长度限制为10个字符,则不会执行长度为70的长度为3到70的所有2278个子串,而是只有508个子串长度为3到10.并且可能没有那么多长度大于10的字符串上的冲突。再次,哈希的长度可以是动态的 - 长度X哈希可能有一个标志“如果你的字符串长于X,请尝试长度X + Y哈希,这个太常见“,否则只是终止散列。这可能会减少表中的数据量,但在某些情况下会降低查找速度。