Index索引刷新过程:
只有Index Writer上的commit操作才会导致ram directory上的数据完全同步到文件。
Index Writer提供了实时获得reader的API,这个调用将导致flush操作,生成新的segment,但不会commit(fsync),从而减少 了IO。新的segment被加入到新生成的reader里。从返回的reader里,可以看到更新。所以,只要每次新的搜索都从IndexWriter获得一个新的reader,就可以搜索到最新的内容。这一操作的开销仅仅是flush,相对commit来说,开销很小。
Lucene的index组织方式为一个index目录下的多个segment。新的doc会加入新的segment里,这些新的小segment每隔一段时间就合并起来。因为合并,总的segment数量保持的较小,总体search速度仍然很快。为了防止读写冲突,lucene只创建新的segment,并在任何active的reader不在使用后删除掉老的segment。
flush是把数据写入到操作系统的缓冲区,只要缓冲区不满,就不会有硬盘操作。commit是把所有内存缓冲区的数据写入到硬盘,是完全的硬盘操作, 重量级操作。这是因为,Lucene索引中最主要的结构posting通过VINT和delta的格式存储并紧密排列。合并时要对同一个term的posting进行归并排序,是一个读出,合并再生成的过程。
Lucene实现SearchManager近实时搜索
lucene通过NRTManager这个类来实现近实时搜索,所谓近实时搜索即在索引发生改变时,通过线程跟踪,在相对很短的时间反映给给用户程序的调用NRTManager通过管理IndexWriter对象,并将IndexWriter的一些方法(增删改),例如addDocument,deleteDocument等方法暴露给客户调用,它的操作全部在内存里面,所以如果你不调用IndexWriter的commit方法,通过以上的操作,用户硬盘里面的索引库是不会变化的,所以你每次更新完索引库请记得commit掉,这样才能将变化的索引一起写到硬盘中,实现索引更新后的同步用户每次获取最新索引(IndexSearcher),可以通过两种方式,第一种是通过调用NRTManagerReopenThread对象,该线程负责实时跟踪索引内存的变化,每次变化就调用maybeReopen方法,保持最新代索引,打开一个新的IndexSearcher对象,而用户所要的IndexSearcher对象是NRTManager通过调用getSearcherManager方法获得SearcherManager对象,然后通过SearcherManager对象获取IndexSearcher对象返回个客户使用,用户使用完之后调用SearcherManager的release释放IndexSearcher对象,最后记得关闭NRTManagerReopenThread;
第二种方式是不通过NRTManagerReopenThread对象,而是直接调用NRTManager的maybeReopen方法来获取最新的IndexSearcher对象来获取最新索引.
1.单独使用SearchManager
1.// 实现近实时搜索//所以就不通过Indexread来创建search
private SearcherManager manager;//是线程安全的
2.在构造方法中实现SearchManager
// 创建SearcherManager
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
indexWriterConfig.setRAMBufferSizeMB(48);
indexWriter = new IndexWriter(FSDirectory.open(new File(Constants.INDEX_STORE_PATH)), indexWriterConfig);
trackingIndexWriter = new TrackingIndexWriter(indexWriter);
commitStrategy = new CommitStrategy(indexWriter);
commitStrategy.start();
nRTSearcherManager = new NRTSearcherManager(trackingIndexWriter);
在新建SearcherManager时候,创建监听线程更新索引变化.
indexSearcherReferenceManager = new SearcherManager(trackingIndexWriter.getIndexWriter(),
true, new SearcherFactory());
indexSearcherReopenThread =
new ControlledRealTimeReopenThread<>(trackingIndexWriter,
indexSearcherReferenceManager,
60, // when there is nobody waiting
0.1); // when there is someone waiting
indexSearcherReopenThread.start();
3.在检索的query方法中
// 获得一个search
IndexSearcher search = indexSearcherReferenceManager.acquire();
4.
/*
* maybeReopen会自动检查是否需要重新打开
* 比如重复执行search02几次,中间一次删除一条数据
* 这个删除的数据需要对writer进行commit才行
* 那么使用maybeReopen就可以检测到硬盘中的索引是否改变
* 并在下次查询的时候把删除的这条给去掉
*/
manager.maybeReopen();
5.通过SearchManager关闭search
finally {
try {
// 通过manager关闭search
indexSearcherReferenceManager.release(search);
} catch (IOException e) {
e.printStackTrace();
}
6.对索引操作时
writer.deleteDocuments(new Term("id", "1"));
// 使用SearchManage需要对writer进行commit
writer.commit();