关于Lucene中IndexSearcher,IndexWriter和IndexReader的管理

前沿

自己在这近一个月的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的分析


总结要点

  1. IndexReader是和索引库目录打交道的类,实例之后,就相当于这个实例就是一份索引库了。索引开销很到,一般一个索引库全程只维护一个IndexReader

  2. IndexSearcher实例化,尽量用IndexReader来创建,这样就能维持始终一个IndexReader。

  3. 关于写索引,lucene一个索引库只有一个writer,luncene在建立索引时时线程安全的,它是加了锁的,所以应该一个索引库全程只使用一个writer

  4. 实时更新索引的情况下,要想增量索引或者更新的索引被马上搜索到,需要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());

                    }

                }

            }

        }

    }



}

代码还有待重构,但是基本功能已经实现了··········








转载于:https://my.oschina.net/AbnerLee/blog/372368

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值