tim-wu

2008年2月6日

Lucene的索引结构图

反向索引:

正向索引(草稿,不完全,因为收到field info的影响,不同的field存储内容不同,且fieldInfo的有些信息,TOKENIZED BINARY COMPRESSED也是保存在.fdt的每个document相关段的bits中,而不是.fnm中):
posted @ 2008-02-27 18:14 鹏飞万里 阅读(1589) | 评论 (0) | 编辑 收藏
 
Lucene和GCJ
Lucene在1.9版本的时候就已经加入了对GCJ的支持,利用GCJ编译Lucene,并且使用新的GCJIndexInput.java读写文件系统,
直接调用操作系统级别的native方法,相信读写性能能够极大得提高啊。

具体代码可见Lucene的gcj目录,编译使用ant gcj
posted @ 2008-02-14 15:27 鹏飞万里 阅读(454) | 评论 (0) | 编辑 收藏
 
备忘:lucene中的ranking算法

说明见Similarity.java的javadoc信息:


算法请参考javadoc的,它使用的是Vector Space Model (VSM) of Information Retrieval。

                针对一条查询语句q(query),一个d(document)的得分公式
score(q,d)   =   coord(q,d)  ·  queryNorm(q)  ·  ∑ ( tf(t in d)  ·  idf(t)2  ·  t.getBoost() ·  norm(t,d) )
t in q
               其中,
               tf(t in d) 表示某个term的出现频率,定义了term t出现在当前地document d的次数。 那些query中给定地term,如果出现越多次的,得分越高。它在默认实现DefaultSimilarity的公式为
tf(t in d)   =   frequency½
               idf(t) 表示反向文档频率。这个参数表示docFreq(term t一共在多少个文档中出现)的反向影响值。它意味着在越少文档中出现的terms贡献越高地分数。它在默认实现DefaultSimilarity的公式为:
idf(t)  =   1 + log (
numDocs
–––––––––
docFreq+1
)
                coord(q,d) 是一个基于在该文档中出现了多少个query中的terms的得分因素。文档中出现的query中的terms数量/query总共多少个query数量。典型的,一个文档包含越多地query中的terms会得到更高地分。This is a search time factor computed in coord(q,d) by the Similarity in effect at search time. 
                queryNorm(q) 是一个标准化参数,它是用来区分比较不同queries时的因素,这个因素不影响document ranking (因为所有的ranked document都会乘以相同的值),但是不同地queries(或这不同地indexes中)它会得到不同的可用于比较的值。This is a search time factor computed by the Similarity in effect at search time. 它在默认实现DefaultSimilarity的公式为:
queryNorm(q)   =   queryNorm(sumOfSquaredWeights)   =  
1
––––––––––––––
sumOfSquaredWeights½
                其中的sumOfSquaredWeights(of the query terms)是根据the query Weight object计算出来的. For example, a boolean query computes this value as:
sumOfSquaredWeights   =   q.getBoost() 2  ·  ∑ ( idf(t)  ·  t.getBoost() ) 2
t in q
 
                t.getBoost() 是一个term t在query q中的search time boost, 它是在the query text (see query syntax)中指定的, 或者被应用程序直接调用setBoost()设置的. 注意,这儿没有直接的API去访问在 a multi term query的一个term的boost值,但是multi terms会以multi TermQuery objects在一个query中被表示,因此the boost of a term in the query可以使用子query的getBoost()反问到. 
                norm(t,d) 封装(encapsulates)了一些(indexing time)的boost和length factors:  ???这个参数之和field中tokens的数量有关,和term本身无关???
                          Document boost - set by calling doc.setBoost() before adding the document to the index.
                          Field boost - set by calling field.setBoost() before adding the field to a document.
                          lengthNorm(field) -。当文档被加入到索引时计算,,和document的field中的tokens的数量有关,因此,一个比较短的fields贡献更高的分数。LengthNorm  is computed by the Similarity class in effect at indexing. DefaultSimilarity中的实现为(float)(1.0 / Math.sqrt(numTerms));
                    当一个文档被加入索引时,上述因素会被相乘。如果文档有多个fields同名,他们的boosts数值会被多次相乘。
 
 
norm(t,d)   =   doc.getBoost()  ·  lengthNorm(field)  ·  ∏ f.getBoost()
field f in d named as t
                     但是,计算出的norm数值在存储时是使用一个a single byte编码的。search时,这个norm byte从index directory读取,并且被解码回float。这个编码/解码算法会产生精度丢失。 - it is not guaranteed that decode(encode(x)) = x. For instance, decode(encode(0.89)) = 0.75. Also notice that search time is too late to modify this norm part of scoring, e.g. by using a different Similarity for search. 
posted @ 2008-02-09 17:58 鹏飞万里 阅读(1867) | 评论 (0) | 编辑 收藏
 
Lucene如何控制segments的数量?
Lucene的索引文件,会包含很多个segments文件,每个segment中包含多个documents文件,一个segment中会有完整的正向索引和反向索引。
在搜索时,Lucene会遍历这些segments,以segments为基本单位独立搜索每个segments文件,而后再把搜索结果合并。

建立索引文件的过程,实际就是把documents文件一个个加入索引中,Lucene的做法是最开始为每个新加入的document独立生成一个segment,放在内存中。而后,当内存中segments数量到达一个阙值时,合并这些segments,新生成一个segment加入文件系统的segments列表中。
而当文件系统的segments数量过多时,势必影响搜索效率,因此需要不断合并这些segments文件。

那么Lucene的合并策略是什么?如何保证合适的segments数量呢?

其实Lucene有两套基本的策略:
第一种策略实现代码位于IndexWriter#optimize()函数,其实就是把所有segments文件合并成一个文件。合并的算法是递归合并列表最后的mergeFactor个segments文件直到合并成一个文件。这儿mergeFactor是Lucene的一个参数。
btw: 从算法细节上看,其实我不是喜欢这段实现,因为列表的最后mergeFactor个文件内容实际被扫描了segmens_count/mergeFactor次。如果分段归纳合并的方式不知道是否更好,每个segment文件内容都将被扫描 ceil(Log_mergeFactor(segmens_count)) 或ceil(Log_mergeFactor(segmens_count)) +1次,是否更好?

第二种策略实现代码位于IndexWriter#maybeMergeSegments()函数中,这个代码就复杂了,它的基本策略就是要求确保合并后两个公式的成立:
假定segments是个有序列表,B表示maxBufferedDocs,f(n)=ceil(log_M(ceil(n/B))),M表示mergeFactor,这儿maxBufferedDocs和mergeFactor是两个参数
1. 如果第i个segment的documents数量为x,第i+1个segment的documents数量为y,那么f(x)>f(y)一定成立
2. f(n)值相同的segments的数量不得超过M。
那么maybeMergeSegments()函数是如何确保这两个公式成立的呢?
1.首先,从代码:
    protected final void maybeFlushRamSegments() throws IOException {
        
// A flush is triggered if enough new documents are buffered or
        
// if enough delete terms are buffered
        if (ramSegmentInfos.size() >= minMergeDocs
                
|| numBufferedDeleteTerms >= maxBufferedDeleteTerms) {
            flushRamSegments();
        }
    }
这儿minMergeDocs=maxBufferedDocs, 因此可以看出,当内存中缓存的segments被合并写回磁盘时生成的segment的document count等于或小于maxBufferedDocs(即minMergeDocs)。
补充:因为maybeMergeSegments()运行在同步代码中,因此只要ramSegmentInfos.size==minMergerDocs(即maxBufferedDocs)就会写回磁盘,因此应该不存在ramSegmentInfos.size>maxBufferedDocs才写回的情况。而且,但如果是这种情况,因为合并后的segment文件的document count过大,后面的maybeMergeSegments()将不做合并处理直接退出,上述公式就可能不成立,那么算法将有错。
----
2.
2.1 因此maybeMergeSegments()第一次执行时,所有segments的document count都小于等于maxBufferedDocs。此时,从i=0开始,合并i~i+mergeFactor-1个文件,如果合并后的doc count>maxBufferedDocs时,保留第i个segment,否则继续合并改变后的i~i+mergeFactor-1,直到doc count>maxBufferedDocs或所有segments文件个数已经<mergeFactor了。经过这样一轮的合并,除了最后<mergeFactor个的doc counts<=maxBufferedDocs文件外,其它文件的doc counts一定都>maxBufferedDocs并<maxBufferedDocs*mergeFactor了。
 2.2 这时,不理会最后<mergeFactor个doc count<maxBufferedDocs的文件,而后按2.1的同理规则,合并之前的文件,让这些文件的最后<mergerFactor个segment符合 maxBufferedDocs<doc counts<=maxBufferedDocs*mergeFactor,之前的segment文件都符合maxBufferedDocs*mergeFactor<doc counts<=maxBufferedDocs*mergeFactor^2
2.3 重复2.2,最后得到的列表就会满足上述两等式的成立
---
3
之后,每次从内存缓存中flush出一个新的segment时,也就是往这个segments列表的最后增加一个doc_count<=maxBufferedDocs的文件,同样执行上述步骤2进行合并,能够始终保证上述两公式的成立。
----
4
4.1
IndexWriter#addIndexesNoOptimize同样借鉴使用了maybeMergeSegments()算法,区别此时,实际是已有一个符合两公式的segments序列T,在T之后追加上随意顺序的segments序列S。这时,我们先找到S中doc count值最大的那个segment,计算出它属于的区间f(x),使得maxBufferedDocs*mergeFactor^x<doc counts<=maxBufferedDocs*mergeFactor^(x+1),而后按2.2的算法合并出除了最后<mergerFactor个segments外, 之前所有segments都符合 a. doc count>maxBufferedDocs*mergeFactor^(x+1) b.符合上述2等式。
btw: 因为这儿调用IndexWriter#addIndexesNoOptimize传入的参数是maxBufferedDocs*mergeFactor^(x+1),因为S所有segment的doc count都一定小于maxBufferedDocs*mergeFactor^(x+1),因此S的所有元素都会参与收缩合并。
4.2 将最后<mergerFactor个doc count<maxBufferedDocs*mergeFactor^(x+1)的segments合并,如果合并后的segment的doc count大于maxBufferedDocs*mergeFactor^(x+1),就继续2.2步骤,使得整个队列符合上述两公式
-----

上述两种策略,最终确保了Lucene中的segments不会太多,确保效率。

BTW:实际上,如果documents太多时,Lucene还支持把docuements分成几个组,每个组用独立的进程或电脑进行索引,而后再多个目录的索引合并起来,具体可参考IndexWriter#addIndexesNoOptimize和IndexWriter#addIndexes函数
posted @ 2008-02-06 01:58 鹏飞万里 阅读(4679) | 评论 (3) | 编辑 收藏
 
 
<2008年2月>
日一二三四五六
272829303112
3456789
10111213141516
17181920212223
2425262728291
2345678

 导航

  • BlogJava
  • 首页
  • 发新随笔
  • 发新文章
  • 联系
  • 聚合
  • 管理

 统计

  • 随笔: 28
  • 文章: 0
  • 评论: 38
  • 引用: 1

常用链接

  • 我的随笔
  • 我的评论
  • 我的参与
  • 最新评论

留言簿(4)

  • 给我留言
  • 查看公开留言
  • 查看私人留言

我参与的团队

  • WebGIS开发设计组(0/0)

随笔档案

  • 2008年2月 (4)
  • 2008年1月 (6)
  • 2007年12月 (2)
  • 2007年11月 (2)
  • 2007年9月 (8)
  • 2007年3月 (1)
  • 2006年5月 (4)
  • 2006年4月 (1)

搜索

  •  

最新评论

  • 1. re: 关于Javascript的内存泄漏问题的整理稿
  • 很有帮助!谢谢
  • --selma
  • 2. re: 关于Javascript的内存泄漏问题的整理稿
  • 对象是值传递的,所以第一段代码不可能是js对象和dom对象的循环引用,而是dom自身的循环引用
  • --axiheyhey
  • 3. re: 关于Javascript的内存泄漏问题的整理稿[未登录]
  • 评论内容较长,点击标题查看
  • --鹏飞万里
  • 4. re: 关于Javascript的内存泄漏问题的整理稿
  • 评论内容较长,点击标题查看
  • --goofect
  • 5. re: 关于Javascript的内存泄漏问题的整理稿
  • 评论内容较长,点击标题查看
  • --goofect

阅读排行榜

  • 1. 关于Javascript的内存泄漏问题的整理稿(18615)
  • 2. 关于google map api中的球平投影算法接口: GProjection和GMercatorProjection类(4825)
  • 3. Lucene如何控制segments的数量?(4679)
  • 4. javascript的prototype(2525)
  • 5. Java7 VB2008都开始支持Lambda(Closure)了(2366)

评论排行榜

  • 1. 关于Javascript的内存泄漏问题的整理稿(17)
  • 2. 关于google map api中的球平投影算法接口: GProjection和GMercatorProjection类(7)
  • 3. 备忘: UTF-8的格式(3)
  • 4. Lucene如何控制segments的数量?(3)
  • 5. 复杂度为log(n)的排序堆栈算法(2)

Powered by: 博客园
模板提供:沪江博客
Copyright ©2025 鹏飞万里