快捷搜索:  as  2018  FtCWSyGV  С˵  test  xxx  Ψһ  w3viyKQx

澳门威尼l斯人网址_博格自动化网进入



对付Lucene的索引历程,除了将词(Term)写入倒排表并终极写入Lucene的索引文件外,还包括分词(Analyzer)和合并段(merge segments)的历程,本次不包括这两部分,将在今后的文章中进行阐发。

Lucene的索引历程,很多的博客,文章都有先容,保举大年夜家上网搜一篇文章:《Annotated Lucene》,似乎中文名称叫《Lucene源码剖析》是很不错的。

想要真正懂得Lucene索引文件历程,最好的法子是跟进代码调试,对着文章看代码,这样不只能够最具体准确的掌握索引历程(描述都是有误差的,而代码是不会骗你的),而且还能够进修Lucene的一些优秀的实现,能够在今后的事情中为我所用,终究Lucene是对照优秀的开源项目之一。

因为Lucene已经进级到3.0.0了,本索引历程为Lucene 3.0.0的索引历程。

一、索引历程体系布局

Lucene 3.0的搜索要经历一个十分繁杂的历程,各类信息分散在不合的工具中阐发,处置惩罚,写入,为了支持多线程,每个线程都创建了一系列类似布局的工具集,为了前进效率,要复用一些工具集,这使得索引历程加倍繁杂。

着实索引历程,便是经历下图中所示的索引链的历程,索引链中的每个节点,认真索引文档的不合部分的信息 ,当经历完所有的索引链的时刻,文档就处置惩罚完毕了。最初的索引链,我们称之基础索引链。

为了支持多线程,使得多个线程能够并发处置惩罚文档,因而每个线程都要建立自己的索引链体系,使得每个线程能够自力事情,在基础索引链根基上建立起来的每个线程自力的索引链体系,我们称之线程索引链。线程索引链的每个节点是由基础索引链中的响应的节点调用函数addThreads创建的。

为了前进效率,斟酌到对相同域的处置惩罚有相似的历程,利用的缓存也大年夜致相称,因而不必每个线程在处置惩罚每一篇文档的时刻都从新创建一系列工具,而是复用这些工具。以是对每个域也建立了自己的索引链体系,我们称之域索引链。域索引链的每个节点是由线程索引链中的响应的节点调用addFields创建的。

当完成对文档的处置惩罚后,各部分信息都要写到索引文件中,写入索引文件的历程是同步的,不是多线程的,也是沿着基础索引链将各部分信息依次写入索引文件的。

下面具体阐发这一历程。

二、具体索引历程

1、创建IndexWriter工具

代码:

IndexWriter writer = new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED);

IndexWriter工具主要包孕以下几方面的信息:

用于索引文档

Directory directory;指向索引文件夹

Analyzer analyzer;分词器

Similarity similarity = Similarity.getDefault(); 影响打分的标准化因子(normalization factor)部分,对文档的打分分两个部分,一部分是索引阶段谋略的,与查询语句无关,一部分是搜索阶段谋略的,与查询语句相关。

SegmentInfos segmentInfos = new SegmentInfos(); 保存段信息,大年夜家会发明,和segments_N中的信息险些逐一对应。

IndexFileDeleter deleter; 此工具不是用来删除文档的,而是用来治理索引文件的。

Lock writeLock; 每一个索引文件夹只能打开一个IndexWriter,以是必要锁。

Set segmentsToOptimize = new HashSet(); 保存正在最优化(optimize)的段信息。当调用optimize的时刻,当前所有的段信息加入此Set,此后新天生的段并不介入这次最优化。

用于合并段,在合并段的文章中将具体描述

SegmentInfos localRollbackSegmentInfos;

HashSet mergingSegments = new HashSet();

MergePolicy mergePolicy = new LogByteSizeMergePolicy(this);

MergeScheduler mergeScheduler = new ConcurrentMergeScheduler();

LinkedList pendingMerges = new LinkedList();

Set runningMerges = new HashSet();

List mergeExceptions = new ArrayList();

long mergeGen;

为维持索引完备性,同等性和事务性

SegmentInfos rollbackSegmentInfos; 当IndexWriter对索引进行了添加,删除文档操作后,可以调用commit将改动提交到文件中去,也可以调用rollback取消从上次commit到此时的改动。

SegmentInfos localRollbackSegmentInfos; 此段信息主要用于将其他的索引文件夹合并到此索引文件夹的时刻,为防止合并到一半掉足可回滚所保存的原本的段信息。

一些设置设置设备摆设摆设

long writeLockTimeout; 得到锁的光阴超时。当超时的时刻,阐明此索引文件夹已经被另一个IndexWriter打开了。

int termIndexInterval; 同tii和tis文件中的indexInterval。

有关SegmentInfos工具所保存的信息:

当索引文件夹如下的时刻,SegmentInfos工具如下表

segmentInfosSegmentInfos(id=37)

capacityIncrement0

counter3

elementCount3

elementDataObject[10](id=68)

[0]SegmentInfo(id=166)

delCount0

delGen-1

diagnosticsHashMap(id=170)

dirSimpleFSDirectory(id=171)

docCount2

docStoreIsCompoundFilefalse

docStoreOffset-1

docStoreSegmentnull

filesArrayList(id=173)

hasProxtrue

hasSingleNormFiletrue

isCompoundFile1

name"_0"

normGennull

preLocklessfalse

sizeInBytes635

[1]SegmentInfo(id=168)

delCount0

delGen-1

diagnosticsHashMap(id=177)

dirSimpleFSDirectory(id=171)

docCount2

docStoreIsCompoundFilefalse

docStoreOffset-1

docStoreSegmentnull

filesArrayList(id=178)

hasProxtrue

hasSingleNormFiletrue

isCompoundFile1

name"_1"

normGennull

preLocklessfalse

sizeInBytes635

[2]SegmentInfo(id=169)

delCount0

delGen-1

diagnosticsHashMap(id=180)

dirSimpleFSDirectory(id=171)

docCount2

docStoreIsCompoundFilefalse

docStoreOffset-1

docStoreSegmentnull

filesArrayList(id=214)

hasProxtrue

hasSingleNormFiletrue

isCompoundFile1

name"_2"

normGennull

preLocklessfalse

sizeInBytes635

generation4

lastGeneration4

modCount3

pendingSegnOutputnull

userDataHashMap(id=146)

version1263044890832

有关IndexFileDeleter:

其不是用来删除文档的,而是用来治理索引文件的。

在对文档的添加,删除,对段的合并的处置惩罚历程中,会天生很多新的文件,并必要删除老的文件,因而必要治理。

然而要被删除的文件又可能在被用,因而要保存一个引用计数,仅仅当引用计数为零的时刻,才履行删除。

下面这个例子能很好的阐明IndexFileDeleter若何对文件引用计数并进行添加和删除的。

(1) 创建IndexWriter时

IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED);

writer.setMergeFactor(3);

索引文件夹如下:

引用计数如下:

refCountsHashMap(id=101)

size1

tableHashMap$Entry[16](id=105)

[8]HashMap$Entry(id=110)

key"segments_1"

valueIndexFileDeleter$RefCount(id=38)

count1

(2) 添加第一个段时

indexDocs(writer, docDir);

writer.commit();

首老师成的不是compound文件

因而引用计数如下:

refCountsHashMap(id=101)

size9

tableHashMap$Entry[16](id=105)

[1]HashMap$Entry(id=129)

key"_0.tis"

valueIndexFileDeleter$RefCount(id=138)

count1

[3]HashMap$Entry(id=130)

key"_0.fnm"

valueIndexFileDeleter$RefCount(id=141)

count1

[4]HashMap$Entry(id=134)

key"_0.tii"

valueIndexFileDeleter$RefCount(id=142)

count1

[8]HashMap$Entry(id=135)

key"_0.frq"

valueIndexFileDeleter$RefCount(id=143)

count1

[10]HashMap$Entry(id=136)

key"_0.fdx"

valueIndexFileDeleter$RefCount(id=144)

count1

[13]HashMap$Entry(id=139)

key"_0.prx"

valueIndexFileDeleter$RefCount(id=145)

count1

[14]HashMap$Entry(id=140)

key"_0.fdt"

valueIndexFileDeleter$RefCount(id=146)

count1

然后会集并成compound文件,并加入引用计数

refCountsHashMap(id=101)

size10

tableHashMap$Entry[16](id=105)

[1]HashMap$Entry(id=129)

key"_0.tis"

valueIndexFileDeleter$RefCount(id=138)

count1

[2]HashMap$Entry(id=154)

key"_0.cfs"

valueIndexFileDeleter$RefCount(id=155)

count1

[3]HashMap$Entry(id=澳门威尼l斯人网址130)

key"_0.fnm"

valueIndexFileDeleter$RefCount(id=141)

count1

[4]HashMap$Entry(id=134)

key"_0.tii"

valueIndexFileDeleter$RefCount(id=142)

count1

[8]HashMap$Entry(id=135)

key"_0.frq"

valueIndexFileDeleter$RefCount(id=143)

count1

[10]HashMap$Entry(id=136)

key"_0.fdx"

valueIndexFileDeleter$RefCount(id=144)

count1

[13]HashMap$Entry(id=139)

key"_0.prx"

valueIndexFileDeleter$RefCount(id=145)

count1

[14]HashMap$Entry(id=140)

key"_0.fdt"

valueIndexFileDeleter$RefCount(id=146)

count1

然后会用IndexFileDeleter.decRef()来删除[_0.nrm, _0.tis, _0.fnm, _0.tii, _0.frq, _0.fdx, _0.prx, _0.fdt]文件

refCountsHashMap(id=101)

size2

tableHashMap$Entry[16](id=105)

[2]HashMap$Entry(id=154)

key"_0.cfs"

valueIndexFileDeleter$RefCount(id=155)

count1

[8]HashMap$Entry(id=110)

key"segments_1"

valueIndexFileDeleter$RefCount(id=38)

count1

然后为建立新的segments_2

refCountsHashMap(id=77)

size3

tableHashMap$Entry[16](id=84)

[2]HashMap$Entry(id=87)

key"_0.cfs"

valueIndexFileDeleter$RefCount(id=91)

count3

[8]HashMap$Entry(id=89)

key"segments_1"

valueIndexFileDeleter$RefCount(id=62)

count0

[9]HashMap$Entry(id=90)

key"segments_2"

nextnull

valueIndexFileDeleter$RefCount(id=93)

count1

然后IndexFileDeleter.decRef() 删除segments_1文件

refCountsHashMap(id=77)

size2

tableHashMap$Entry[16](id=84)

[2]HashMap$Entry(id=87)

key"_0.cfs"

valueIndexFileDeleter$RefCount(id=91)

count2

[9]HashMap$Entry(id=90)

key"segments_2"

valueIndexFileDeleter$RefCount(id=93)

count1

(3) 添加第二个段

indexDocs(writer, docDir);

writer.commit();

(4) 添加第三个段,因为MergeFactor为3,则会进行一次段合并。

indexDocs(writer, docDir);

writer.commit();

首先和其他的段一样,天生_2.cfs以及segments_4

同时创建了一个线程来进行背落后行段合并(ConcurrentMergeScheduler$MergeThread.run())

这时刻的引用计数如下

refCountsHashMap(id=84)

size5

tableHashMap$Entry[16](id=98)

[2]HashMap$Entry(id=112)

key"_0.cfs"

valueIndexFileDeleter$RefCount(id=117)

count1

[4]HashMap$Entry(id=113)

key"_3.cfs"

valueIndexFileDeleter$RefCount(id=118)

count1

[12]HashMap$Entry(id=114)

key"_1.cfs"

valueIndexFileDeleter$RefCount(id=119)

count1

[13]HashMap$Entry(id=115)

key"_2.cfs"

valueIndexFileDeleter$RefCount(id=120)

count1

[15]HashMap$Entry(id=116)

key"segments_4"

valueIndexFileDeleter$RefCount(id=121)

count1

(5) 关闭writer

writer.close();

经由过程IndexFileDeleter.decRef()删除被合并的段

有关SimpleFSLock进行JVM之间的同步:

无意偶尔候,我们写java法度榜样的时刻,也必要不合的JVM之间进行同步,来保护一个全部系统中独一的资本。

假如独一的资本仅仅在一个进程中,则可以应用线程同步的机制

然而假如独一的资本要被多个进程进行造访,则必要进程间同步的机制,无论是Windows和Linux在操作系统层面都有很多的进程间同步的机制。

但进程间的同步却不是Java的特长,Lucene的SimpleFSLock给我们供给了一种要领。

Lock的抽象类

public abstract class Lock {

public static long LOCK_POLL_INTERVAL = 1000;

public static final long LOCK_OBTAIN_WAIT_FOREVER = -1;

public abstract boolean obtain() throws IOException;

public boolean obtain(long lockWaitTimeout) throws LockObtainFailedException, IOException {

boolean locked = obtain();

if (lockWaitTimeout = maxSleepCount) {

throw new LockObtainFailedException("Lock obtain timed out.");

}

try {

Thread.sleep(LOCK_POLL_INTERVAL);

} catch (InterruptedException ie) {

throw new ThreadInterruptedException(ie);

}

locked = obtain();

}

return locked;

}

public abstract void release() throws IOException;

public abstract boolean isLocked() throws IOException;

}

LockFactory的抽象类

public abstract class LockFactory {

public abstract Lock makeLock(String lockName);

abstract public void clearLock(String lockName) throws IOException;

}

SimpleFSLock的实现类

class SimpleFSLock extends Lock {

File lockFile;

File lockDir;

public SimpleFSL澳门威尼l斯人网址ock(File lockDir, String lockFileName) {

this.lockDir = lockDir;

lockFile = new File(lockDir, lockFileName);

}

@Override

public boolean obtain() throws IOException {

if (!lockDir.exists()) {

if (!lockDir.mkdirs())

throw new IOException("Cannot create directory: " + lockDir.getAbsolutePath());

} else if (!lockDir.isDirectory()) {

throw new IOException("Found regular file where directory expected: " + lockDir.getAbsolutePath());

}

return lockFile.createNewFile();

}

@Override

public void release() throws LockReleaseFailedException {

if (lockFile.exists() && !lockFile.delete())

throw new LockReleaseFailedException("f澳门威尼l斯人网址ailed to delete " + lockFile);

}

@Override

public boolean isLocked() {

return lockFile.exists();

}

}

SimpleFSLockFactory的实现类

public class SimpleFSLockFactory extends FSLockFactory {

public SimpleFSLockFactory(String lockDirName) throws IOException {

setLockDir(new File(lockDirName));

}

@Override

public Lock makeLock(String lockName) {

if (lockPrefi澳门威尼l斯人网址x != null) {

lockName = lockPrefix + "-" + lockName;

}

return new SimpleFSLock(lockDir, lockName);

}

@Override

public void clearLock(String lockName) throws IOException {

if (lockDir.exists()) {

if (lockPrefix != null) {

lockName = lockPrefix + "-" + lockName;

}

File lockFile = new File(lockDir, lockName);

if (lockFile.exists() && !lockFile.delete()) {

throw new IOException("Cannot delete " + lockFile);

}

}

}

};

2、创建文档Document工具,并加入域(Field)

代码:

Document doc = new Document();

doc.add(new Field("path", f.getPath(), Field.Store.YES, Field.Index.NOT_ANALYZED));

doc.add(new Field("modified",DateTools.timeToString(f.lastModified(), DateTools.Resolution.MINUTE), Field.Store.YES, Field.Index.NOT_ANALYZED));

doc.add(new Field("contents", new FileReader(f)));

Document工具主要包括以下部分:

此文档的boost,默觉得1,大年夜于一阐明澳门威尼l斯人网址比一样平常的文档加倍紧张,小于一阐明更不紧张。

一个ArrayList保存此文档所有的域

每一个域包括域名,域值,和一些标志位,和fnm,fdx,fdt中的描述相对应。

docDocument(id=42)

boost1.0

fieldsArrayList(id=44)

elementDataObject[10](id=46)

[0]Field(id=48)

binaryLength0

binaryOffset0

boost1.0

fieldsData"exampledocs\\file01.txt"

isBinaryfalse

isIndexedtrue

isStoredtrue

isTokenizedfalse

lazyfalse

name"path"

omitNormsfalse

omitTermFreqAndPositionsfalse

storeOffsetWithTermVectorfalse

storePositionWithTermVectorfalse

storeTermVectorfalse

tokenStreamnull

[1]Field(id=50)

binaryLength0

binaryOffset0

boost1.0

fieldsData"200910240957"

isBinaryfalse

isIndexedtrue

isStoredtrue

isTokenizedfalse

lazyfalse

name"modified"

omitNormsfalse

omitTermFreqAndPositionsfalse

storeOffsetWithTermVectorfalse

storePositionWithTermVectorfalse

storeTermVectorfalse

tokenStreamnull

[2]Field(id=52)

binaryLength0

binaryOffset0

boost1.0

fieldsDataFileReader(id=58)

isBinaryfalse

isIndexedtrue

isStoredfalse

isTokenizedtrue

lazyfalse

name"contents"

omitNormsfalse

omitTermFreqAndPositionsfalse

storeOffsetWithTermVectorfalse

storePositionWithTermVectorfalse

storeTermVectorfalse

tokenStreamnull

modCount3

size3

您可能还会对下面的文章感兴趣: