Google

Archives

lucene并行建索引解决方案–转载

 
 

Sent to you by Jeffye via Google Reader:

 
 

lucene并行建索引解决方案–转载

via 笨笨的小田园 by 笨笨 on 8/22/08

 背景:单线程为30万条数据建索引花了10分钟,为了提高效率采用多线程
 
 起初我采用多个线程共享一个indexwriter实例(也意味着往同一个目录写索引),这是

 lucene in action 和lucene
wiki的推荐做法,不知道到为什么总是报FileNotFoundException,
 很让人困惑。偶尔会成功一次。这个错误让我想起另外一个问题,就是在建索引的时候搜索也会报这个

 错误,lucene in action 明明也说了建索引读的时候没问题。
 
 言归正传,我第二次尝试使用每个线程单独拥有自己的indexwriter实例,但往同一个目录写索引,果然报了

 写锁的错, 这和书上说的很一致。
 
 最后没办法了,我使用每个线程单独使用自己的实例,往自己的目录写索引,最后一个干完的线程将所有的索引合并

 比如我开了4个线程,那么就有5个目录build_index,build_index1,build_index2,build_index3,build_index4

 线程1往build_index1中写,线程2往build_index2,。。。依次类推,最后一个干完的将build_index1-4目录的索引合并到

 build_index.
 
 
 我开了4个线程尝试发现也要花大概7-8分钟,合并索引的过程非常快20秒左右。
 开了10个线程,整个过程需要6分多钟,合并索引也只花了21秒。

似乎效果并不明显,这因该是因为数据量还不够大引起的,数据量越大,并行的优势会越明显
 
 可
见合并索引的过程非常快,这又提供了另外的好处,我们通常将build_index作为搜索目录,就像上面说的那样,建索引的过程 会影响搜索(虽然按照
书上说是不影响的),如果我们采用这种方案,建索引的绝大部分过程其实与build_index目录无关,只有最后 合并的时候需要用到
build_index,但那个过程又非常的快速,所以可以极大的缓解建索引给搜索带来的问题。
 

顺便说:当然你也可以再开一个通知线程专门等待索引线程,当索引线程完毕之后加入通知线程的队列,通知线程发现自己的队列有通知记录就开始合并索引,这样就不用所有的线程完毕之后才开始合并索引。(这种方案待尝试)

 如果条件允许,你可以扩展一下这个方案,将多线程索引升级为多台机器同时建。

 
 

Things you can do from here:

Subscribe to 笨笨的小田园 using Google Reader
Get started using Google Reader to easily keep up with all your favorite sites

 
 
Incoming search terms for the article:lucene 多线程 (2)lucene 并行 (2)多线程建索引 (2)多目录索引 lucence (2)lucene合并索引 [...]

Lucene同时进行查询和索引

Does Lucene allow searching and indexing simultaneously?Yes. However, an IndexReader only searches the index as of the “point in time” that it was opened.

Lucene在用IndexReader打开索引的同时,允许用IndexWriter对该索引进行更新,但是IndexReader只能查询到open索引时所索引的文件或者说Document,要想查询新索引的Document,IndexReader必须调用reopen方法(该方法开销较小)。

IndexReader.isCurrent() 方法可以用于测试索引是否有更新。

扩展Lucene的索引文件存储

form: http://www.dlog.cn/javayou

本文主要叙述如何通过引入Commons-VFS项目来扩展Lucene的索引文件存储方式。在阅读本文之前,您必须对Lucene有一定的了解,最好是有编写过Lucene代码。另外文章中所提到的Lucene如果不做特殊说明指的是Lucene的Java版本。

使用过Lucene来做为搜索引擎的朋友知道,Lucene默认的使用文件系统来存储索引文件。一般我们需要指定一个路径做为参数来初始化索引的读写类。例如下面语句准备在D盘的lucene_idx目录下写索引信息。

IndexWriter idx_writer = new IndexWriter(“D:/lucene_idx”,new StandardAnalyzer(),false);

因此整个系统的索引一般就会集中在某台服务器的某个目录。为了保证写索引不会产生冲突,经常我们只运行一个负责写索引的进程,其他的应用可以访问索引目录进行查询。当应用程序是集群环境下的不同机器时,其他的机器如何访问到索引服务器上的索引数据目录呢?你肯定已经想到很多种方法来访问其他机器的目录:例如Windows机器可以通过网上邻居;Linux服务器之间可以通过NFS访问;或者Linux和Windows服务器之间可以通过SAMBA来相互访问各自的文件系统。还有没有其他更加灵活的解决方案吗?

当然有,通过扩展Lucene的索引存储接口,你可以通过FTP、HTTP来读写索引文件,你甚至可以将Lucene的索引数据保存在数据库中,虽然我不知道这到底是不是一个好主意,但起码技术上是可行的。

为了使我们对Lucene的扩展有着实际的意义,我们将引入另外一个Apache组织新的开源项目—— Commons-VFS,我们简称为VFS。VFS项目把对各种各样的文件系统的访问封装成统一的应用程序接口,这大大的简化了应用程序本身代码的复杂度。目前VFS支持下面一些文件系统,而这个列表会越来越多,当然你也可以自行进行扩展。

FTPLocal FilesHTTP and HTTPSSFTPTemporary FilesZip, Jar and Tar (uncompressed, tgz or tbz2)gzip and bzip2resram

或许你已经猜出来了引入VFS项目的目的:我们要利用VFS 项目和Lucene结合,使Lucene的索引文件可以存放在VFS所支持的各种可读写的文件系统中。

因此接下来我们的任务就是让Lucene使用VFS来存取索引数据,至于这些数据放在哪里,是放在数据库、文件系统还是其他地方,这个问题就交给VFS的配置去处理了。

你准备好了嘛?下面我们要深入到Lucene的代码中去探个究竟了。首先我们注意到了IndexReader、IndexWriter、IndexSearcher等这几个索引数据的读写类的构造函数都可以接受名为org.apache.lucene.store.Directory 的类做为参数。再查看api文档发现Directory有两个子类分别是:FSDirectory和RAMDirectory,从类名上可以猜出FSDirectory是基于文件系统的,而RAMDirectory是基于内存的。再读一下IndexWriter类的源码后证实了这个猜想,当我们传递路径字符串而不是Directory对象给IndexXxxxx类时,Lucene会初始化一个FSDirectory对象来处理索引数据的读写操作。

而另外一个RAMDirectory类是基于内存的索引数据存取,它是Lucene所提供的第二种索引数据存取方式。它可以接受一个FSDirectory做为构造函数的参数,相当于把存放在磁盘中的索引数据全部加载到内存中,以提高索引搜索的性能,我们暂不理会这种方式。但我们已经了解到org.apache.lucene.store包中的类便是Lucene存储的核心,我们可以通过扩展Directory类来实现存储自定义。姑且把所要扩展的这个类命名为VFSDirectory。

先来看看Directory都有什么方法:

Method Summary

abstract void

close() 关闭存储.

abstract IndexOutput

createOutput(String name) 使用给定的名字创建一个新的空文件.

abstract void

deleteFile(String name) 删除指定名字的文件.

abstract boolean

fileExists(String name) 判断文件是否存在.

abstract long

fileLength(String name) 获取文件长度.

abstract long

fileModified(String name) 返回文件最近修改的时间.

abstract String[]

list() 列出所有的文件.

abstract Lock

makeLock(String name) 构造一个锁对象.

abstract IndexInput

openInput(String name) 获取文件输入流.

abstract void

renameFile(String from, String to) 文件重命名.

abstract void

touchFile(String [...]

Lucene索引中的数据.

Lucene本身并不支持更新, 所以只能选择先删除再新增记录。
lucene本身支持两种删除模式
1,DeleteDocument(int docNum) //指定文档标号自动删除
2,DeleteDocuments(Term term) //删除所有出现该term的文档
一般使用的是第二种
IndexReader reader = IndexReader.Open(path));
int count=reader.DeleteDocuments(new Term(“FieldName”,”Txt”));

Lucene的删除也就是一次搜索的过程.
备注:需要匹配删除的字段存储时不要进切词.

我的疑问:

1. 用以上方式就行索引更新,特别是当更新的数据非常多的时候,如何保证更新的同时提高查询服务?optimize()数据俩一大还是挺慢的,不能在短时间完成。

是否可以在更新的数据量很大时,直接生成新索引,然后更换查询的索引。

2. 用lucene实现在线查询,该如何做? 是否可以在内存中用生成索引, 同时查两个索引。等内存中索引到一定大小时,写到硬盘或就行合并,但这样索引合并的代价比较大。

知道的朋友,劳烦告诉我,3qs!

—————————–

该问题已经解决,其实Lucene中自带此功能, 如果要实现在线索引感觉还是在内存中保持Index比较方便。

索引修改后,要想保持查询同步,只需使用reopen()函数重新打开索引即可,这个函数实现速度较快。

下面是我的应用中一段程序,对应凑合看就行。

public void refreshSearcher() {

synchronized (searcher) {

try {

int preNum = searcher.maxDoc();

IndexSearcher[] searchers = new IndexSearcher[ireaderList

.size()];

ArrayList<IndexReader> list = new ArrayList<IndexReader>();

for (int i = 0; [...]

Inside Lucene/超人气搜索引擎学习(2.0)-读取索引

以前搜集的两篇文章,感觉对lucene的研究能稍微深入点的,想研究或者修改源代码的话值得一看。 下面是源地址
Inside Lucene/超人气搜索引擎学习(2.0)-读取索引

Index in Practice 索引: 按图索骥

T ermDoc从哪读取数据,自然是硬盘上已经建好的某个index, 具体说, 是从index中的某个文件读取. 要了解TermDoc读了什么东东,怎么读这些东东,必要时得考察Lucene index的细部结构.

T ermDoc是个抽象类,这很好,以后可以创建自己的index结构,建立自己的搜索算法.不过这之前先要了解Lucene是怎么干的,而这个抽象类并不包含这个信息,所以,我们首先要找到TermQuery使用哪个TermDoc实现.

回想一下scorer中的TermDoc从哪里来. public class TermQuery extends Query { private class TermWeight implements Weight { public Scorer scorer(IndexReader reader) throws IOException { TermDocs termDocs = reader.termDocs(term);

if (termDocs == null) return null;

return new TermScorer(this, termDocs, getSimilarity(searcher), reader.norms(term.field())); } … } … } 从 [...]