posts - 48, comments - 900, trackbacks - 14, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

Lucene.Net RangeQuery 效率确实低下

Posted on 2008-11-03 12:07 eaglet 阅读(381) 评论(7)  编辑 收藏 网摘 所属分类: 搜索引擎Lucene

Lucene.Net RangeQuery 效率确实低下

 

 很多文章提到 Lucene.Net RangeQuery 的查询效率非常低下,我今天测试了一下,果然非常低下,而且结果也不正确。

 测试 代码:

索引


        public void Index(int count)
        
{
            IndexWriter writer 
= new IndexWriter(INDEX_DIR, new Lucene.Net.Analysis.SimpleAnalyzer(), true);

            _Count 
= count;

            Document doc 
= new Document();

            
for (int i = 0; i < count; i++)
            
{
                
if (IndexProgress != null)
                
{
                    IndexProgress(i);
                }


                
string iStr = string.Format("{0:00000}", i);
                Field field 
= new Field("Id", iStr, Field.Store.YES, Field.Index.UN_TOKENIZED);
                doc.Add(field);
                field 
= new Field("Text""Test " + i.ToString(), Field.Store.YES, Field.Index.TOKENIZED);
                doc.Add(field);
                writer.AddDocument(doc);
            }


            
if (IndexProgress != null)
            
{
                IndexProgress(count);
            }


            writer.Optimize();
            writer.Close();

        }


 查找

 

        public string Search(int begin, int end)
        
{
            IndexSearcher search 
= new IndexSearcher(INDEX_DIR);

            
try
            
{
                
string bStr = string.Format("{0:00000}", begin);
                
string eStr = string.Format("{0:00000}", end);

                RangeQuery query 
= new RangeQuery(new Term("Id", bStr), new Term("Id", eStr), true);
                
//QueryParser qp = new QueryParser("Text", new Lucene.Net.Analysis.SimpleAnalyzer());
                
//Query q = qp.Parse("Test");
                
//BooleanQuery bq = new BooleanQuery();
                
//bq.Add(query, BooleanClause.Occur.MUST);
                
//bq.Add(q, BooleanClause.Occur.MUST);

                _StopWatch.Reset();
                _StopWatch.Start();
                Hits hits 
= search.Search(query);
                
int retCount = hits.Length();
                _StopWatch.Stop();

            }

            
catch(Exception e)
            
{
                
return e.Message;
            }

            
finally
            
{
                search.Close();
            }


            StringBuilder report 
= new StringBuilder();

            report.AppendLine(
"**************TestRange Report******************");

            report.AppendFormat(
"Index count = {0}\r\n", _Count);
            report.AppendFormat(
"Begin {0} to {1}\r\n", begin, end);
            report.AppendFormat(
"ElapsedMilliseconds = {0}\r\n", ElapsedMilliseconds);

            report.AppendLine(
"**************End Report************************");
            
return report.ToString();
        }

 

 这段代码 我尝试插入3000条记录

 搜索 0-1000条记录耗时 2秒多,如果搜索 0-2000条记录,lucene 会报错。

 得到的结果也有问题,虽然  hits.HitDocs 的长度为1000,但hits.Length()的大小却是3000.

而hits.HitDocs 是私有成员,根本无法取出。

范围超过2000报错是因为clause的数量超过2000,而这个数量默认的最大值是2000.

从这个结果我大概判断,Lucene在进行范围查找的时候,并不是利用传统的B+树或者类似的算法计算范围,而是用全文的方法

计算范围,找到Score 值明显较大的记录,于是在查找00000 - 001000 的时候就产生 1001个Clause ,

分别是"00000", "00001", ..."001000", 用这1001个Clause

到全文索引中一一匹配得到Score值大的记录,然后输出。

Lucene 搜索过程的代码如下:

            if (hitDocs.Count > min)
            
{
                min 
= hitDocs.Count;
            }

            
            
int n = min * 2// double # retrieved
            TopDocs topDocs = (sort == null? searcher.Search(weight, filter, n) : searcher.Search(weight, filter, n, sort);
            length 
= topDocs.totalHits;
            ScoreDoc[] scoreDocs 
= topDocs.scoreDocs;
            
            
float scoreNorm = 1.0f;
            
            
if (length > 0 && topDocs.GetMaxScore() > 1.0f)
            
{
                scoreNorm 
= 1.0f / topDocs.GetMaxScore();
            }

            
            
int end = scoreDocs.Length < length?scoreDocs.Length:length;
            
for (int i = hitDocs.Count; i < end; i++)
            
{
                hitDocs.Add(
new HitDoc(scoreDocs[i].score * scoreNorm, scoreDocs[i].doc));
            }

 

 从这里我们可以看出length 被赋值为 topDocs.totalHits; 而不是topDocs.scoreDocs.Count

而这个 topDocs.totalHits的值始终是3000.也就是索引文件的记录总数。

排除结果不对的问题不说,这种算法的效率也实在太低,而且限制你输出记录总数的范围只能在2000个记录以内,输出

的范围超过2000个就无法查询(虽然你可以人为提高Clause的上限,但这将导致系统开销极大的增加)

 

对于垂直搜索来说,这种范围结合全文的查询是非常普遍的,比如我们需要查某个名字的书名,且价格在某个范围内。

这种需求用 lucene实现,基本就不可能了。

这个测试使我对lucene的商业化应用的前景产生了怀疑,我打算调整Hubble.Net 的设计和研发计划,优先考虑部分或

全部替换lucene。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Feedback

#1楼    回复  引用  查看    

2008-11-03 19:20 by Kevin Li      
赞一下楼主的钻研精神,避免后来者走弯路

#2楼    回复  引用  查看    

2008-11-09 12:44 by Jason Cui      
是Lucene的问题还是Lucene.net的问题?我现在把搜索全部换成了solr,效果很不错,而且范围查询也没有发现什么问题。

#3楼 [楼主]   回复  引用  查看    

2008-11-09 17:10 by eaglet      
Lucene和 Lucene.net 算法是一样的,你用的solr 是基于lucene的,也应该有这个问题。你的查询范围具体是怎么做的?是不是数值比较小? Lucene 范围查询效率低下的问题并不是我说的,Lucene In Action 那本书里面也讨论过这个问题

#4楼    回复  引用  查看    

2008-11-09 17:24 by Jason Cui      
我用日期,范围是一年内的,占到所有文章总数(50万)的四分之一。没觉得慢。

#5楼 [楼主]   回复  引用  查看    

2008-11-09 17:40 by eaglet      
你可以看这篇
http://www.oss-home.com/articles/200810/2090310903.shtml

Lucene 在做范围查询时实际上是将范围展开,然后当作关键字进行查询。
比如 你如果想查 1990 年 到 1999 年的记录,如果按年来查,那么将展开成
1990,1991...1999 10个关键词,然后用这10个关键词去匹配。但如果你精确到日,那么就是3万6千多个关键词。
lucene 在处理时间的范围时还勉强可以胜任,但如果处理整数的范围查询,就很困难,如果你查0 到100万之间的记录,你总不能把 0 到 1百万全部展开吧?而且0到100万在整数区间中还是很小的一段。
所以 Lucene 在处理范围查询时肯定是有问题的,你处理范围没有问题很可能是你并没有踩到它性能瓶颈的红线上。

#6楼    回复  引用  查看    

2008-11-09 18:00 by Jason Cui      
持怀疑态度。我的文章的时间和查询的时候都是到秒的。没有这个问题。

#7楼 [楼主]   回复  引用  查看    

2008-11-09 18:47 by eaglet      
Lucene RangeQuery 的原理肯定是我说的那样,我也去跟过Lucene的程序,就是这么做的。你可以再看看下面这篇
http://blog.csdn.net/pwlazy/archive/2006/12/07/1433774.aspx

你的没有问题,肯定是有原因的,由于我没有看到你的代码,我也无法推测,你有兴趣可以跟到Lucene代码里面去看看,你看看你每次搜索时到底产生了多少个
Clauses,如果这个值很小,那效率肯定比较高,然后你再分析分析这个值小的原因,就知道你的查询为什么会没有出问题了。

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-11-03 12:29 编辑过
Google站内搜索
[推荐职位]上海盛大网络招聘架构师



China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
近千种 9-95 新二手计算图书火热销售中!
开发者征途系统新作:《设计模式——基于C#的工程化实现及扩展》

相关文章:


相关搜索:
Lucene RangeQuery 测试

相关链接: