根據 Lucene-2.2.0 源代碼閱讀學習(16) 中對IndexFileDeleter類和CommitPoint類的源代碼的閱讀學習,在此進行總結:
一個提交點所具有的信息如下所示:
???? long gen;???
// 下次提交索引段segments_N的版本
??? List files;???
// 屬于當前索引目錄的索引段的一個列表
??? String segmentsFileName;???
// 一個索引段
??? boolean deleted;???
// 刪除標志
一個提交點具有的行為:
1、通過getSegmentsFileName()方法,得到一個索引段文件的名稱;
2、通過delete()方法,獲取到具有deleted標志(當delete為false時,即還沒有被刪除)的提交點,加入到commitsToDelete列表中,真正刪除是在CommitPoint類的外部類IndexFileDeleter類中的deleteCommits()方法中;
3、該類的compareTo()實現了自然排序的功能,排序是根據gen = segmentInfos.getGeneration();返回的整數值進行實現的。也就是說,如果把一個個的CommitPoint加入到列表中的時候,它是有序的,可以很方便地獲取最早的提交點和最近提交點。
在IndexFileDeleter類和CommitPoint類中,都涉及到了關于索引段Segment的內容,研究一下SegmentInfos類和SegmentInfo類。
先看一下SegmentInfo類的結構,然后再學習代碼:
?
?
?
?
?
?
SegmentInfos類實現的源代碼:
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;
// 用于指向最近的文件的格式(因為Lucene2.1以后對索引文件的格式進行了優化的改變),可以參考官方文檔
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;???
// 用于命名當前最新的索引段文件
/**
?? * 統計索引文件變化的頻率(如添加索引、刪除索引會使索引文件的格式發生變化)
?? * 根據當前的時間(精確到毫秒)創建一個唯一的版本號數字串.
?? */
private long version = System.currentTimeMillis();
private long generation = 0;????
// 下次提交時"segments_N"的N=generation
private long lastGeneration = 0;??
// 最后一次成功讀取或者寫入,"segments_N"中N=lastGeneration
/**
?? * 如果索引文件不是null的,則構造一個輸出流,輸出segments_N文件
?? */
private static PrintStream infoStream;
public final SegmentInfo info(int i) {
??? return (SegmentInfo) elementAt(i);
}
/**
?? * 從指定的文件列表files中獲取當前segments_N文件的版本號(generation)
?? */
public static long getCurrentSegmentGeneration(String[] files) {
??? if (files == null) {???
// 如果指定的索引目錄中沒有索引文件,返回-1
????? return -1;
??? }
??? long max = -1;???
// 不存在任何索引文件,當默認當前版本號為-1
??? for (int i = 0; i < files.length; i++) {???
// 對索引目錄中所有索引文件遍歷,取出segments_N中最大的N的作為當前版本號
????? 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);
// 調用后面的方法,獲取索引文件的版本號(generation)
??????? if (gen > max) {
????????? max = gen;
??????? }
????? }
??? }
??? return max;???
//?? 將segmen
ts_N中最大的N返回,作為當前版本號(generation)
}
/**
?? * 重載的方法,從指定的索引目錄中獲取當前segments_N文件的版本號(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);????
//調用getCurrentSegmentGeneration()方法,從索引目錄中讀取的文件列表files中獲取當前segments_N文件的版本號(generation)
}
/**
?? * 指定索引文件列表,獲取當前segments_N文件的名稱
?? */
public static String getCurrentSegmentFileName(String[] files) throws IOException {
??? return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS, "",getCurrentSegmentGeneration(files));???
// 調用了IndexFileNames類的fileNameFromGeneration()方法,在后面有講解
}
/**
?? * 重載的方法,指定索引目錄,獲取當前segments_N文件的名稱
?? */
public static String getCurrentSegmentFileName(Directory directory) throws IOException {
??? return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,"",
getCurrentSegmentGeneration(directory));
}
??
/**
?? * 重載的方法,根據索引文件的信息,即最后成功讀取或寫入時的版本號
lastGeneration
,獲取當前segments_N文件的名稱
?? */
public String getCurrentSegmentFileName() {
??? return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,"",lastGeneration);
}
/**
?? * 從索引文件名稱的字符串中解析索引文件的版本號,即segments_N中的N,并且最后返回N的值
?? */
public static long generationFromSegmentsFileName(String fileName) {
??? if (fileName.equals(IndexFileNames.SEGMENTS)) {???
// 如果文件名稱為segments,沒有擴展名,則返回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轉換為Long型
??? } else {???
// 解析失敗,拋出異常
????? throw new IllegalArgumentException("fileName \"" + fileName + "\" is not a segments file");
??? }
}
/**
?? * 獲取下一個將被寫入索引目錄的segments_N文件
?? */
public String getNextSegmentFileName() {
??? long nextGeneration;
??? if (generation == -1) {???
// 如果當前索引目錄中沒有任何索引文件,則最新寫入的索引文件的版本號為1,即segments_1
????? nextGeneration = 1;
??? } else {
????? nextGeneration = generation+1;??
// 否則,當前的版本號+1為將要寫入的索引文件的版本號
??? }
???
// 返回將要寫入索引目錄的索引文件的名稱,即文件名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創建一個輸入流
??? generation = generationFromSegmentsFileName(segmentFileName);??? // 下次要提交的索引文件的版本號
??? lastGeneration = generation;??? // 最后成功讀取或寫入索引文件的版本號
??? try {
????? int format = input.readInt();???
// 讀取4個字節,返回一個Int型整數,索引文件中具有版本號的記錄
????? if(format < 0){????
// 如果文件包含了外部的版本號
???????
// 要解析成內部能夠使用的信息
??????? if (format < CURRENT_FORMAT)????
// 如果讀取到的Int整數小于當前從索引文件中獲取的版本號,則是錯誤的
????????? throw new CorruptIndexException("Unknown format version: " + format);
??????? version = input.readLong();
// 讀取版本號Long串
??????? counter = input.readInt();
// 讀取用于命名當前的索引文件的gen值
????? }
????? else{????
// 索引文件沒有外部格式信息,就去當前從索引文件中讀取到的整數值為當前的索引文件命名
??????? counter = format;
????? }
?????
????? for (int i = input.readInt(); i > 0; i--) {
// 讀取索引段信息
??????? addElement(new SegmentInfo(directory, format, input));???
//?? 構造一個用于管理索引文件的SegmentInfo對象,添加到SegmentInfos向量列表中去
????? }
?????
????? if(format >= 0){???
// 對于舊格式的索引文件,版本號信息可能在文件的末尾
??????? if (input.getFilePointer() >= input.length())
????????? version = System.currentTimeMillis();
// 如果舊文件格式沒有版本號信息,則設置當前版本號
??????? else
????????? version = input.readLong();
// 否則,如果不是舊格式索引文件,直接從索引文件中讀取版本號
????? }
????? success = true;???
// 獲取到索引文件的版本號,則標志位success置true,表示可以生成當前版本的索引文件(名稱)
??? }
??? finally {
????? input.close();
????? if (!success) {
??????? clear();
????? }
??? }
}
/**
?? * 如果讀取索引文件失敗,重新嘗試再次去讀取
?? */
public final void read(Directory directory) throws CorruptIndexException, IOException {
??? generation = lastGeneration = -1;
??? new FindSegmentsFile(directory) {??? // FindSegmentsFile是一個靜態抽象內部類,在此實現從索引目錄中加載索引文件
????? protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
??????? read(directory, segmentFileName);???
// 初始化一個FindSegmentsFile的實例時,調用上面實現的讀取索引文件的read方法
??????? return null;
????? }
??? }.run();???
//?? 調用繼承自抽象類FindSegmentsFile的run方法進行讀取,(run方法的實現比較復雜)
}
/**
?? * 執行寫入當前的索引文件操作
?? */
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);??? // 構造一個索引文件輸出流
??? boolean success = false;
??? try {
????? output.writeInt(CURRENT_FORMAT);
// 寫入FORMAT
????? output.writeLong(++version); ??
// 寫入版本號
????? output.writeInt(counter);???
//?? 寫入當前的索引文件的外部信息(即segment_N中的N的值)
????? output.writeInt(size());??
// 寫入該SegmentInfos中的每個SegmentInfo的信息
????? for (int i = 0; i < size(); i++) {
??????? info(i).write(output);
????? }????????
??? }
??? finally {
????? try {
??????? output.close();???
// 關閉索引文件輸出流,成功寫入索引目錄
??????? success = true;
????? } finally {
??????? if (!success) {???
// 如果寫入失敗,執行回滾操作,刪除非法的寫入失敗的索引文件
????????? directory.deleteFile(segmentFileName);
??????? }
????? }
??? }
??? try {
????? output = directory.createOutput(IndexFileNames.SEGMENTS_GEN);???
// 創建segment.gen文件,打開一個輸出文件流
????? try {???
// 寫入維護所需要的信息
??????? 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;
}
/**
?? * 克隆一個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生成的版本號
?? */
public long getVersion() {
??? return version;
}
public long getGeneration() {
??? return generation;
}
/**
?? * 從segments文件中讀取當前的版本號.
?? */
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抽象靜態內部類的定義,可以參考Lucene實現源代碼********////
}
?
?
?
從SegmentInfos類的實現過程可以看出,該類主要是對SegmentInfo進行管理的。在每次執行打開索引目錄、打開索引文件、寫入文件等等,都需要對SegmentInfos進行維護。
因為SegmentInfos記錄了對索引文件進行操作(如:建立索引、刪除索引)而生成的一些索引文件格式、版本號的信息,所以每當索引文件有操作需求,都要從SegmentInfos中獲取當前的一些詳細記錄,SegmentInfos是操作索引文件的依據,同時操作索引文件結束后,要及時更新SegmentInfos的記錄信息,為下次操作索引文件提供準確的信息。
SegmentInfos類主要通過兩個文件來維護這些信息:segment_N和segment.gen文件。
segment_N文件存儲的是當前正處于激活狀態的索引文件的信息,也就是當前操作的索引文件的維護信息。
segment.gen文件是專門用于管理segment_N文件的。這里,segment_N文件是動態變化的,比如每次寫入新的索引文件或者刪除索引文件都涉及到當前索引文件的版本問題。segment.gen主要管理的的操作索引文件的版本信息的。
在處理提交點的時候,也要參考索引文件的版本,都需要從segment.gen中讀??;根據實際的操作,還要在操作結束的時候更新segment.gen文件,保證下次操作的正確性。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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