高性能的独特文档ID检索

时间:2014-07-21 09:43:02

标签: lucene

目前,我正在使用Java平台上的Lucene 4.9.0开发高性能NRT系统,该系统检测近乎重复的文本文档。

为此,我查询Lucene返回一些匹配候选项并在本地进行近似重复计算(通过检索和缓存术语向量)。但我主要担心的是将Lucene的docId(可以改变)绑定到我自己的索引中存储的唯一且不可变的文档id的性能问题。

我的流程如下:

  • 查询Lucene中的文件
  • 每个文件:
    • 根据Lucene docId
    • 获取我的唯一文档ID
    • 从缓存中为我的文档ID获取术语向量(如果它不存在 - 从Lucene获取并填充缓存)
    • 做数学......

我的主要瓶颈是“获取我独特的文档ID”步骤,这会导致巨大的性能下降(特别是有时我必须在单循环中进行计算,比如40000个术语向量)。 / p>

    try {
        Document document = indexReader.document(id);
        return document.getField(ID_FIELD_NAME).numericValue().intValue();
    } catch (IOException e) {
        throw new IndexException(e);
    }

我正在考虑的可能解决方案是:

还有其他建议吗?

1 个答案:

答案 0 :(得分:0)

我已经找到了如何使用Lucene的AtomicReader的优势部分解决问题。为此,我使用全局缓存来保持已经实例化的段'FieldCache。

Map<Object, FieldCache.Ints> fieldCacheMap = new HashMap<Object, FieldCache.Ints>();

在我的方法中,我使用以下代码:

Query query = new TermQuery(new Term(FIELD_NAME, fieldValue));
IndexReader indexReader = DirectoryReader.open(indexWriter, true);

List<AtomicReaderContext> leaves = indexReader.getContext().leaves();

// process each segment separately
for (AtomicReaderContext leave : leaves) {
    AtomicReader reader = leave.reader();

    FieldCache.Ints fieldCache;
    Object fieldCacheKey = reader.getCoreCacheKey();

    synchronized (fieldCacheMap) {
        fieldCache = fieldCacheMap.get(fieldCacheKey);
        if (fieldCache == null) {
            fieldCache = FieldCache.DEFAULT.getInts(reader, ID_FIELD_NAME, true);
            fieldCacheMap.put(fieldCacheKey, fieldCache);
        }
        usedReaderSet.add(fieldCacheKey);
    }

    IndexSearcher searcher = new IndexSearcher(reader);
    TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE);

    ScoreDoc[] scoreDocs = topDocs.scoreDocs;

    for (int i = 0; i < scoreDocs.length; i++) {
         int docID = scoreDocs[i].doc;
         int offerId = fieldCache.get(docID);
         // do your processing here
    }
}

// remove unused entries in cache set
synchronized(fieldCacheMap) {
    Set<Object> inCacheSet = fieldCacheMap.keySet();
    Set<Object> toRemove = new HashSet();
    for(Object inCache : inCacheSet) {
        if(!usedReaderSet.contains(inCache)) {
            toRemove.add(inCache);
        }
    }

    for(Object subject : toRemove) {
         fieldCacheMap.remove(subject);
    }

}

indexReader.close();

它运作得非常快。我主要担心的是使用大索引时内存使用率可能非常高。