当前位置 : 主页 > 网络编程 > 其它编程 >

初识Lucene【转】

来源:互联网 收集:自由互联 发布时间:2023-07-02
1lucene简介1.1什么是luceneLucene是一个全文搜索框架而不是应用产品。因此它并不像www.baidu.com或者googleDesktop那么拿来 1 lucene简介 1.1 什么是lucene Lucene是一个全文搜索框架而不是应用产品。
1lucene简介1.1什么是luceneLucene是一个全文搜索框架而不是应用产品。因此它并不像www.baidu.com或者googleDesktop那么拿来 1 lucene简介 1.1 什么是lucene Lucene是一个全文搜索框架而不是应用产品。因此它并不像www.baidu.com 或者google Desktop那么拿来就能用它只是提供了一种工具让你能实现这些产品。 1.2 lucene能做什么 要回答这个问题先要了解lucene的本质。实际上lucene的功能很单一说到底就是你给它若干个字符串然后它为你提供一个全文搜索服务告诉你你要搜索的关键词出现在哪里。知道了这个本质你就可以发挥想象做任何符合这个条件的事情了。你可以把站内新闻都索引了做个资料库你可以把一个数据库表的若干个字段索引起来那就不用再担心因为“%like%”而锁表了你也可以写个自己的搜索引擎…… 1.3 你该不该选择lucene 下面给出一些测试数据如果你觉得可以接受那么可以选择。 测试一250万记录300M左右文本生成索引380M左右800线程下平均处理时间300ms。 测试二37000记录索引数据库中的两个varchar字段索引文件2.6M800线程下平均处理时间1.5ms。 2 lucene的工作方式 lucene提供的服务实际包含两部分一入一出。所谓入是写入即将你提供的源本质是字符串写入索引或者将其从索引中删除所谓出是读出即向用户提供全文搜索服务让用户可以通过关键词定位源。 2.1写入流程 源字符串首先经过analyzer处理包括分词分成一个个单词去除stopword可选。 将源中需要的信息加入Document的各个Field中并把需要索引的Field索引起来把需要存储的Field存储起来。 将索引写入存储器存储器可以是内存或磁盘。 2.2读出流程 用户提供搜索关键词经过analyzer处理。 对处理后的关键词搜索索引找出对应的Document。 用户根据需要从找到的Document中提取需要的Field。 3 一些需要知道的概念 lucene用到一些概念了解它们的含义有利于下面的讲解。 3.1 analyzer Analyzer是分析器它的作用是把一个字符串按某种规则划分成一个个词语并去除其中的无效词语这里说的无效词语是指英文中的“of”、 “the”中文中的“的”、“地”等词语这些词语在文章中大量出现但是本身不包含什么关键信息去掉有利于缩小索引文件、提高效率、提高命中率。 分词的规则千变万化但目的只有一个按语义划分。这点在英文中比较容易实现因为英文本身就是以单词为单位的已经用空格分开而中文则必须以某种方法将连成一片的句子划分成一个个词语。具体划分方法下面再详细介绍这里只需了解分析器的概念即可。 3.2 document 用户提供的源是一条条记录它们可以是文本文件、字符串或者数据库表的一条记录等等。一条记录经过索引之后就是以一个Document的形式存储在索引文件中的。用户进行搜索也是以Document列表的形式返回。 3.3 field 一个Document可以包含多个信息域例如一篇文章可以包含“标题”、“正文”、“最后修改时间”等信息域这些信息域就是通过Field在Document中存储的。 Field有两个属性可选存储和索引。通过存储属性你可以控制是否对这个Field进行存储通过索引属性你可以控制是否对该Field进行索引。这看起来似乎有些废话事实上对这两个属性的正确组合很重要下面举例说明 还是以刚才的文章为例子我们需要对标题和正文进行全文搜索所以我们要把索引属性设置为真同时我们希望能直接从搜索结果中提取文章标题所以我们把标题域的存储属性设置为真但是由于正文域太大了我们为了缩小索引文件大小将正文域的存储属性设置为假当需要时再直接读取文件我们只是希望能从搜索解果中提取最后修改时间不需要对它进行搜索所以我们把最后修改时间域的存储属性设置为真索引属性设置为假。上面的三个域涵盖了两个属性的三种组合还有一种全为假的没有用到事实上Field不允许你那么设置因为既不存储又不索引的域是没有意义的。 3.4 term term是搜索的最小单位它表示文档的一个词语term由两部分组成它表示的词语和这个词语所出现的field。 3.5 tocken tocken是term的一次出现它包含trem文本和相应的起止偏移以及一个类型字符串。一句话中可以出现多次相同的词语它们都用同一个term表示但是用不同的tocken每个tocken标记该词语出现的地方。 3.6 segment 添加索引时并不是每个document都马上添加到同一个索引文件它们首先被写入到不同的小文件然后再合并成一个大索引文件这里每个小文件都是一个segment。  4 lucene的结构 lucene包括core和sandbox两部分其中core是lucene稳定的核心部分sandbox包含了一些附加功能例如highlighter、各种分析器。 Lucene core有七个包analysisdocumentindexqueryParsersearchstoreutil。 4.1 analysis Analysis包含一些内建的分析器例如按空白字符分词的WhitespaceAnalyzer添加了stopwrod过滤的StopAnalyzer最常用的StandardAnalyzer。 4.2 document Document包含文档的数据结构例如Document类定义了存储文档的数据结构Field类定义了Document的一个域。 4.3 index Index包含了索引的读写类例如对索引文件的segment进行写、合并、优化的IndexWriter类和对索引进行读取和删除操作的 IndexReader类这里要注意的是不要被IndexReader这个名字误导以为它是索引文件的读取类实际上删除索引也是由它完成 IndexWriter只关心如何将索引写入一个个segment并将它们合并优化IndexReader则关注索引文件中各个文档的组织形式。 4.4 queryParser QueryParser包含了解析查询语句的类lucene的查询语句和sql语句有点类似有各种保留字按照一定的语法可以组成各种查询。 Lucene有很多种Query类它们都继承自Query执行各种特殊的查询QueryParser的作用就是解析查询语句按顺序调用各种 Query类查找出结果。 4.5 search Search包含了从索引中搜索结果的各种类例如刚才说的各种Query类包括TermQuery、BooleanQuery等就在这个包里。 4.6 store Store包含了索引的存储类例如Directory定义了索引文件的存储结构FSDirectory为存储在文件中的索引RAMDirectory为存储在内存中的索引MmapDirectory为使用内存映射的索引。 4.7 util Util包含一些公共工具类例如时间和字符串之间的转换工具。 5 如何建索引 5.1 最简单的能完成索引的代码片断 IndexWriter writer  new IndexWriter(“/data/index/”, new StandardAnalyzer(), true); Document doc  new Document(); doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED)); doc.add(new Field("content", "lucene works well", Field.Store.YES, Field.Index.TOKENIZED)); writer.addDocument(doc); writer.optimize(); writer.close(); 下面我们分析一下这段代码。 首先我们创建了一个writer并指定存放索引的目录为“/data/index”使用的分析器为StandardAnalyzer第三个参数说明如果已经有索引文件在索引目录下我们将覆盖它们。 然后我们新建一个document。 我们向document添加一个field名字是“title”内容是“lucene introduction”对它进行存储并索引。 再添加一个名字是“content”的field内容是“lucene works well”也是存储并索引。 然后我们将这个文档添加到索引中如果有多个文档可以重复上面的操作创建document并添加。 添加完所有document我们对索引进行优化优化主要是将多个segment合并到一个有利于提高索引速度。 随后将writer关闭这点很重要。 对创建索引就这么简单 当然你可能修改上面的代码获得更具个性化的服务。 5.2 将索引直接写在内存 你需要首先创建一个RAMDirectory并将其传给writer代码如下 Directory dir  new RAMDirectory(); IndexWriter writer  new IndexWriter(dir, new StandardAnalyzer(), true); Document doc  new Document(); doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED)); doc.add(new Field("content", "lucene works well", Field.Store.YES, Field.Index.TOKENIZED)); writer.addDocument(doc); writer.optimize(); writer.close(); 5.3 索引文本文件 如果你想把纯文本文件索引起来而不想自己将它们读入字符串创建field你可以用下面的代码创建field Field field  new Field("content", new FileReader(file)); 这里的file就是该文本文件。该构造函数实际上是读去文件内容并对其进行索引但不存储。 6 如何维护索引 索引的维护操作都是由IndexReader类提供。 6.1 如何删除索引 lucene提供了两种从索引中删除document的方法一种是 void deleteDocument(int docNum) 这种方法是根据document在索引中的编号来删除每个document加进索引后都会有个唯一编号所以根据编号删除是一种精确删除但是这个编号是索引的内部结构一般我们不会知道某个文件的编号到底是几所以用处不大。另一种是 void deleteDocuments(Term term) 这种方法实际上是首先根据参数term执行一个搜索操作然后把搜索到的结果批量删除了。我们可以通过这个方法提供一个严格的查询条件达到删除指定document的目的。 下面给出一个例子 Directory dir  FSDirectory.getDirectory(PATH, false); IndexReader reader  IndexReader.open(dir); Term term  new Term(field, key); reader.deleteDocuments(term); reader.close(); 6.2 如何更新索引 lucene并没有提供专门的索引更新方法我们需要先将相应的document删除然后再将新的document加入索引。例如 Directory dir  FSDirectory.getDirectory(PATH, false); IndexReader reader  IndexReader.open(dir); Term term  new Term(“title”, “lucene introduction”); reader.deleteDocuments(term); reader.close(); IndexWriter writer  new IndexWriter(dir, new StandardAnalyzer(), true); Document doc  new Document(); doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED)); doc.add(new Field("content", "lucene is funny", Field.Store.YES, Field.Index.TOKENIZED)); writer.addDocument(doc); writer.optimize(); writer.close(); 7 如何搜索 lucene的搜索相当强大它提供了很多辅助查询类每个类都继承自Query类各自完成一种特殊的查询你可以像搭积木一样将它们任意组合使用完成一些复杂操作另外lucene还提供了Sort类对结果进行排序提供了Filter类对查询条件进行限制。你或许会不自觉地拿它跟SQL语句进行比较“lucene能执行and、or、order by、where、like ‘%xx%’操作吗”回答是“当然没问题” 7.1 各种各样的Query 下面我们看看lucene到底允许我们进行哪些查询操作 7.1.1 TermQuery 首先介绍最基本的查询如果你想执行一个这样的查询“在content域中包含‘lucene’的document”那么你可以用TermQuery Term t  new Term("content", " lucene"; Query query  new TermQuery(t); 7.1.2 BooleanQuery 如果你想这么查询“在content域中包含java或perl的document”那么你可以建立两个TermQuery并把它们用BooleanQuery连接起来 TermQuery termQuery1  new TermQuery(new Term("content", "java"); TermQuery termQuery 2  new TermQuery(new Term("content", "perl"); BooleanQuery booleanQuery  new BooleanQuery(); booleanQuery.add(termQuery 1, BooleanClause.Occur.SHOULD); booleanQuery.add(termQuery 2, BooleanClause.Occur.SHOULD); 7.1.3 WildcardQuery 如果你想对某单词进行通配符查询你可以用WildcardQuery通配符包括’?’匹配一个任意字符和’*’匹配零个或多个任意字符例如你搜索’use*’你可能找到’useful’或者’useless’ Query query  new WildcardQuery(new Term("content", "use*"); 7.1.4 PhraseQuery 你可能对中日关系比较感兴趣想查找‘中’和‘日’挨得比较近5个字的距离内的文章超过这个距离的不予考虑你可以 PhraseQuery query  new PhraseQuery(); query.setSlop(5); query.add(new Term("content ", “中”)); query.add(new Term(“content”, “日”)); 那么它可能搜到“中日合作……”、“中方和日方……”但是搜不到“中国某高层领导说日本欠扁”。 7.1.5 PrefixQuery 如果你想搜以‘中’开头的词语你可以用PrefixQuery PrefixQuery query  new PrefixQuery(new Term("content ", "中"); 7.1.6 FuzzyQuery FuzzyQuery用来搜索相似的term使用Levenshtein算法。假设你想搜索跟‘wuzza’相似的词语你可以 Query query  new FuzzyQuery(new Term("content", "wuzza"); 你可能得到‘fuzzy’和‘wuzzy’。 7.1.7 RangeQuery 另一个常用的Query是RangeQuery你也许想搜索时间域从20060101到20060130之间的document你可以用RangeQuery RangeQuery query  new RangeQuery(new Term(“time”, “20060101”), new Term(“time”, “20060130”), true); 最后的true表示用闭合区间。 7.2 QueryParser 看了这么多Query你可能会问“不会让我自己组合各种Query吧太麻烦了”当然不会lucene提供了一种类似于SQL语句的查询语句我们姑且叫它lucene语句通过它你可以把各种查询一句话搞定lucene会自动把它们查分成小块交给相应Query执行。下面我们对应每种Query演示一下 TermQuery可以用“field:key”方式例如“content:lucene”。 BooleanQuery中‘与’用‘’‘或’用‘ ’例如“content:java contenterl”。 WildcardQuery仍然用‘?’和‘*’例如“content:use*”。 PhraseQuery用‘~’例如“content:"中日"~5”。 PrefixQuery用‘*’例如“中*”。 FuzzyQuery用‘~’例如“content: wuzza ~”。 RangeQuery用‘[]’或‘{}’前者表示闭区间后者表示开区间例如“time:[20060101 TO 20060130]”注意TO区分大小写。 你可以任意组合query string完成复杂操作例如“标题或正文包括lucene并且时间在20060101到20060130之间的文章”可以表示为“ (title:lucene content:lucene) time:[20060101 TO 20060130]”。代码如下 Directory dir  FSDirectory.getDirectory(PATH, false); IndexSearcher is  new IndexSearcher(dir); QueryParser parser  new QueryParser("content", new StandardAnalyzer()); Query query  parser.parse("(title:lucene content:lucene) time:[20060101 TO 20060130]"; Hits hits  is.search(query); for (int i  0; i < hits.length(); i) { Document doc  hits.doc(i);System.out.println(doc.get("title"); } is.close(); 首先我们创建一个在指定文件目录上的IndexSearcher。 然后创建一个使用StandardAnalyzer作为分析器的QueryParser它默认搜索的域是content。 接着我们用QueryParser来parse查询字串生成一个Query。 然后利用这个Query去查找结果结果以Hits的形式返回。 这个Hits对象包含一个列表我们挨个把它的内容显示出来。 7.3 Filterfilter的作用就是限制只查询索引的某个子集它的作用有点像SQL语句里的where但又有区别它不是正规查询的一部分只是对数据源进行预处理然后交给查询语句。注意它执行的是预处理而不是对查询结果进行过滤所以使用filter的代价是很大的它可能会使一次查询耗时提高一百倍。 最常用的filter是RangeFilter和QueryFilter。RangeFilter是设定只搜索指定范围内的索引QueryFilter是在上次查询的结果中搜索。 Filter的使用非常简单你只需创建一个filter实例然后把它传给searcher。继续上面的例子查询“时间在20060101到20060130之间的文章”除了将限制写在query string中你还可以写在RangeFilter中Directory dir  FSDirectory.getDirectory(PATH, false);IndexSearcher is  new IndexSearcher(dir); QueryParser parser  new QueryParser("content", new StandardAnalyzer()); Query query  parser.parse("title:lucene content:lucene"; RangeFilter filter  new RangeFilter("time", "20060101", "20060230", true, true); Hits hits  is.search(query, filter); for (int i i < hits.length(); i) { Document doc  hits.doc(i);System.out.println(doc.get("title"); } is.close();7.4 Sort 有时你想要一个排好序的结果集就像SQL语句的“order by”lucene能做到通过Sort。 Sort sort Sort(“time”); //相当于SQL的“order by time” Sort sort  new Sort(“time”, true); // 相当于SQL的“order by time desc” 下面是一个完整的例子 Directory dir  FSDirectory.getDirectory(PATH, false); IndexSearcher is  new IndexSearcher(dir); QueryParser parser  new QueryParser("content", new StandardAnalyzer()); Query query  parser.parse("title:lucene content:lucene"; RangeFilter filter  new RangeFilter("time", "20060101", "20060230", true, true); Sort sort  new Sort(“time”); Hits hits  is.search(query, filter, sort); for (int i  0; i < hits.length(); i) { Document doc  hits.doc(i);System.out.println(doc.get("title"); } is.close(); 8 分析器 在前面的概念介绍中我们已经知道了分析器的作用就是把句子按照语义切分成一个个词语。英文切分已经有了很成熟的分析器 StandardAnalyzer很多情况下StandardAnalyzer是个不错的选择。甚至你会发现StandardAnalyzer也能对中文进行分词。 但是我们的焦点是中文分词StandardAnalyzer能支持中文分词吗实践证明是可以的但是效果并不好搜索“如果”会把“牛奶不如果汁好喝”也搜索出来而且索引文件很大。那么我们手头上还有什么分析器可以使用呢core里面没有我们可以在sandbox里面找到两个 ChineseAnalyzer和CJKAnalyzer。但是它们同样都有分词不准的问题。相比之下用StandardAnalyzer和 ChineseAnalyzer建立索引时间差不多索引文件大小也差不多CJKAnalyzer表现会差些索引文件大且耗时比较长。 要解决问题首先分析一下这三个分析器的分词方式。StandardAnalyzer和ChineseAnalyzer都是把句子按单个字切分也就是说 “牛奶不如果汁好喝”会被它们切分成“牛 奶 不 如 果 汁 好 喝”而CJKAnalyzer则会切分成“牛奶 奶不 不如 如果 果汁 汁好好喝”。这也就解释了为什么搜索“果汁”都能匹配这个句子。 以上分词的缺点至少有两个匹配不准确和索引文件大。我们的目标是将上面的句子分解成“牛奶 不如 果汁好喝”。这里的关键就是语义识别我们如何识别“牛奶”是一个词而“奶不”不是词语我们很自然会想到基于词库的分词法也就是我们先得到一个词库里面列举了大部分词语我们把句子按某种方式切分当得到的词语与词库中的项匹配时我们就认为这种切分是正确的。这样切词的过程就转变成匹配的过程而匹配的方式最简单的有正向最大匹配和逆向最大匹配两种说白了就是一个从句子开头向后进行匹配一个从句子末尾向前进行匹配。基于词库的分词词库非常重要词库的容量直接影响搜索结果在相同词库的前提下据说逆向最大匹配优于正向最大匹配。 当然还有别的分词方法这本身就是一个学科我这里也没有深入研究。回到具体应用我们的目标是能找到成熟的、现成的分词工具避免重新发明车轮。经过网上搜索用的比较多的是中科院的ICTCLAS和一个不开放源码但是免费的JE-Analysis。ICTCLAS有个问题是它是一个动态链接库 java调用需要本地方法调用不方便也有安全隐患而且口碑也确实不大好。JE-Analysis效果还不错当然也会有分词不准的地方相比比较方便放心。 new  0;  9 性能优化 一直到这里我们还是在讨论怎么样使lucene跑起来完成指定任务。利用前面说的也确实能完成大部分功能。但是测试表明lucene的性能并不是很好在大数据量大并发的条件下甚至会有半分钟返回的情况。另外大数据量的数据初始化建立索引也是一个十分耗时的过程。那么如何提高lucene的性能呢下面从优化创建索引性能和优化搜索性能两方面介绍。 9.1 优化创建索引性能 这方面的优化途径比较有限IndexWriter提供了一些接口可以控制建立索引的操作另外我们可以先将索引写入RAMDirectory再批量写入FSDirectory不管怎样目的都是尽量少的文件IO因为创建索引的最大瓶颈在于磁盘IO。另外选择一个较好的分析器也能提高一些性能。 9.1.1 通过设置IndexWriter的参数优化索引建立 setMaxBufferedDocs(int maxBufferedDocs) 控制写入一个新的segment前内存中保存的document的数目设置较大的数目可以加快建索引速度默认为10。 setMaxMergeDocs(int maxMergeDocs) 控制一个segment中可以保存的最大document数目值较小有利于追加索引的速度默认Integer.MAX_VALUE无需修改。 setMergeFactor(int mergeFactor) 控制多个segment合并的频率值较大时建立索引速度较快默认是10可以在建立索引时设置为100。 9.1.2 通过RAMDirectory缓写提高性能 我们可以先把索引写入RAMDirectory达到一定数量时再批量写进FSDirectory减少磁盘IO次数。 FSDirectory fsDir  FSDirectory.getDirectory("/data/index", true); RAMDirectory ramDir  new RAMDirectory(); IndexWriter fsWriter  new IndexWriter(fsDir, new StandardAnalyzer(), true); IndexWriter ramWriter  new IndexWriter(ramDir, new StandardAnalyzer(), true); while (there are documents to index) {... create Document ... ramWriter.addDocument(doc); if (condition for flushing memory to disk has been met) { fsWriter.addIndexes(new Directory[] { ramDir }); ramWriter.close(); ramWriter  new IndexWriter(ramDir, new StandardAnalyzer(), true); } }9.1.3 选择较好的分析器 这个优化主要是对磁盘空间的优化可以将索引文件减小将近一半相同测试数据下由600M减少到380M。但是对时间并没有什么帮助甚至会需要更长时间因为较好的分析器需要匹配词库会消耗更多cpu测试数据用StandardAnalyzer耗时133分钟用MMAnalyzer耗时150分钟。 9.2 优化搜索性能 虽然建立索引的操作非常耗时但是那毕竟只在最初创建时才需要平时只是少量的维护操作更何况这些可以放到一个后台进程处理并不影响用户搜索。我们创建索引的目的就是给用户搜索所以搜索的性能才是我们最关心的。下面就来探讨一下如何提高搜索性能。 9.2.1 将索引放入内存 这是一个最直观的想法因为内存比磁盘快很多。Lucene提供了RAMDirectory可以在内存中容纳索引 Directory fsDir  FSDirectory.getDirectory(“/data/index/”, false); Directory ramDir  new RAMDirectory(fsDir); Searcher searcher  new IndexSearcher(ramDir); 但是实践证明RAMDirectory和FSDirectory速度差不多当数据量很小时两者都非常快当数据量较大时索引文件400MRAMDirectory甚至比FSDirectory还要慢一点这确实让人出乎意料。 而且lucene的搜索非常耗内存即使将400M的索引文件载入内存在运行一段时间后都会out of memory所以个人认为载入内存的作用并不大。9.2.2 优化时间范围限制 既然载入内存并不能提高效率一定有其它瓶颈经过测试发现最大的瓶颈居然是时间范围限制那么我们可以怎样使时间范围限制的代价最小呢 当需要搜索指定时间范围内的结果时可以 1、用RangeQuery设置范围但是RangeQuery的实现实际上是将时间范围内的时间点展开组成一个个BooleanClause加入到 BooleanQuery中查询因此时间范围不可能设置太大经测试范围超过一个月就会抛BooleanQuery.TooManyClauses可以通过设置 BooleanQuery.setMaxClauseCount(int maxClauseCount)扩大但是扩大也是有限的并且随着maxClauseCount扩大占用内存也扩大 2、用RangeFilter代替RangeQuery经测试速度不会比RangeQuery慢但是仍然有性能瓶颈查询的90%以上时间耗费在 RangeFilter研究其源码发现RangeFilter实际上是首先遍历所有索引生成一个BitSet标记每个document在时间范围内的标记为true不在的标记为false然后将结果传递给Searcher查找这是十分耗时的。 3、进一步提高性能这个又有两个思路 a、缓存Filter结果。既然RangeFilter的执行是在搜索之前那么它的输入都是一定的就是IndexReader而 IndexReader是由Directory决定的所以可以认为RangeFilter的结果是由范围的上下限决定的也就是由具体的 RangeFilter对象决定所以我们只要以RangeFilter对象为键将filter结果BitSet缓存起来即可。lucene API已经提供了一个CachingWrapperFilter类封装了Filter及其结果所以具体实施起来我们可以cache CachingWrapperFilter对象需要注意的是不要被CachingWrapperFilter的名字及其说明误导 CachingWrapperFilter看起来是有缓存功能但的缓存是针对同一个filter的也就是在你用同一个filter过滤不同 IndexReader时它可以帮你缓存不同IndexReader的结果而我们的需求恰恰相反我们是用不同filter过滤同一个 IndexReader所以只能把它作为一个封装类。 b、降低时间精度。研究Filter的工作原理可以看出它每次工作都是遍历整个索引的所以时间粒度越大对比越快搜索时间越短在不影响功能的情况下时间精度越低越好有时甚至牺牲一点精度也值得当然最好的情况是根本不作时间限制。 下面针对上面的两个思路演示一下优化结果都采用800线程随机关键词随即时间范围 第一组时间精度为秒 方式 直接用RangeFilter 使用cache 不用filter 平均每个线程耗时 10s 1s 300ms 第二组时间精度为天 方式 直接用RangeFilter 使用cache 不用filter 平均每个线程耗时 900ms 360ms 300ms 由以上数据可以得出结论 1、 尽量降低时间精度将精度由秒换成天带来的性能提高甚至比使用cache还好最好不使用filter。 2、 在不能降低时间精度的情况下使用cache能带了10倍左右的性能提高。 9.2.3 使用更好的分析器 这个跟创建索引优化道理差不多索引文件小了搜索自然会加快。当然这个提高也是有限的。较好的分析器相对于最差的分析器对性能的提升在20%以下。10 一些经验 10.1关键词区分大小写 or AND TO等关键词是区分大小写的lucene只认大写的小写的当做普通单词。 10.2 读写互斥性 同一时刻只能有一个对索引的写操作在写的同时可以进行搜索 10.3 文件锁 在写索引的过程中强行退出将在tmp目录留下一个lock文件使以后的写操作无法进行可以将其手工删除 10.4 时间格式 lucene只支持一种时间格式yyMMddHHmmss所以你传一个yy-MM-dd HH:mm:ss的时间给lucene它是不会当作时间来处理的 10.5 设置boost 有些时候在搜索时某个字段的权重需要大一些例如你可能认为标题中出现关键词的文章比正文中出现关键词的文章更有价值你可以把标题的boost设置的更大那么搜索结果会优先显示标题中出现关键词的文章没有使用排序的前题下。使用方法 Field. setBoost(float boost);默认值是1.0也就是说要增加权重的需要设置得比1大。【文章原创作者:武汉网页开发 http://www.1234xp.com/wuhan.html 欢迎留下您的宝贵建议】
上一篇:AI与人类围棋士的差距到底有多大
下一篇:没有了
网友评论