前沿
自己在这近一个月的Lucene学习过程中,始终在IndexSearcher和IndexWriter是否单例的问题上纠结,还有如何能实时更新索引并且能够被搜索的问题也让我苦恼了一阵子,上网查了很多资料,最终有了一个初步的了解。
参考链接
http://www.xuebuyuan.com/1935691.html
http://lucene-group.group.iteye.com/group/topic/24404
http://www.cnblogs.com/kissdodog/p/3657094.html
第一个链接是写的代码
第二个链接是一个关于单例的讨论
第三个链接是关于IndexReader的分析
总结要点
IndexReader是和索引库目录打交道的类,实例之后,就相当于这个实例就是一份索引库了。索引开销很到,一般一个索引库全程只维护一个IndexReader
IndexSearcher实例化,尽量用IndexReader来创建,这样就能维持始终一个IndexReader。
关于写索引,lucene一个索引库只有一个writer,luncene在建立索引时时线程安全的,它是加了锁的,所以应该一个索引库全程只使用一个writer
实时更新索引的情况下,要想增量索引或者更新的索引被马上搜索到,需要3个条件。
writer在更新索引后,需要显示的commit
对应索引库的IndexReader,需要重新开启
针对这个IndexReader创建新的IndexSearcher,然后通过这个IndexSearcher进行搜索
最后记得,将过时的IndexReader和IndexSearcher关闭掉
代码实现
类IndexManager | 类ReaderManager |
/** * Created by AbnerLee on 15-1-25. * 索引库管理器类 */ public class IndexManager { /** * writer公共配置 */ private static final IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_4_10_2, Constant.ANALYZER_SMART); static { iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND); iwc.setRAMBufferSizeMB(20.0); iwc.setMaxBufferedDocs(10000); } private Directory dir; private IndexReader reader; private IndexSearcher searcher; private IndexWriter writer; /** * 构造函数 */ public IndexManager(String indexPath) { this(new File(indexPath)); } private IndexManager(File indexDir) { init(indexDir); } /** * 初始化方法 */ private void init(File indexDir) { try { /** * Directory 初始化 */ this.dir = FSDirectory.open(indexDir); /** * IndexWriter 初始化 */ this.writer = new IndexWriter(this.dir, IndexManager.iwc); this.commitWriter(); /** * IndexReader 初始化 */ ReaderManager.getInstance().createIndexReader(dir); this.reader = ReaderManager.getInstance().getIndexReader(dir); /** * IndexSearcher 初始化 */ this.searcher = new IndexSearcher(this.reader); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } public IndexWriter getWriter() { return this.writer; } public void commitWriter() { try { writer.commit(); } catch (IOException e) { this.rollback(); } } private void rollback() { try { writer.rollback(); } catch (IOException e) { e.printStackTrace(); } } public IndexSearcher getSearcher() { IndexReader reader = ReaderManager.getInstance().getIndexReader(this.dir); if (this.reader == null || this.reader != reader){ this.reader = reader; searcher = new IndexSearcher(this.reader); } return this.searcher; } } |
/** * Created by AbnerLee on 15-1-25. * IndexReader 生命周期管理 */ public class ReaderManager { /** * DirectoryReader回收站 */ private static final Map<DirectoryReader, Long> recyleReaderMap = new HashMap<>(); /** * Directory 和 DirectoryReader映射表 */ private static final Map<Directory, DirectoryReader> readerMap = new HashMap<>(); /** * 最大周期 */ private static final int maxLifeTime = 60 * 1000; /** * 定时器 */ private static final Timer readerRefreshTimer = new Timer(); /** * 单例 */ private static final ReaderManager manager = new ReaderManager(); /** * 私有构造器 */ private ReaderManager() { } /** * 获取单例 */ public static synchronized ReaderManager getInstance() { return manager; } /** * 创建IndexReader并放在映射表中 */ public synchronized void createIndexReader(Directory dir) { try { DirectoryReader oldReader = readerMap.get(dir); if (oldReader != null) { // 判断是否重复打开相同dir,导致旧的reader没有关闭 oldReader.close(); } readerMap.put(dir, DirectoryReader.open(dir)); } catch (IOException e) { e.printStackTrace(); } } /** * 获取IndexReader */ public IndexReader getIndexReader(Directory dir) { return readerMap.get(dir); } /** * 定时跟新IndexReader */ static { readerRefreshTimer.schedule(new RefreshReaderTimerTask(), 60 * 1000); } private static class RefreshReaderTimerTask extends TimerTask { @Override public void run() { /** * 刷新reader */ for (Entry<Directory, DirectoryReader> entry : readerMap.entrySet()) { try { DirectoryReader oldReader = entry.getValue(); DirectoryReader newReader = DirectoryReader.openIfChanged(oldReader); if (newReader != null) { //替换旧reader对象 readerMap.put(entry.getKey(), newReader); //放入回收MAP中 recyleReaderMap.put(oldReader, System.currentTimeMillis()); } } catch (IOException e) { e.printStackTrace(); } } /** * 回收无用reader */ for (Entry<DirectoryReader, Long> entry : new HashMap<DirectoryReader, Long>(recyleReaderMap).entrySet()) { if (System.currentTimeMillis() - entry.getValue() > maxLifeTime){ try { entry.getKey().close(); } catch (IOException e) { e.printStackTrace(); } finally { recyleReaderMap.remove(entry.getKey()); } } } } } } |
代码还有待重构,但是基本功能已经实现了··········