• 索引管理 《第四篇》


    一、索引管理

      1、查看索引的相关信息

      查看索引的信息大概有如下方法:

    1. 通过IndexWriter类读取索引的相关信息,将其显示出来。
    2. 通过IndexReader及其子类读取索引的相关信息,将其显示出来。
    3. 通过可视化工具查看索引的相关信息。

      1、使用IndexWriter类读取索引的相关信息

      使用IndexWriter读取并非最佳方法。

      2、使用IndexReader及其子类读取索引的相关信息

      IndexReader类是专门用于读取索引信息的类,它是一个抽象类,含有FilterIndexReader、MultiReader、ParallelReader等子类。

      IndexReader类通常使用其静态方法Open创建其子类对象。

      使用IndexReader类可以获得某个索引的详细信息。例如,指定路径的索引是否存在,索引中包含的文档,索引中包含的词条情况,索引是否经过了优化等等。

        using (IndexReader reader = IndexReader.Open(fsdirectory, true))
        {
            Console.WriteLine(reader.IsCurrent());      //False 是否当前正在使用的索引
            Console.WriteLine(reader.IsOptimized());    //False 索引是否经过了优化
            Console.WriteLine(reader.Directory());      //索引路径
            Console.WriteLine(reader.NumDocs());        //含有文档数目
            Console.WriteLine(reader.Document(1).GetField("Name").StringValue); //输出 第1个文档 Name字段的值
    
            //所有词条
            TermEnum te = reader.Terms();
            while (te.Next())
            {
                Console.WriteLine(te.Term.Text);
            }
        }
        //上次更新时间
        long g = IndexReader.LastModified(fsdirectory);
        DateTime dt = DateTime.FromFileTime(g); 
        Console.WriteLine(dt);

      2、删除索引中的文档

      如果某个文档不需要被检索了,就应该把它从索引中删除。在Lucene.net中,通过IndexReader和IndexModifier两个类从索引中删除文档。

      1、使用IndexReader从索引中删除文档

      (1)、删除指定序号的文档

      通过调用IndexReader类的DeleteDocument(int id),可以从索引中删除指定序号的文档。

        using (IndexReader reader = IndexReader.Open(fsdirectory, false))   //注意readonly参数要设为false
        {
            reader.DeleteDocument(1);
        }

      查看索引目录,发现生成了一个扩展名为"del"的新文件,在这个文件中存储着被删除文档的信息。

      

      这个时候,文档并没有真正从索引中删除,只是做了已经被删除的标记,从而使之不能参与检索。如果此时再通过NumDocs()方法读取,会减少一条。

      2、恢复被删除的文档

      通过调用IndexReader类的DeleteDocument(int id)方法,文档并没有实际删除,所以可以把这些标记为删除的文档恢复过来,调用UnDeleteAll()方法即可将所有文档都恢复。

      此时,刚才生成的"del"文档消失了。

      

      3、物理删除文档

      要从物理上删除索引中的文档,需要有如下步骤:

    1. 使用IndexReader为文档作删除标记。
    2. 执行IndexWriter的Optimize()方法。

      3、批量删除文档

      IndexReader还有一个方法

      int DeleteDocuments(Term term);

      通过这个方法,可以删除指定Term的文档。

        using (IndexReader reader = IndexReader.Open(fsdirectory, false))   //注意readonly参数要设为false
        {
            Term term = new Term("Name", "");
            reader.DeleteDocuments(term);
        }

      4、更新文档

      1、更新索引中的单个文档

      目前的版本,索引的更新是可以用的,但是也相当于原有索引作废,不参与检索,然后新增一条索引,可以用UpdateDocument了。2018-03-19

        using (IndexWriter writer = new IndexWriter(fsdirectory, analyzer, maxFieldLength))
        {
            Document document1 = new Document();
            document1.Add(new Field("Name", "刘备", Field.Store.YES, Field.Index.ANALYZED));
            writer.AddDocument(document1);
        }
    
        using (IndexReader reader = IndexReader.Open(fsdirectory, false))
        {
            reader.DeleteDocument(1);   //删除文档
        }
    
        using (IndexWriter writer = new IndexWriter(fsdirectory, analyzer, maxFieldLength))
        {
            //再添加
            Document document2 = new Document();
            document2.Add(new Field("Name", "关羽", Field.Store.YES, Field.Index.ANALYZED));
            writer.AddDocument(document2);
        }

      2、批量更新索引中的文档

      这个不再说明,批量删除然后再一个个的添加。

      3、更新方法

      IndexWriter有一个方法可以实现索引的更新。

        public virtual void UpdateDocument(Term term, Document doc);
        public virtual void UpdateDocument(Term term, Document doc, Analyzer analyzer);

      该方法使用示例如下:

        using (IndexWriter writer = new IndexWriter(fsdirectory, analyzer, maxFieldLength))
        {
            Document document1 = new Document();
            document1.Add(new Field("Name", "张飞", Field.Store.YES, Field.Index.ANALYZED));
            writer.AddDocument(document1); 
        }
    
        using (IndexWriter writer = new IndexWriter(fsdirectory, analyzer, maxFieldLength))
        {
            Document doc = new Document();
            doc.Add(new Field("Name", "赵云", Field.Store.YES, Field.Index.ANALYZED));
            writer.UpdateDocument(new Term("Name", ""), doc);
        }

    二、索引的同步

      1、Lucene并发访问规则

      并发访问问题,如果处理不好,就会导致索引文件的损坏。为了避免出现这类问题,需要遵守如下规则:

    1. 任意数量的只读操作都可以同时执行。
    2. 某一时刻,只允许执行一个修改索引的操作。也就是说,在同一时间,一个索引文件只能够被一个IndexWriter、IndexReader对象打开。要做到这一点并不难,在编写代码的时候,这三个类的操作都分别进行,一个类操作完成后立即释放资源,然后再调用下一个类的方法。
    3. 只读的搜索操作可以在索引被修改的时候进行。

      2、线程安全性

      Lucene保持索引操作线程安全性的原则是:IndexWriter、IndexReader两个类不能同时对同一个索引进行操作。

      但是这三个类本身都是线程安全的。这三个类的实例都可以被多线程共享,Lucene会对各个线程中所有修改索引的方法的调用进行恰当的同步处理。以此来确保修改操作能一个接一个地有序进行。

      应用程序不需要额外的同步处理,只需确保这三个类的对象对索引的修改操作部重叠。即:一个类操作完成,要马上释放资源,然后在使用下一个类。

      3、索引锁机制

      为了处理索引同步问题,Lucene内置了一种锁机制,锁,体现为一种文件。锁分为以下两种:

    • write.lock
    • commit.lock

      write.lock文件用于阻止进程同时修改一个索引。对于IndexWriter、IndexReader这三个类,在它们对索引进行添加、修改或删除的时候,write.lock文件就产生了。这时候如果有人也想修改索引,是做不到的。必须等到这三个类close()之后调用,write.lock文件才会消失,这时,锁解除了。其他用户才可以对索引进行修改操作。

      在读取和合并索引块的时候,会用到commit.lock锁,这时一个事务锁。例如,在优化索引的时候,需要先将所有的索引块合并起来,等合并成功之时,再将原先分散的索引块删除,整个过程中任何一步出现问题都会导致索引优化失败,因此要用到事务锁。

      这两种锁都是在操作索引时Lucene自动建立的,不需要手动去建立和修改。

      IndexWriter类可以通过WriteLockTimeout属性来设定或取得锁的时间。参数的单位都是毫秒。

  • 相关阅读:
    分布式存储
    存储知识学习
    洛谷 P1003 铺地毯 (C/C++, JAVA)
    多线程面试题系列3_生产者消费者模式的两种实现方法
    多线程面试题系列2_监视线程的简单实现
    多线程面试题系列1_数组多线程分解
    《深度学习》阅读笔记1
    素数在两种常见情况下的标准最优算法
    dfs与dp算法之关系与经典入门例题
    百度之星资格赛2018B题-子串查询
  • 原文地址:https://www.cnblogs.com/kissdodog/p/3637667.html
Copyright © 2020-2023  润新知