java - Lucene 4.x性能问题

标签 java performance lucene

在过去的几周中,我一直在努力将应用程序从Lucene 3.x升级到Lucene 4.x,以期提高性能。不幸的是,在经历了完整的迁移过程并进行了在线和文档中发现的各种调整之后,Lucene 4的运行速度明显慢于Lucene 3(〜50%)。在这一点上,我几乎没有任何想法,并且想知道是否有人对如何加快速度提出了任何建议。我什至不希望在3.x上有更大的改进。我很高兴能与之匹配并保持最新版本的Lucene。

<编辑>

为了确认没有任何标准迁移更改对性能造成负面影响,我将Lucene 4.x版本移植回了Lucene 3.6.2,并保留了较新的API,而不是使用自定义的ParallelMultiSearcher和其他不推荐使用的方法/类。

3.6.2中的性能比以前更快:

  • 旧应用程序(Lucene 3.6.0)-约5700个请求/分钟
  • 使用新的API更新了应用程序,并进行了一些次要的优化(Lucene 4.4.0)-约2900个请求/分钟
  • 移植回新版本的应用程序,但保留了优化和更新的IndexSearcher/etc API(Lucene 3.6.2)-约6200个请求/分钟

  • 由于更新的Lucene API的优化和使用实际上提高了3.6.2的性能,因此,除了Lucene之外,这都不是问题。我只是不知道我还可以在程序中进行哪些更改以修复它。



    应用信息
  • 我们有一个分为20个分片的索引-这在Lucene 3.x和Lucene 4.x中都提供了最佳性能。
  • 该索引当前包含约1.5亿个文档,所有文档都非常简单且经过高度规范化,因此存在大量重复标记。仅存储一个字段(一个ID)-其他字段不可检索。
  • 我们有一组固定的相对简单的查询,这些查询填充有用户输入并执行-它们由多个BooleanQueries,TermQueries和TermRangeQueries组成。其中一些是嵌套的,但现在只有一个级别。
  • 我们不会对结果做任何高级处理-我们只是获取分数和存储的ID字段
  • 我们正在使用MmapDirectories指向tmpfs中的索引文件。我们玩过useUnmap“hack”,因为我们不经常打开新目录,并且从
  • 得到了很好的提升
  • 我们对所有查询使用单个IndexSearcher
  • 我们的测试机具有94GB的RAM和64个逻辑内核

  • 一般处理

    1)套接字监听器收到的请求

    2)最多生成4个查询对象,并使用规范化的用户输入填充(查询的所有必需输入都必须存在,否则将不执行)

    3)使用Fork/Join框架并行执行查询
  • 使用IndexSearcher w/ExecutorService
  • 并行执行每个分片的子查询

    4)聚合和其他简单的后处理

    其他相关信息
  • 为4.x系统重新创建了索引,但是数据是相同的。我们尝试了普通的Lucene42编解码器以及不使用压缩的扩展编解码器(根据网络上的建议)
  • 在3.x中,我们使用了ParallelMultisearcher的修改版本,在4.x中,我们将IndexSearcher与ExecutorService一起使用,并将所有阅读器合并在MultiReader中
  • 在3.x中,我们使用ThreadPoolExecutor代替了Fork/Join(在我的测试中,Fork/Join表现更好)

    4.x热点

    方法自我时间(%)|自拍时间(毫秒)|自拍时间(以毫秒为单位的CPU)

    java.util.concurrent.CountDownLatch.await()| 11.29%| 140887.219 | 0.0 <-这只是等待实际工作完成的tcp线程-您可以忽略它
    org.apache.lucene.codecs.lucene41.Lucene41PostingsReader $ BlockDocsEnum。()| 9.74%| 21594.03 | 121594
    org.apache.lucene.codecs.BlockTreeTerReader $ FieldReader $ SegmentTermsEnum $ Frame。()| 9.59%| 119680.956 | 119680
    org.apache.lucene.codecs.lucene41.ForUtil.readBlock()| 6.91%| 86208.621 | 86208
    org.apache.lucene.search.DisjunctionScorer.heapAdjust()| 6.68%| 83332.525 | 83332
    java.util.concurrent.ExecutorCompletionService.take()| 5.29%| 66081.499 | 6153
    org.apache.lucene.search.DisjunctionSucorer.afterNext()| 4.93%| 61560.872 | 61560
    org.apache.lucene.search.Tercorer.advance()| 4.53%| 56530.752 | 56530
    java.nio.DirectByteBuffer.get()| 3.96%| 49470.349 | 49470
    org.apache.lucene.codecs.BlockTreeTerReader $ FieldReader $ SegmentTerEnum。()| 2.97%| 37051.644 | 37051
    org.apache.lucene.codecs.BlockTreeTerReader $ FieldReader $ SegmentTerEnum.getFrame()| 2.77%| 34576.54 | 34576
    org.apache.lucene.codecs.MultiLevelSkipListReader.skipTo()| 2.47%| 30767.711 | 30767
    org.apache.lucene.codecs.lucene41.Lucene41PostingsReader.newTertate()| 2.23%| 27782.522 | 27782
    java.net.ServerSocket.accept()| 2.19%| 27380.696 | 0.0
    org.apache.lucene.search.DisjunctionSucorer.advance()| 1.82%| 22775.325 | 22775
    org.apache.lucene.search.HitQueue.getSentinelObject()| 1.59%| 19869.871 | 19869
    org.apache.lucene.store.ByteBufferIndexInput.buildSlice()| 1.43%| 17861.148 | 17861
    org.apache.lucene.codecs.BlockTreeTerReader $ FieldReader $ SegmentTerEnum.getArc()| 1.35%| 16813.927 | 16813
    org.apache.lucene.search.DisjunctionSucorer.countMatches()| 1.25%| 15603.283 | 15603
    org.apache.lucene.codecs.lucene41.Lucene41PostingsReader $ BlockDocsEnum.refillDocs()| 1.12%| 13929.646 | 13929
    java.util.concurrent.locks.ReentrantLock.lock()| 1.05%| 13145.631 | 8618
    org.apache.lucene.util.PriorityQueue.downHeap()| 1.00%| 12513.406 | 12513
    java.util.TreeMap.get()| 0.89%| 11070.192 | 11070
    org.apache.lucene.codecs.lucene41.Lucene41PostingsReader.docs()| 0.80%| 10026.117 | 10026
    org.apache.lucene.codecs.BlockTreeTerReader $ FieldReader $ SegmentTerEnum $ Frame.decodeMetaData()| 0.62%| 7746.05 | 7746
    org.apache.lucene.codecs.BlockTreeTerReader $ FieldReader.iterator()| 0.60%| 7482.395 | 7482
    org.apache.lucene.codecs.BlockTreeTerReader $ FieldReader $ SegmentTerEnum.seekExact()| 0.55%| 6863.069 | 6863
    org.apache.lucene.store.DataInput.clone()| 0.54%| 6721.357 | 6721
    java.nio.DirectByteBufferR.duplicate()| 0.48%| 5930.226 | 5930
    org.apache.lucene.util.fst.ByteSequenceOutputs.read()| 0.46%| 5708.354 | 5708
    org.apache.lucene.util.fst.FST.findTargetArc()| 0.45%| 5601.63 | 5601
    org.apache.lucene.codecs.lucene41.Lucene41PostingsReader.readTermsBlock()| 0.45%| 5567.914 | 5567
    org.apache.lucene.store.ByteBufferIndexInput.toString()| 0.39%| 4889.302 | 4889
    org.apache.lucene.codecs.lucene41.Lucene41SkipReader。()| 0.33%| 4147.285 | 4147
    org.apache.lucene.search.TermQuery $ TermWeight.scorer()| 0.32%| 4045.912 | 4045
    org.apache.lucene.codecs.MultiLevelSkipListReader。()| 0.31%| 3890.399 | 3890
    org.apache.lucene.codecs.BlockTreeTermsReader $ FieldReader $ SegmentTermsEnum $ Frame.loadBlock()| 0.31%| 3886.194 | 3886

    如果您还有其他可能有用的信息,请告诉我。

  • 最佳答案

    对于关心或正在尝试执行类似操作(查询中的受控并行性)的任何人,我遇到的问题是IndexSearcher在每个分片的每个段中创建一个任务,而不是在每个分片中创建任务-我误读了Javadoc。

    我通过在分片上使用forceMerge(1)限制了额外线程的数量来解决了这个问题。在我的用例中,这没什么大不了的,因为我当前不使用NRT搜索,但是它仍然为更新+从属同步过程增加了不必要的复杂性,因此我正在研究避免forceMerge的方法。

    作为快速解决方案,我可能会扩展IndexSearcher并使其为每个阅读器而不是每个段产生一个线程,但是Lucene邮件列表中提出了“虚拟段”的概念。这将是一个更好的长期解决方案。

    如果您想查看更多信息,可以在这里关注lucene邮件列表线程:
    http://www.mail-archive.com/java-user@lucene.apache.org/msg42961.html

    关于java - Lucene 4.x性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18882071/

    相关文章:

    search - 我如何对solr/lucene分数进行归一化?

    java - Lucene - 在 writer.updateDocuments() 中使用 LongPoint Field 作为术语更新文档

    java - 为什么数组不能分配给Iterable?

    java - 虚拟 log4j 配置(用于测试)

    html - 将我的 CSS/JS/Sprite 嵌入我的单页 Web 应用程序会使其加载速度更快吗?

    image - javafx加载时旋转圆

    java - 需要按预期将长字符串值发送到 Excel

    java - 从 Activity 获取 fragment 内的数据

    c++ - 如何优化 C++ avr 代码

    java - 使用 Lucene IndexableField 在运行时出现 NoClassDefFoundError