使用lucene改进multithreading索引

我试图在Lucene中使用多个线程构建索引。 所以,我开始编写代码并编写了以下代码。 首先,我找到文件,并为每个文件,我创建一个索引它的线程。 之后,我加入线程并优化索引。 它有效,但我不确定……我可以大规模信任它吗? 有没有办法改善它?

import java.io.File; import java.io.FileFilter; import java.io.FileReader; import java.io.IOException; import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.document.Field; import org.apache.lucene.document.Document; import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.StopAnalyzer; import org.apache.lucene.index.IndexReader; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.util.Version; import org.apache.lucene.index.TermFreqVector; public class mIndexer extends Thread { private File ifile; private static IndexWriter writer; public mIndexer(File f) { ifile = f.getAbsoluteFile(); } public static void main(String args[]) throws Exception { System.out.println("here..."); String indexDir; String dataDir; if (args.length != 2) { dataDir = new String("/home/omid/Ranking/docs/"); indexDir = new String("/home/omid/Ranking/indexes/"); } else { dataDir = args[0]; indexDir = args[1]; } long start = System.currentTimeMillis(); Directory dir = FSDirectory.open(new File(indexDir)); writer = new IndexWriter(dir, new StopAnalyzer(Version.LUCENE_34, new File("/home/omid/Desktop/stopwords.txt")), true, IndexWriter.MaxFieldLength.UNLIMITED); int numIndexed = 0; try { numIndexed = index(dataDir, new TextFilesFilter()); } finally { long end = System.currentTimeMillis(); System.out.println("Indexing " + numIndexed + " files took " + (end - start) + " milliseconds"); writer.optimize(); System.out.println("Optimization took place in " + (System.currentTimeMillis() - end) + " milliseconds"); writer.close(); } System.out.println("Enjoy your day/night"); } public static int index(String dataDir, FileFilter filter) throws Exception { File[] dires = new File(dataDir).listFiles(); for (File d: dires) { if (d.isDirectory()) { File[] files = new File(d.getAbsolutePath()).listFiles(); for (File f: files) { if (!f.isDirectory() && !f.isHidden() && f.exists() && f.canRead() && (filter == null || filter.accept(f))) { Thread t = new mIndexer(f); t.start(); t.join(); } } } } return writer.numDocs(); } private static class TextFilesFilter implements FileFilter { public boolean accept(File path) { return path.getName().toLowerCase().endsWith(".txt"); } } protected Document getDocument() throws Exception { Document doc = new Document(); if (ifile.exists()) { doc.add(new Field("contents", new FileReader(ifile), Field.TermVector.YES)); doc.add(new Field("path", ifile.getAbsolutePath(), Field.Store.YES, Field.Index.NOT_ANALYZED)); String cat = "WIR"; cat = ifile.getAbsolutePath().substring(0, ifile.getAbsolutePath().length()-ifile.getName().length()-1); cat = cat.substring(cat.lastIndexOf('/')+1, cat.length()); //doc.add(new Field("category", cat.subSequence(0, cat.length()), Field.Store.YES)); //System.out.println(cat.subSequence(0, cat.length())); } return doc; } public void run() { try { System.out.println("Indexing " + ifile.getAbsolutePath()); Document doc = getDocument(); writer.addDocument(doc); } catch (Exception e) { System.out.println(e.toString()); } } } 

任何肝脏被认为。

如果要并行化索引,可以执行以下两项操作:

  • 并行调用addDocument,
  • 增加合并调度程序的最大线程数。

您正在使用并行化对addDocuments的调用的正确途径,但是每个文档生成一个线程将无法扩展,因为您需要索引的文档数量将增加。 您应该使用固定大小的ThreadPoolExecutor 。 由于此任务主要是CPU密集型(取决于您的分析器和检索数据的方式),因此将计算机的CPU数量设置为最大线程数可能是一个良好的开端。

关于合并调度程序,您可以增加可与ConcurrentMergeScheduler的setMaxThreadCount方法一起使用的最大线程数。 请注意,磁盘在顺序读取/写入方面比随机读取/写入要好得多,因此为合并调度程序设置过高的最大线程数可能会降低索引速度,从而加快速度。

但在尝试并行化索引过程之前,您应该尝试找出瓶颈所在。 如果您的磁盘太慢,则瓶颈可能是刷新和合并步骤,因此并行调用addDocument(主要包括分析文档并在内存中缓冲分析结果)将无法提高索引速度一点都不

一些附注:

  • 在Lucene的开发版本中有一些正在进行的工作,以便改进索引并行性(特别是,这个博客条目解释了它的工作方式)。

  • Lucene有一个关于如何提高索引速度的不错的wiki页面,你会发现其他方法来提高索引速度。

我认为更现代的方法是使用ThreadPoolExecutor并提交一个正在进行索引的Runnable 。 您可以使用.awaitTermination或CountdownLatch等待所有线程终止。

我不是让你的主类扩展Thread的忠实粉丝,只是创建一个可运行的内部类,在构造函数中获取它的depdencies。 这使得您的代码更具可读性,因为线程所做的工作与应用程序设置代码明确分开。

关于样式的一些注释,我不是让你的主类抛出exception的忠实粉丝,这通常只是意味着你不清楚你正在使用的代码可以抛出的不同的已检查exception情况。 除非你有一个非常具体的原因,否则通常不是正确的做法。