如何使用Lucene或类似的方法为超大型数据集构建一个类型的索引?

时间:2010-05-04 20:33:03

标签: autocomplete lucene indexing typeahead

我有一个2亿+记录的数据集,我正在寻找一个专用的后端来为先进的解决方案提供支持。 Lucene因其受欢迎程度和许可类型而受到关注,但我也对其他开源建议持开放态度。我正在寻找建议,来自战壕的故事,甚至更好的直接指导我将需要的硬件和软件结构的数量。要求:

必须:

  • 执行的能力从子串匹配开始(我输入'st'并且它应匹配'Stephen')
  • 能够非常快速地返回结果,我说500ms是一个上限。

很高兴:

  • 将相关性信息提供给索引过程的能力,例如,更流行的术语将先于其他术语返回,而不仅仅是按字母顺序排列,也称为Google风格。
  • 字内子字符串匹配,例如('st'匹配'畅销书')

注意:

  • 此索引将纯粹用于提前输入,不需要提供标准搜索查询。
  • 我不担心如何设置前端或AJAX,只要索引可以作为服务查询或直接通过Java代码查询。

投票支持任何有用的信息,使我能够更接近企业级别类型提前解决方案

3 个答案:

答案 0 :(得分:7)

如果每条记录相对较小(少于几个字),您可以尝试Trie数据结构:

http://en.wikipedia.org/wiki/Trie

它专为减轻快速前缀匹配而设计,相对空间有效。我已经将这个数据结构用于您正在寻找的确切自动完成功能,而且我知道其他人已经为大批量生产网站做过这样的事情。根据我的经验,您可以预期单个查询的响应时间为几十毫秒。

您可以很容易地自己实现Trie,或者可以下载实现。参见

Where do I find a standard Trie based map implementation in Java?

根据您使用的实现,使用相关性分数标记每个索引记录应该相对简单,然后您可以使用这些记录从查询中获取记录列表时进行排序。

答案 1 :(得分:4)

你可能不需要任何太花哨的东西。您的“必须”列表可以通过简单的数据库引擎(例如BerkeleyDB或ESENT)来满足。将所有单词放入表中,然后使用seek查找单词。

一个8kb页面的b树应至少获得250个字符串/页面,这将产生1M页面页面,从而得到高度为3的b树。即使使用5400 RPM笔记本电脑磁盘,I / O延迟也会减少超过15ms所以,在最坏的情况下,你将能够在最坏的情况下获得~50ms的结果(完全未缓存的数据和慢速驱动)。

(我构建了一个使用基于ESENT的PersistentDictionary类的预先输入应用程序。对于第一次查找,我得到了大约35毫秒的响应,其中数据根本没有缓存。在做了一堆查询后,响应时间下降到~5ms)。

要支持大量并发用户,您可以添加更多缓存或更快的磁盘。完全缓存所有数据可能是可能的(目前8GB的RAM是相当实惠的)并且先行数据肯定足够小以适应SSD,这将提供可笑的IOPS数量。我可能会选择SSD,因为即使缓存很冷(例如重启后),它也能提供出色的性能。

基于数据库引擎的解决方案应该非常快速地构建。

答案 2 :(得分:2)

以下是我们在SOLR中的表现:

搜索的关键是如何使用适当的过滤工厂获得正确的数据类型。

在架构中设置名为 textPrefix

的数据类型

示例:

<!--
 This type is used for type ahead style searching and starts with searching. 
-->
−
<fieldType name="textPrefix" class="solr.TextField" positionIncrementGap="1" sortMissingLast="true">
−
<analyzer type="index">
<tokenizer class="solr.KeywordTokenizerFactory"/>
<filter class="ISOLatin1AccentFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<!-- Remove non alpha-numeric characters -->
<filter class="solr.PatternReplaceFilterFactory" pattern="[^a-zA-Z0-9 ]" replacement="" replace="all"/>
<filter class="solr.TrimFilterFactory"/>
<!-- Remove leading "the "-->
<filter class="solr.PatternReplaceFilterFactory" pattern="^the\s" replacement="" replace="all"/>
<filter class="solr.TrimFilterFactory"/>
<filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="6"/>
</analyzer>
−
<analyzer type="query">
<tokenizer class="solr.KeywordTokenizerFactory"/>
<filter class="ISOLatin1AccentFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<!-- Remove non alpha-numeric characters -->
<filter class="solr.PatternReplaceFilterFactory" pattern="[^a-zA-Z0-9 ]" replacement="" replace="all"/>
<filter class="solr.TrimFilterFactory"/>
<!-- Remove leading "the "-->
<filter class="solr.PatternReplaceFilterFactory" pattern="^the\s" replacement="" replace="all"/>
<filter class="solr.TrimFilterFactory"/>
</analyzer>
</fieldType>

然后在您的架构文档中创建一个新的数据字段:

<field name="CustomerNamePrefix" type="textPrefix" indexed="true" stored="false"/>

然后将Customer Name的副本存储到此CustomerNamePrefix字段中。

现在,当您查询此字段时,您可以简单地使用名称的第一个字母,它将为您提供这些字母的最佳匹配。根据您的查询方式,您可以根据文档中的其他因素来提高结果。

示例:

http://solrserver:8080/solr/select/?q=CustomerNamePrefix:jame&q.alt=*:*&start=0&rows=10&mm=1