根據(jù) Lucene-2.2.0 源代碼閱讀學(xué)習(xí)(16) 中對(duì)IndexFileDeleter類和CommitPoint類的源代碼的閱讀學(xué)習(xí),在此進(jìn)行總結(jié):
一個(gè)提交點(diǎn)所具有的信息如下所示:
???? long gen;???
// 下次提交索引段segments_N的版本
??? List files;???
// 屬于當(dāng)前索引目錄的索引段的一個(gè)列表
??? String segmentsFileName;???
// 一個(gè)索引段
??? boolean deleted;???
// 刪除標(biāo)志
一個(gè)提交點(diǎn)具有的行為:
1、通過getSegmentsFileName()方法,得到一個(gè)索引段文件的名稱;
2、通過delete()方法,獲取到具有deleted標(biāo)志(當(dāng)delete為false時(shí),即還沒有被刪除)的提交點(diǎn),加入到commitsToDelete列表中,真正刪除是在CommitPoint類的外部類IndexFileDeleter類中的deleteCommits()方法中;
3、該類的compareTo()實(shí)現(xiàn)了自然排序的功能,排序是根據(jù)gen = segmentInfos.getGeneration();返回的整數(shù)值進(jìn)行實(shí)現(xiàn)的。也就是說,如果把一個(gè)個(gè)的CommitPoint加入到列表中的時(shí)候,它是有序的,可以很方便地獲取最早的提交點(diǎn)和最近提交點(diǎn)。
在IndexFileDeleter類和CommitPoint類中,都涉及到了關(guān)于索引段Segment的內(nèi)容,研究一下SegmentInfos類和SegmentInfo類。
先看一下SegmentInfo類的結(jié)構(gòu),然后再學(xué)習(xí)代碼:
?
?
?
?
?
?
SegmentInfos類實(shí)現(xiàn)的源代碼:
package org.apache.lucene.index;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Vector;
final class SegmentInfos extends Vector {
//
public static final int FORMAT_LOCKLESS = -2;
//
public static final int FORMAT_SINGLE_NORM_FILE = -3;
// 用于指向最近的文件的格式(因?yàn)長ucene2.1以后對(duì)索引文件的格式進(jìn)行了優(yōu)化的改變),可以參考官方文檔
http://lucene.apache.org/java/2_2_0/fileformats.html#Segments%20File
private static final int CURRENT_FORMAT = FORMAT_SINGLE_NORM_FILE;
public int counter = 0;???
// 用于命名當(dāng)前最新的索引段文件
/**
?? * 統(tǒng)計(jì)索引文件變化的頻率(如添加索引、刪除索引會(huì)使索引文件的格式發(fā)生變化)
?? * 根據(jù)當(dāng)前的時(shí)間(精確到毫秒)創(chuàng)建一個(gè)唯一的版本號(hào)數(shù)字串.
?? */
private long version = System.currentTimeMillis();
private long generation = 0;????
// 下次提交時(shí)"segments_N"的N=generation
private long lastGeneration = 0;??
// 最后一次成功讀取或者寫入,"segments_N"中N=lastGeneration
/**
?? * 如果索引文件不是null的,則構(gòu)造一個(gè)輸出流,輸出segments_N文件
?? */
private static PrintStream infoStream;
public final SegmentInfo info(int i) {
??? return (SegmentInfo) elementAt(i);
}
/**
?? * 從指定的文件列表files中獲取當(dāng)前segments_N文件的版本號(hào)(generation)
?? */
public static long getCurrentSegmentGeneration(String[] files) {
??? if (files == null) {???
// 如果指定的索引目錄中沒有索引文件,返回-1
????? return -1;
??? }
??? long max = -1;???
// 不存在任何索引文件,當(dāng)默認(rèn)當(dāng)前版本號(hào)為-1
??? for (int i = 0; i < files.length; i++) {???
// 對(duì)索引目錄中所有索引文件遍歷,取出segments_N中最大的N的作為當(dāng)前版本號(hào)
????? String file = files[i];
// IndexFileNames.SEGMENTS="segments",segments是生成的索引文件,在IndexFileNames類中定義了所有的索引文件名
// IndexFileNames.SEGMENTS_GEN="segments.gen"
????? if (file.startsWith(IndexFileNames.SEGMENTS) && !file.equals(IndexFileNames.SEGMENTS_GEN)) {
??????? long gen = generationFromSegmentsFileName(file);
// 調(diào)用后面的方法,獲取索引文件的版本號(hào)(generation)
??????? if (gen > max) {
????????? max = gen;
??????? }
????? }
??? }
??? return max;???
//?? 將segmen
ts_N中最大的N返回,作為當(dāng)前版本號(hào)(generation)
}
/**
?? * 重載的方法,從指定的索引目錄中獲取當(dāng)前segments_N文件的版本號(hào)(generation)
?? */
public static long getCurrentSegmentGeneration(Directory directory) throws IOException {
??? String[] files = directory.list();
??? if (files == null)
????? throw new IOException("cannot read directory " + directory + ": list() returned null");
??? return getCurrentSegmentGeneration(files);????
//調(diào)用getCurrentSegmentGeneration()方法,從索引目錄中讀取的文件列表files中獲取當(dāng)前segments_N文件的版本號(hào)(generation)
}
/**
?? * 指定索引文件列表,獲取當(dāng)前segments_N文件的名稱
?? */
public static String getCurrentSegmentFileName(String[] files) throws IOException {
??? return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS, "",getCurrentSegmentGeneration(files));???
// 調(diào)用了IndexFileNames類的fileNameFromGeneration()方法,在后面有講解
}
/**
?? * 重載的方法,指定索引目錄,獲取當(dāng)前segments_N文件的名稱
?? */
public static String getCurrentSegmentFileName(Directory directory) throws IOException {
??? return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,"",
getCurrentSegmentGeneration(directory));
}
??
/**
?? * 重載的方法,根據(jù)索引文件的信息,即最后成功讀取或?qū)懭霑r(shí)的版本號(hào)
lastGeneration
,獲取當(dāng)前segments_N文件的名稱
?? */
public String getCurrentSegmentFileName() {
??? return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,"",lastGeneration);
}
/**
?? * 從索引文件名稱的字符串中解析索引文件的版本號(hào),即segments_N中的N,并且最后返回N的值
?? */
public static long generationFromSegmentsFileName(String fileName) {
??? if (fileName.equals(IndexFileNames.SEGMENTS)) {???
// 如果文件名稱為segments,沒有擴(kuò)展名,則返回0
????? return 0;
??? } else if (fileName.startsWith(IndexFileNames.SEGMENTS)) {
????? return Long.parseLong(fileName.substring(1+IndexFileNames.SEGMENTS.length()),Character.MAX_RADIX);???
// 取segments_N中的子串N,并將N轉(zhuǎn)換為Long型
??? } else {???
// 解析失敗,拋出異常
????? throw new IllegalArgumentException("fileName \"" + fileName + "\" is not a segments file");
??? }
}
/**
?? * 獲取下一個(gè)將被寫入索引目錄的segments_N文件
?? */
public String getNextSegmentFileName() {
??? long nextGeneration;
??? if (generation == -1) {???
// 如果當(dāng)前索引目錄中沒有任何索引文件,則最新寫入的索引文件的版本號(hào)為1,即segments_1
????? nextGeneration = 1;
??? } else {
????? nextGeneration = generation+1;??
// 否則,當(dāng)前的版本號(hào)+1為將要寫入的索引文件的版本號(hào)
??? }
???
// 返回將要寫入索引目錄的索引文件的名稱,即文件名segments_N,N用nextGeneration替換
??? return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,"",nextGeneration);
}
/**
?? * 讀取指定的索引文件
?? */
public final void read(Directory directory, String segmentFileName) throws CorruptIndexException, IOException {
??? boolean success = false;
??? IndexInput input = directory.openInput(segmentFileName);??? // 為索引文件segmentFileName創(chuàng)建一個(gè)輸入流
??? generation = generationFromSegmentsFileName(segmentFileName);??? // 下次要提交的索引文件的版本號(hào)
??? lastGeneration = generation;??? // 最后成功讀取或?qū)懭胨饕募陌姹咎?hào)
??? try {
????? int format = input.readInt();???
// 讀取4個(gè)字節(jié),返回一個(gè)Int型整數(shù),索引文件中具有版本號(hào)的記錄
????? if(format < 0){????
// 如果文件包含了外部的版本號(hào)
???????
// 要解析成內(nèi)部能夠使用的信息
??????? if (format < CURRENT_FORMAT)????
// 如果讀取到的Int整數(shù)小于當(dāng)前從索引文件中獲取的版本號(hào),則是錯(cuò)誤的
????????? throw new CorruptIndexException("Unknown format version: " + format);
??????? version = input.readLong();
// 讀取版本號(hào)Long串
??????? counter = input.readInt();
// 讀取用于命名當(dāng)前的索引文件的gen值
????? }
????? else{????
// 索引文件沒有外部格式信息,就去當(dāng)前從索引文件中讀取到的整數(shù)值為當(dāng)前的索引文件命名
??????? counter = format;
????? }
?????
????? for (int i = input.readInt(); i > 0; i--) {
// 讀取索引段信息
??????? addElement(new SegmentInfo(directory, format, input));???
//?? 構(gòu)造一個(gè)用于管理索引文件的SegmentInfo對(duì)象,添加到SegmentInfos向量列表中去
????? }
?????
????? if(format >= 0){???
// 對(duì)于舊格式的索引文件,版本號(hào)信息可能在文件的末尾
??????? if (input.getFilePointer() >= input.length())
????????? version = System.currentTimeMillis();
// 如果舊文件格式?jīng)]有版本號(hào)信息,則設(shè)置當(dāng)前版本號(hào)
??????? else
????????? version = input.readLong();
// 否則,如果不是舊格式索引文件,直接從索引文件中讀取版本號(hào)
????? }
????? success = true;???
// 獲取到索引文件的版本號(hào),則標(biāo)志位success置true,表示可以生成當(dāng)前版本的索引文件(名稱)
??? }
??? finally {
????? input.close();
????? if (!success) {
??????? clear();
????? }
??? }
}
/**
?? * 如果讀取索引文件失敗,重新嘗試再次去讀取
?? */
public final void read(Directory directory) throws CorruptIndexException, IOException {
??? generation = lastGeneration = -1;
??? new FindSegmentsFile(directory) {??? // FindSegmentsFile是一個(gè)靜態(tài)抽象內(nèi)部類,在此實(shí)現(xiàn)從索引目錄中加載索引文件
????? protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
??????? read(directory, segmentFileName);???
// 初始化一個(gè)FindSegmentsFile的實(shí)例時(shí),調(diào)用上面實(shí)現(xiàn)的讀取索引文件的read方法
??????? return null;
????? }
??? }.run();???
//?? 調(diào)用繼承自抽象類FindSegmentsFile的run方法進(jìn)行讀取,(run方法的實(shí)現(xiàn)比較復(fù)雜)
}
/**
?? * 執(zhí)行寫入當(dāng)前的索引文件操作
?? */
public final void write(Directory directory) throws IOException {
??? String segmentFileName = getNextSegmentFileName();
???
// Always advance the generation on write:
??? if (generation == -1) {
????? generation = 1;
??? } else {
????? generation++;
??? }
??? IndexOutput output = directory.createOutput(segmentFileName);??? // 構(gòu)造一個(gè)索引文件輸出流
??? boolean success = false;
??? try {
????? output.writeInt(CURRENT_FORMAT);
// 寫入FORMAT
????? output.writeLong(++version); ??
// 寫入版本號(hào)
????? output.writeInt(counter);???
//?? 寫入當(dāng)前的索引文件的外部信息(即segment_N中的N的值)
????? output.writeInt(size());??
// 寫入該SegmentInfos中的每個(gè)SegmentInfo的信息
????? for (int i = 0; i < size(); i++) {
??????? info(i).write(output);
????? }????????
??? }
??? finally {
????? try {
??????? output.close();???
// 關(guān)閉索引文件輸出流,成功寫入索引目錄
??????? success = true;
????? } finally {
??????? if (!success) {???
// 如果寫入失敗,執(zhí)行回滾操作,刪除非法的寫入失敗的索引文件
????????? directory.deleteFile(segmentFileName);
??????? }
????? }
??? }
??? try {
????? output = directory.createOutput(IndexFileNames.SEGMENTS_GEN);???
// 創(chuàng)建segment.gen文件,打開一個(gè)輸出文件流
????? try {???
// 寫入維護(hù)所需要的信息
??????? output.writeInt(FORMAT_LOCKLESS);
??????? output.writeLong(generation);
??????? output.writeLong(generation);
????? } finally {
??????? output.close();
????? }
??? } catch (IOException e) {
?????
// It's OK if we fail to write this file since it's
????? // used only as one of the retry fallbacks.
??? }
???
??? lastGeneration = generation;
}
/**
?? * 克隆一個(gè)SegmentInfos
?? */
public Object clone() {
??? SegmentInfos sis = (SegmentInfos) super.clone();
??? for(int i=0;i<sis.size();i++) {
????? sis.setElementAt(((SegmentInfo) sis.elementAt(i)).clone(), i);
??? }
??? return sis;
}
/**
?? * SegmentInfos生成的版本號(hào)
?? */
public long getVersion() {
??? return version;
}
public long getGeneration() {
??? return generation;
}
/**
?? * 從segments文件中讀取當(dāng)前的版本號(hào).
?? */
public static long readCurrentVersion(Directory directory)
??? throws CorruptIndexException, IOException {
??? return ((Long) new FindSegmentsFile(directory) {
??????? protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
????????? IndexInput input = directory.openInput(segmentFileName);
????????? int format = 0;
????????? long version = 0;
????????? try {
??????????? format = input.readInt();
??????????? if(format < 0){
????????????? if (format < CURRENT_FORMAT)
??????????????? throw new CorruptIndexException("Unknown format version: " + format);
????????????? version = input.readLong();??
// read version
??????????? }
????????? }
????????? finally {
??????????? input.close();
????????? }
????
????????? if(format < 0)
??????????? return new Long(version);
?????????
// We cannot be sure about the format of the file.
????????? // Therefore we have to read the whole file and cannot simply seek to the version entry.
????????? SegmentInfos sis = new SegmentInfos();
????????? sis.read(directory, segmentFileName);
????????? return new Long(sis.getVersion());
??????? }
????? }.run()).longValue();
}
/**
?? * segments 文件輸出流
?? */
public static void setInfoStream(PrintStream infoStream) {
??? SegmentInfos.infoStream = infoStream;
}
/* Advanced configuration of retry logic in loading
???? segments_N file */
private static int defaultGenFileRetryCount = 10;
private static int defaultGenFileRetryPauseMsec = 50;
private static int defaultGenLookaheadCount = 10;
/**
?? * Advanced: set how many times to try loading the
?? * segments.gen file contents to determine current segment
?? * generation. This file is only referenced when the
?? * primary method (listing the directory) fails.
?? */
public static void setDefaultGenFileRetryCount(int count) {
??? defaultGenFileRetryCount = count;
}
public static int getDefaultGenFileRetryCount() {
??? return defaultGenFileRetryCount;
}
/**
?? * Advanced: set how many milliseconds to pause in between
?? * attempts to load the segments.gen file.
?? */
public static void setDefaultGenFileRetryPauseMsec(int msec) {
??? defaultGenFileRetryPauseMsec = msec;
}
public static int getDefaultGenFileRetryPauseMsec() {
??? return defaultGenFileRetryPauseMsec;
}
/**
?? * Advanced: set how many times to try incrementing the
?? * gen when loading the segments file. This only runs if
?? * the primary (listing directory) and secondary (opening
?? * segments.gen file) methods fail to find the segments
?? * file.
?? */
public static void setDefaultGenLookaheadCount(int count) {
??? defaultGenLookaheadCount = count;
}
public static int getDefaultGenLookahedCount() {
??? return defaultGenLookaheadCount;
}
public static PrintStream getInfoStream() {
??? return infoStream;
}
private static void message(String message) {
??? if (infoStream != null) {
????? infoStream.println(Thread.currentThread().getName() + ": " + message);
??? }
}
////********這里是FindSegmentsFile抽象靜態(tài)內(nèi)部類的定義,可以參考Lucene實(shí)現(xiàn)源代碼********////
}
?
?
?
從SegmentInfos類的實(shí)現(xiàn)過程可以看出,該類主要是對(duì)SegmentInfo進(jìn)行管理的。在每次執(zhí)行打開索引目錄、打開索引文件、寫入文件等等,都需要對(duì)SegmentInfos進(jìn)行維護(hù)。
因?yàn)镾egmentInfos記錄了對(duì)索引文件進(jìn)行操作(如:建立索引、刪除索引)而生成的一些索引文件格式、版本號(hào)的信息,所以每當(dāng)索引文件有操作需求,都要從SegmentInfos中獲取當(dāng)前的一些詳細(xì)記錄,SegmentInfos是操作索引文件的依據(jù),同時(shí)操作索引文件結(jié)束后,要及時(shí)更新SegmentInfos的記錄信息,為下次操作索引文件提供準(zhǔn)確的信息。
SegmentInfos類主要通過兩個(gè)文件來維護(hù)這些信息:segment_N和segment.gen文件。
segment_N文件存儲(chǔ)的是當(dāng)前正處于激活狀態(tài)的索引文件的信息,也就是當(dāng)前操作的索引文件的維護(hù)信息。
segment.gen文件是專門用于管理segment_N文件的。這里,segment_N文件是動(dòng)態(tài)變化的,比如每次寫入新的索引文件或者刪除索引文件都涉及到當(dāng)前索引文件的版本問題。segment.gen主要管理的的操作索引文件的版本信息的。
在處理提交點(diǎn)的時(shí)候,也要參考索引文件的版本,都需要從segment.gen中讀取;根據(jù)實(shí)際的操作,還要在操作結(jié)束的時(shí)候更新segment.gen文件,保證下次操作的正確性。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
