介绍开源软件搜索引擎——lucene的各个实现的功能,性能,以及代码分析
1.2. 背景
分析的系统名称 | Lucene |
该开源主页 | |
开发语言 | JAVA |
该系统的分析者 | zzpchina |
该系统作者简介 | Lucene的贡献者Doug Cutting是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。 |
该系统简介 | Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。 |
1.3. 该项目的使用现状
经过多年的发展,Lucene在全文检索领域已经有了很多的成功案例,并积累了良好的声誉。
基于Lucene的全文检索产品和应用Lucene的项目在世界各地已经非常之多,比较知名的有:
- Eclipse:主流Java开发工具,其帮助文档采用Lucene作为检索引擎
- Jive:知名论坛系统,其检索功能基于Lucene
- Ifinder:出自德国的网站检索系统,基于Lucene(http://ifinder.intrafind.org/)
- MIT DSpace Federation:一个文档管理系统(http://www.dspace.org/)
国内外采用Lucene作为网站全文检索引擎的也很多,比较知名的有:
- http://www.blogchina.com/weblucene/
- http://www.ioffer.com/
- http://search.soufun.com/
- http://www.taminn.com/
(更多案例,参见http://wiki.apache.org/jakarta-lucene/PoweredBy)
在所有这些案例中,开源应用占了很大一部分,但更多的还是商化业产品和网站。
2. 功能分析
2.1. 与Oracle数据库对比
Lucene的API接口设计的比较通用,输入输出结构都很像数据库的表==>记录==>字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看:可以先把Lucene当成一个支持全文索引的数据库系统。
全文检索库对关系型数据库对比 | ||
对比项 | 全文检索库(Lucene) | 关系型数据库(Oracle) |
核心功能 | 以文本检索为主,插入(insert)、删除(delete)、修改(update)比较麻烦,适合于大文本块的查询。 | 插入(insert)、删除(delete)、修改(update)十分方便,有专门的SQL命令,但对于大文本块(如CLOB)类型的检索效率低下。 |
库 | 与Oracle类似,都可以建多个库,且各个库的存储位置可以不同。 | 可以建多个库,每个库一般都有控制文件和数据文件等,比较复杂。 |
表 | 没有严格的表的概念,比如Lucene的表只是由入库时的定义字段松散组成。 | 有严格的表结构,有主键,有字段类型等。 |
记录 | 由于没有严格表的概念,所以记录体现为一个对象,在Lucene里记录对应的类是Document。 | Record,与表结构对应。 |
字段 | 字段类型只有文本和日期两种,字段一般不支持运算,更无函数功能。 在Lucene里字段的类是Field,如document(field1,field2…) | 字段类型丰富,功能强大。 record(field1,field2…) |
查询结果集 | 在Lucene里表示查询结果集的类是Hits,如hits(doc1,doc2,doc3…) | 在JDBC为例, Resultset(record1,record2,record3...) |
2.2. Lucene全文搜索相对数据库搜索的优点
全文检索 ≠ like "%keyword%"
通 常比较厚的书籍后面常常附关键词索引表(比如:北京:12, 34页,上海:3,77页……),它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样,想像一下通过书后面的索 引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高,另外一个原因是它是排好序的。对于检索系统来说核心是一个排序问题。
由 于数据库索引不是为全文索引设计的,因此,使用like "%keyword%"时,数据库索引是不起作用的,在使用like查询时,搜索过程又变成类似于一页页翻书的遍历过程了,所以对于含有模糊查询的数据库 服务来说,LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配:like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。
所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制,将数据源(比如多篇文章)排序顺序存储的同时,有另外一个排好序的关键词列表,用于存储关键词==>文章映射关系,利用这样的映射关系索引:
关键词==>出现关键词的文章编号、出现次数、起始偏移量、结束偏移量,出现频率
检索过程就是把模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程。从而大大提高了多关键词查询的效率,所以,全文检索问题归结到最后是一个排序问题。
由此可以看出,模糊查询相对数据库的精确查询是一个非常不确定的问题,这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制,并提供了扩展接口,以方便针对不同应用的定制。
可以通过一下表格对比一下数据库的模糊查询:
| Lucene全文索引引擎 | 数据库 |
索引 | 将数据源中的数据都通过全文索引一一建立反向索引 | 对于LIKE查询来说,数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配,比有索引的搜索速度要有多个数量级的下降。 |
匹配效果 | 通过词元(term)进行匹配,通过语言分析接口的实现,可以实现对中文等非英语的支持。 | 使用:like "%net%" 会把netherlands也匹配出来, |
匹配度 | 有匹配度算法,将匹配程度(相似度)比较高的结果排在前面。 | 没有匹配程度的控制:比如有记录中net出现5词和出现1次的,结果是一样的。 |
结果输出 | 通过特别的算法,将最匹配度最高的头100条结果输出,结果集是缓冲式的小批量读取的。 | 返回所有的结果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放这些临时结果集。 |
可定制性 | 通过不同的语言分析接口实现,可以方便的定制出符合应用需要的索引规则(包括对中文的支持) | 没有接口或接口复杂,无法定制 |
结论 | 高负载的模糊查询应用,需要负责的模糊查询的规则,索引的资料量比较大 | 使用率低,模糊匹配规则简单或者需要模糊查询的资料量少 |
全文检索和数据库应用最大的不同在于:让最相关的头100条结果满足98%以上用户的需求
2.3. 相对一般的搜索引擎优点
大部分的搜索(数据库)引擎都是用B树结构来 维护索引,索引的更新会导致大量的IO操作,Lucene在实现中,对此稍微有所改进:不是维护一个索引文件,而是在扩展索引的时候不断创建新的索引文 件,然后定期的把这些新的小索引文件合并到原先的大索引中(针对不同的更新策略,批次的大小可以调整),这样在不影响检索的效率的前提下,提高了索引的效 率。
Lucene和其他一些全文检索系统/应用的比较:
| Lucene | 其他开源全文检索系统 |
增量索引和批量索引 | 可以进行增量的索引(Append),可以对于大量数据进行批量索引,并且接口设计用于优化批量索引和小批量的增量索引。 | 很多系统只支持批量的索引,有时数据源有一点增加也需要重建索引。 |
数据源 | Lucene没有定义具体的数据源,而是一个文档的结构,因此可以非常灵活的适应各种应用(只要前端有合适的转换器把数据源转换成相应结构), | 很多系统只针对网页,缺乏其他格式文档的灵活性。 |
索引内容抓取 | Lucene的文档是由多个字段组成的,甚至可以控制那些字段需要进行索引,那些字段不需要索引,近一步索引的字段也分为需要分词和不需要分词的类型: | 缺乏通用性,往往将文档整个索引了 |
语言分析 | 通过语言分析器的不同扩展实现: | 缺乏通用接口实现 |
查询分析 | 通过查询分析接口的实现,可以定制自己的查询语法规则: |
|
并发访问 | 能够支持多用户的使用 |
|
2.4. 对索引过程优化
索引一般分2种情况,一种是小批量的索引扩展,一种是大批量的索引重建。在索引过程中,并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作(文件I/O是一件非常消耗资源的事情)。
Lucene 先在内存中进行索引操作,并根据一定的批量进行文件的写入。这个批次的间隔越大,文件的写入次数越少,但占用内存会很多。反之占用内存少,但文件IO操作 频繁,索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充分利用内存减少文件 的操作。根据我的使用经验:缺省Indexer是每20条记录索引后写入一次,每将MERGE_FACTOR增加50倍,索引速度可以提高1倍左右。
2.5.对搜索过程优化
lucene支持内存索引:这样的搜索比基于文件的I/O有数量级的速度提升。
http://www.onjava.com/lpt/a/3273,而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。
Lucene 面向全文检索的优化在于首次索引检索后,并不把所有的记录(Document)具体内容读取出来,而起只将所有结果中匹配度最高的头100条结果 (TopDocs)的ID放到结果集缓存中并返回,这里可以比较一下数据库检索:如果是一个10,000条的数据库检索结果集,数据库是一定要把所有记录 内容都取得以后再开始返回给应用结果集的。
所以即使检索匹配总数很多,Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应用是用不到这么多的结果的,头100条已经可以满足90%以上的检索需求。
如 果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存,并再重新向后抓取。所以如果构造一个 Searcher去查1-120条结果,Searcher其实是进行了2次搜索过程:头100条取完后,缓存结果用完,Searcher重新检索再构造一 个200条的结果缓存,依此类推,400条缓存,800条缓存。由于每次Searcher对象消失后,这些缓存也访问那不到了,你有可能想将结果记录缓存 下来,缓存数尽量保证在100以下以充分利用首次的结果缓存,不让Lucene浪费多次检索,而且可以分级进行结果缓存。
Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了,过滤过程我们可以通过设置最低的匹配度来进行过滤。这也是和数据库应用需要将搜索的结果全部返回不同之处。
http://blog.chinaunix.net/uid-20648927-id-1907275.html