到oo搜索首页

您查询的关键词仅在网页标题或指向此网页的链接中出现。

(oo搜索和网页http://lib.csdn.net/article/vras/63096?knId=2054的作者无关,不对其内容负责。oo搜索快照谨为网络故障时之索引,不代表被搜索网站的即时页面。)


utf-8UTF-8 《开发自己的搜索引擎》读书笔记——索引的建立 - 语音识别与合成知识库

《开发自己的搜索引擎》读书笔记——索引的建立

作者:john_bian

  1. Lucene的Document

    Document的意义为文档,在Lucene中,它代表一种逻辑文件。Lucene本身无法对物理文件建立索引,而只能识别并处理Document类型的文件。在某些时候可以将一个Document与一个物理文件进行对应,用一个Document来代替一个物理文件,然而更多的时候,Document和物理的文件没有关系,它作为一种数据源的集合,向Lucene提供原始的要索引的文本内容。Lucene会从Document取出相关的数据源内容,并根据属性配置进行相应的处理。

    按这样的方式,也可以从不同的物理文件来提取数据源,放入同一个Document中。

    不仅如此,由于Document只是负责收集数据源,因此,甚至也可以不使用物理文件来构建一个Document,一段文本、几个数字、甚至是一些链接都可以作为构建Document的数据源。只要将它们加入到Document对象中,Lucene就可以为这些数据源建立索引,并且使用户查找到它们。

  2. 为Document添加多种Field

    对于一个Document来说,该如何表示其所汇集的数据源呢?其实,在Lucene中,数据源是由一个被称为Field的类来表示的。我们可以把Field理解为字段

    通常情况下,可以直接通过Field的构建函数来创建一个Field类型的对象。这个Field类型主要是用来标识当前的数据源的各种属性,存储来自数据源的数据内容。Lucene在对每个Field进行处理时,会充分考虑到数据源的各种属性,以便做出不同的处理。

    事实上,这种Document-Field结构与关系数据库有异曲同工之处。在关系数据库中一个表(table)可以看成Lucene中的索引,表中的每一个记录,就是Lucene中的Document,而表中的每一个字段,也就是Lucene中的Field。表中的每一个字段有各种属性,可以是字符型,可以是数字型,这也正如Field一样,具有各种属性。

  3. 上面讲到的数据源的各种属性,其实是指以下几种:

    是否存储(该数据源的数据是否要完整地存储于索引中);

    是否索引(该数据源的数据是否要在用户检索时被检索,如果一个数据源没有被索引,用户是无法在该数据源上进行搜索的);

    是否分词(分词是指对数据源的文本按照某种规则进行切分)。

  4. Document的内部实现

    Document的实现相对简单,它主要起到对Field的信息进行记录和管理的作用,以便Lucene遍历所有的Field信息。在Document内部,Field是保存在一个Vector类型的对象数组中。

    Document的主要作用就是维护它内部的Field信息,包括对Field的增、删、查等功能。

  5. Field的内部实现

    在Lucene2.0版本中,Field内部包含了两个静态的内部类:Store和Index。它们分别表示Field的存储方式和索引方式。

    其中,Store类有3个公有的静态属性:

    Store.NO:表示该Field不需要存储;

    Store.YES:表示该Field需要存储;

    Store.COMPRESS:表示使用压缩方式来保存这个Field的值。

       Index类有四个公有的静态属性:

              Index.NO:表示该Field不需要索引(也就是用户不需要去查找该Field的值);

              Index.TOKENIZED:表示该Field先被分词再被索引;

              Index.UN_TOKENIZED:表示不对该Field进行分词,但是要对它进行索引(也就是该Field会被用户查找);

              Index.NO_NORMS:表示对该Field进行索引,但是不使用Analyzer,同时禁止它参加评分,主要是为了减少内存的消耗。

       通过Store和Index类的组合使用,就可以表示出当前这个Field的所有状态。

  1. Field类的构造方法

    在Lucene2.0版本中,Field类本身提供了5种不同的公有构造方法:

    public Field(String name , Stringvalue,Store store,Index index)

    public Field(String name, Stringvalue,Store stor, Index index,TermVector termVector)

    public Field(String name,Reader reader)

    public Field(String name,Readerreader,TermVector termVector)

    publicField(String name,byte[] value,Store store)

    其中,name参数统一指的是Field的名字。在为Field添加值时具体的方式有三种:

              直接的字符串方式;

              使用Reader从外部传入;

              使用直接的二进制byte传入。

    termVector属性是用于表明是否要对Field的词条向量进行存储,其实就是一个有关词条及出现次数的记录。

7、Lucene的索引工具IndexWriter,其主要作用是对索引进行创建,加入Document,合并索引段,以及控制与索引相关的方方面面,它是Lucene的索引的主要操作者。

8、IndexWriter的三个构造函数:

publicIndexWriter(String path,Analyzer a,boolean create)

publicIndexWriter(File path,Analyzer a,boolean create)

public IndexWriter(Directoryd,Analyzer a,boolean create)

       它们的第一个参数,都是代表索引的存放位置。String类型的是绝对路径,File类型是经过包装的绝对路径,Directory类型是Lucene内部的一种目录表示方式。

       Analyzer是Lucene中很重要的一个工具,主要负责对各种输入的数据源进行分析,包括过滤分词等功能。

       第三个参数是一个boolean型的值,该参数是在由第一个参数所指定的路径处,删除原目录内的所有内容重新构建索引,还是在其中已经存在的索引上追加新的Document。

9、在使用addDocument方法加入所有的Document后,一定要使用IndexWriter的close方法来关闭索引器,使所有在I/O缓存中的数据都写入到磁盘上,关闭各种流。这样才能最终完成索引的建立。如果没有关闭,就会发现索引目录内除了一个segments文件外一无所有。

10


11、Segment

       在每个segment里,有许多的Document,在一个索引中,可能有多个segment。Lucene对索引管理的最大单位就是segment。每个segment内的所有索引文件都具有相同的前缀。

       在一个索引中,只有一个“segments”文件,这个文件没有后缀,它记录着当前的索引内有多少个segments,每个segment中有多少个Document这样的信息。

12

       .fdt文件是主要的保存数据源数据的文件,.fdx文件只是记录下当前Document在.fdt中的位置,以便后面读取时方便。需要注意,在.fdt文件中存储的field的值仅为Document中具有Store.YES属性的field。

13、Lucene的索引部分invertDocument方法负责了调用底层分析器的接口,来对数据源进行分析,并统计词条的位置和频率信息,然后将其存入postingTable中。

       在invertDocument方法中,需要注意:

       (1)、对所有需要加入索引的field进行遍历。对于那些不需要分词的field,就将其整个field的数据作为一个大词条,放入postingTable中。

       (2)、对于需要分词的field,则调用底层分词接口进行分词,然后将每个分出来的词都放入postingTable中去。

14对postingTable进行排序

       所有词条被加入postingTable后,Lucene首先将这个postingTable转化为一个Posting类型的数组,然后对这个数组进行排序,使所有的词条按其字典序排列。那样,就可以将词条信息写入.tii和.tis文件。另外,将频率和位置信息写入.frq和.prx文件中去。(在Lucene中采用了快速排序法对这个Posting的数组进行了排序)。

为什么Lucene要对Posting的数组进行排序呢?

       这里涉及一个索引文件的存储方式的问题。索引是整个Lucene工作的基础,没有索引,搜索引擎也就失去了意义。那么,索引文件的存取和访问效率就成了制约搜索引擎性能的重要参数。通常情况下,索引的存储效率可以比读取效率略差一些,这是因为,索引的建立时间对用户体验并无太大影响,所以,应当创建一种索引格式,使之更有助于加速搜索进程。这种格式应当具有索引内容少、可进行有序查找等特点。

15、将Posting信息写入索引

16索引文件格式

(1)、索引的segment

每个segment代表Lucene的一个完整索引段。通常,在一个索引中,会包含有多个segment。每个segment都有统一的前缀,这个签字追的确定是根据当前索引的Document的数量确立的。前缀名等于Document数量转化成36进制后,在前面加上下划线而组成。

       通常,在一个完整的索引中,有且只有一个“segment”文件,这个文件没有后缀,它记录了当前索引中所有segment的信息。

(2).fnm格式的文件中包含了Document中的所有的field名称(field name)。

(3).fdx和.fdt是综合使用的两个文件,其中.fdt用于存储具有Store.YES属性的field的数据。而.fdx则是一个索引,用于存储Document在.fdt中的位置。

(4).tis文件用于存储分词后的词条(Term),而.tii就是它的索引文件,它标明了每个.tis文件中的词条的位置。

(5)在Lucene的索引中,所有的文档被删除后并不是立刻从索引中取出,而是留待下一次合并索引或是对索引进行优化时才真正删除,这点有点类似于Windows的回收站原理。这种功能是通过deletable文件实现的。所有的文档在被删除后,会首先在deletable文件中留一个记录,要真正删除时,才将索引除去。

(6)在IndexWriter中有一个属性:useCompoundFile,默认值为true,表示是否使用复合索引格式来保存索引。在索引内容非常大,文件数量很多的情况下,系统打开文件将会极大地耗费系统资源。因此Lucene提供了一种单文件索引格式,也就是所谓的复合索引格式。使用复合索引格式存储Document内容时,只需要在初始化完成一个IndexWriter对象后,使用setUseCompoundFile(boolean)方法,将属性值设置为true就可以了。

17索引过程的调优

(1)合并因子mergeFactor

该因子决定segment该如何被addDocument()方法进行合并。该值取较小时,建索引时需要更小的内存,搜索未优化索引的速度会更快,但索引建立的速度会比较慢,适合于间歇性地向索引加入文档。该值较大时,适合于批量索引建立

举一个例子:

将mergeFactor的因子设置为10,那么,每向索引添加10个Document时就会有一个新的segment建立。

当第10个这样的segment建立好后,它们会被合并成一个具有100个Document的新segment。

接下来,每100个Document又会创建一个新的segment,当第999个文档被加入索引时,此时磁盘上应该已经有了9个segment,其中每个都有100个Document,而第901个到999个Document此时正在内存中,还未被写入磁盘中。

倘若此时再向索引中加入一个Document,那么,前9个segment就会和这第10个新创建的segment进行合并,成为一个具有1000个Document的segment。过程一次类推。

(2)、maxMergeDocs

该参数用于实现对mergeFactor参数的限制,表示在一个segment中,最多可以拥有的Document数量。

(3)minMergeDocs

当索引被刷到磁盘中,需要首先保存在内存中,minMergeDocs就是用来限制这个内存中文档数量的。

18索引的合并与索引的优化

(1)、Directory类有两个子类:RAMDirectory(与文件系统的内存有关)和FSDirectory(与文件系统的目录有关)。

       FSDirectory指的是在文件系统中的一个路径。因此,当Lucene向其中写入索引时,会直接写入到磁盘上。而RAMDirectory则是内存中的一个区域,当虚拟机退出后,里面的内容会随之消失。因此需要将RAMDirectory中的内容转到FSDirectory中。

       对于RAMDirectory,只需要简单的使用构造函数就可以生成实例。而FSDirectory的实例则需要使用静态方法来生成,这个静态方法有两个参数,第一个参数为所需要存入索引的文件系统的路径,第二个参数为一个boolean型的参数,表示是否将原目录中的所有内容清空。

(2)、使用IndexWriter来合并索引

Lucene的IndexWriter类提供了一个接口,以合并不同的索引。这并不是合并具体的目录,而是合并不同的Directory型对象。不仅可以将存放于不同文件系统路径下的索引合并,还可以将内存中的索引与文件系统中的索引合并,以此来保存那些存放于RAMDirectory中的索引。

       在合并内存中的索引时,一定要先将相应的IndexWriter关闭,以保证滞留在缓存中的文档被“刷”到RAMDirectory中去,这点与使用FSDirectory时一样,如果不使用close方法关闭IndexWriter,就会发现索引文件并未真正写入目录中去。

(3)、索引的优化

IndexWriter的optimize方法能够对当前IndexWriter所指定的索引目录及其所使用的缓存目录下的所有segment做优化,是所有的segments合并成一个完整的segment,即整个索引目录内只出现一种文件前缀。

19从索引中删除文档

(1)、在Lucene的index包中,有一个很重要的工具IndexReader。它主要负责对索引的各种读取和维护工作。如打开一个索引、取得索引中的某个文档、获取索引中总文档的数量,甚至从索引中删除某个文档。

(2)、使用文档ID号来删除特定文档

方法名IndexReader.deleteDocument(intid)。在Lucene的内部使用类似回收站的机制来管理Document的删除。在每个Document被从索引中删除时,它只相当于被扔进了回收站,并未实际删除,如果不使用reader.close()方法将删除文档的信息写入磁盘,一旦进程退出索引会恢复成原来的状态。

IndexReader的undeleteAll()方法可以实现反删除(相当于回收站的还原)。

只需要使用IndexWriter对索引optimize一次,Lucene就会重新为每一个文档分配ID值,这样,那些被标记为已删除的Document就真正的被物理删除了。(清空回收站)。

(3)、使用Field信息来删除批量文档

IndexReader的deleteDocument()方法是一个能够批量删除索引的方法,它删除索引是按照词条来进行的。Term类是用于表示词条的一个工具,它能够将词条表示成对。

20、Lucene的同步问题

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

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

       任一时刻,不能有多个IndexReader在执行文档的删除操作。下一个IndexReader的操作应该在上一个IndexReader的close方法执行未完成后运行;

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

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

(2)Lucene中的锁

       write.lock和commit.lock
21、IndexModifier集成了IndexWriter的大部分功能和IndexReader中的对索引删除的功能。

发表评论

0个评论

我要留言×

技术领域:

我要留言×

留言成功,我们将在审核后加至投票列表中!

提示x

语音识别与合成知识库已成功保存至我的图谱现在你可以用它来管理自己的知识内容了

删除图谱提示×

你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?

删除节点提示×

无法删除该知识节点,因该节点下仍保存有相关知识内容!

删除节点提示×

你确定要删除该知识节点吗?

Powered by oo搜索