Nemo

Nemo 关注TA

路漫漫其修远兮,吾将上下而求索。

Nemo

Nemo

关注TA

路漫漫其修远兮,吾将上下而求索。

  •  普罗旺斯
  • 负责帅就完事了
  • 写了1,495,102字

该文章投稿至Nemo社区   Java  板块 复制链接


Luncene 多线程索引解决方案记录

发布于 2017/09/25 15:11 3,699浏览 0回复 3,357

实际测试情况下,发现每个创建索引线程操作时,索引目录均会生成一个write.lock文件。而此时当有其他线程也在操作创建索引的时候,会抛出

org.apache.lucene.store.LockObtainFailedException: Lock held by this virtual machine: F:\blogIndex\write.lock

at org.apache.lucene.store.NativeFSLockFactory.obtainFSLock(NativeFSLockFactory.java:127)

at org.apache.lucene.store.FSLockFactory.obtainLock(FSLockFactory.java:41)

at org.apache.lucene.store.BaseDirectory.obtainLock(BaseDirectory.java:45)

at org.apache.lucene.index.IndexWriter.(IndexWriter.java:775)

at com.nemo.index.LinkIndexer.(LinkIndexer.java:55)

at com.nemo.index.LinkIndexer.getWriter(LinkIndexer.java:37)

at com.nemo.client.LinkSoupClient.(LinkSoupClient.java:31)

at com.nemo.index.LinkIndexer$2.run(LinkIndexer.java:133)

异常。


从异常信息可以看出,大致意思是当前索引目录正有索引操作,当前目录被锁定,不允许其他创建索引操作。


按照lucene官方的文档,lucene的单线程效率也是很高的。所以如果单线程效率足够满足需求的情况下,那么则使用单线程即可。

实际使用中,可以将多线程中需要索引的信息主键啥的先存到某指定的列表中,再开启单个索引线程,单个索引线程监听需要索引的信息列表,逐个索引。(目前Link-Nemo上使用的文章/新闻索引方案)。


如果单个线程满足不了的情况下,那么则需要考虑多线程索引了。

lucene支持合并小索引,所以咱们可以考虑多线程先创建一定数量的小索引,而最后再将这些小索引合并。

设想的场景下,需查询数据库(1000W条数据的表),然后将查询出来的数据放入lucene索引文件中。

假设此时咱们开启一个线程池,初始化100个线程,某个现成取某个区间的数据,用来单独索引到单独的文件夹中。最后再将索引合并,即可。


多线程的方案暂时没有与单线程方案做效率比较,所以单线程/多线程方案还有待验证。不过按照Link-Nemo目前的数据量而言,单线程足够了。


下面是索引访问的原则:

1.在同一时刻,lucene索引中允许有一个进程对其进行加入文档,删除文档,更新索引等操作。

2.在同一时刻,lucene索引允许多个线程同时对其进行检索。

在Lucene中,对索引发生修改的类主要集中在IndexWriter和IndexReader。其中,IndexWriter主要负责对索引的写入和索引的整体的维护,如合并,优化等操作;IndexReader则负责从索引中删除文档。

如果按照上面第一句所说,则可以得出以下结论:

1.任一时刻,在系统中只能有一个IndexWriter的实例对索引进行操作,不允许有多个IndexWriter向索引添加Document,或是优化索引,合并索引。

lucene3.0API:Opening an IndexWriter creates a lock file for the directory in use. Trying to open another IndexWriter on the same directory will lead to a LockObtainFailedException. The LockObtainFailedException is also thrown if an IndexReader on the same directory is used to delete documents from the index.

2.任一时刻,不能有多个IndexReader在执行文档的删除操作。下一个InderReader应当在上一个InderReader执行close方法之后运行。

3.在使用IndexWriter向索引加入文档前,必须先关闭执行删除操作的IndexReader实例。

lucene3.0API:An IndexReader can be opened on a directory for which an IndexWriter is opened already, but it cannot be used to delete documents from the index then.

4.在使用IndexReader删除前,必须先关闭执行添加Document操作IndexWriter的实例。

综合说来,任何对索引发生修改的操作都不能同时发生;或是在上一修改操作未保存的情况下,用新的实例对索引进行下一修改操作。

Lucene锁机制

lucene使用commit.lock

与write.lock实现锁机制。所谓的锁其实是存放于系统临时目录内的一个文件。例如,建立索引时,可以看到一个write.lock存放在临时目录中。又如,当合并索引时,会看到一个commit.lock存放在临时目录中。

那么,这些锁究竟在什么时候会出现呢?正如前面所说到的,Lucene的同步问题只可能发生在对索引进行文档添加,文档删除,合并segment和优化时,因此lucene锁也基本都出现在这时候。

1.write.lock

writer.lock出现在向索引添加文档时,或是将文档从索引中删除时。writer.lock会在indexwriter被初始化时创建,然 后会在调用IndexWriter的close()方法时被释放。另外,会在IndexReader使用delete方法删除文档时创建,并在调用 IndexReader的close()方法时被释放。

2.commit.lock

commit.lock主要与segment合并和读取的操作相关。例如,他出现在IndexWriter的初始化时,但是一旦当segment的 信息被读取完毕,它就会立刻被释放。另外,当调用IndexWriter的AddIndexs()或MergeSegment()方法时,都会生成这个 锁。

事实上,由于有了这两个锁的所有,才使得lucene具有了保护器索引不受非法操作的功能。

建议IndexWriter作为单例模式得到,IndexReader进行删除时上锁。

点赞(0)
点了个评