lucene4.0中有两个文件保存了段的相关信息,分别是segments.gen和segments_N.当打开一个索引文件的时候,首先要做的就是确定最后一次提交的段文件segments_N,然后再按照段来读取文档、词典等信息文件。下面直接开始。
1、从索引目录下的segments_N中选取N最大的,在从segments.gen中读取保存的最后提交的N,选择最大的作为结果
files = directory.listAll();
if (files != null) {
genA = getLastCommitGeneration(files);
}
从segments.gen中读取保存的最后提交的N,这个文件中N是保存了两遍的,可能就是为了防止有人随意修改吧,所以最后还有一个gen0==gen1的验证
long gen0 = genInput.readLong();
long gen1 = genInput.readLong();
if (infoStream != null) {
message("fallback check: " + gen0 + "; " + gen1);
}
if (gen0 == gen1) {
// The file is consistent.
genB = gen0;
}
从genA和genB中挑出最大的
gen = Math.max(genA, genB);
构造段文件名,读取段文件,如果正常的话,到这里就结束了,但是有可能出现意外情况;所以lucene还有一些容错性的操作,可以做到尽可能的把当前的索引按照正确的读取出来
segmentFileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
"",
gen);
Object v = doBody(segmentFileName);
2、如果通过上边的操作没有读取到争取的段信息,那么lucene会自动尝试读取segmens_N-1文件
if (useFirstMethod && lastGen == gen && retryCount >= 2) { //第三次循环之后才会放弃使用第一种方法
// Give up on first method -- this is 3rd cycle on
// listing directory and checking gen file to
// attempt to locate the segments file.
useFirstMethod = false;
}
// Second method: since both directory cache and
// file contents cache seem to be stale, just
// advance the generation.
if (!useFirstMethod) { // 4
if (genLookaheadCount < defaultGenLookaheadCount) {
gen++;
genLookaheadCount++;
if (infoStream != null) {
message("look ahead increment gen to " + gen);
}
} else {
// All attempts have failed -- throw first exc:
throw exc;
}
} else if (lastGen == gen) { //2 3
// This means we're about to try the same
// segments_N last tried.
retryCount++;
} else { // 1
// Segment file has advanced since our last loop
// (we made "progress"), so reset retryCount:
retryCount = 0;
}
lastGen = gen;
通过上边的代码可以知道,第一次循环之后,retryCount应该是0,如果没有成功的话,会进行第二次,第三次,也就是retryCount++操作,第四次开始执行 if (!useFirstMethod)这块;
第二次读取代码如下:
if (gen > 1 && useFirstMethod && retryCount == 1) { // 第二次循环,N>1,尝试读取segments_N-1
// This is our second time trying this same segments
// file (because retryCount is 1), and, there is
// possibly a segments_(N-1) (because gen > 1).
// So, check if the segments_(N-1) exists and
// try it if so:
String prevSegmentFileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
"",
gen-1);
final boolean prevExists;
prevExists = directory.fileExists(prevSegmentFileName);
if (prevExists) {
if (infoStream != null) {
message("fallback to prior segment file '" + prevSegmentFileName + "'");
}
try {
Object v = doBody(prevSegmentFileName);
if (infoStream != null) {
message("success on fallback " + prevSegmentFileName);
}
return v;
} catch (IOException err2) {
if (infoStream != null) {
message("secondary Exception on '" + prevSegmentFileName + "': " + err2 + "'; will retry");
}
}
}
}
3、如果通过segments_N-1还没有读取到正确的段信息呢,那么lucene还有一个判断,那就是N++,上边已经说了,从第四次循环开始,会开始执行
if (genLookaheadCount < defaultGenLookaheadCount) {
gen++;
genLookaheadCount++;
if (infoStream != null) {
message("look ahead increment gen to " + gen);
}
}
defaultGenLookaheadCount是一个全局变量,默认是10,也就是说,会对gen+1到gen+10之间的数做遍历,构造segments_N,看是否存在这样的段文件;如果一直到gen+10都没有找到正确的segments_N的话,就说明自定的索引不正确。
附加:senments.gen这个文件内容很简单的,一共包含三个数,分别是int,long,long,第一个int是索引文件格式版本,对应的lucene4.0的是-2;后边的两个long保存的最后一次提交的段文件名,senments_N,一般来说应该只有一个这样的文件