黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

Lucene-2.2.0 源代碼閱讀學(xué)習(xí)(25)

系統(tǒng) 2158 0

復(fù)合索引文件格式(.cfs)是如何產(chǎn)生的?從這個(gè)問題出發(fā),研究索引文件是如何合并的,這都是IndexWriter類中定義的一些重要的方法。

在建立索引過程中,生成的索引文件的格式有很多種。

在文章 Lucene-2.2.0 源代碼閱讀學(xué)習(xí)(4) 中測(cè)試的那個(gè)例子,沒有對(duì)IndexWriter進(jìn)行任何的客戶化設(shè)置,完全使用Lucene 2.2.0默認(rèn)的設(shè)置(以及,對(duì)Field的設(shè)置使用了Lucene自帶的Demo中的設(shè)置)。

運(yùn)行程序以后,在本地磁盤的索引目錄中生成了一些.擴(kuò)展名為.cfs的索引文件,即復(fù)合索引格式文件。如圖(該圖在文章 Lucene-2.2.0 源代碼閱讀學(xué)習(xí)(4) 中介紹過)所示:

從上面生成的那些.cfs復(fù)合索引文件可以看出,Lucene 2.2.0版本,IndexWriter索引的一個(gè)成員useCompoundFile的設(shè)置起了作用,可以在IndexWriter類的內(nèi)部看到定義和默認(rèn)設(shè)置:

private boolean useCompoundFile = true;

即,默認(rèn)使用復(fù)合索引文件格式來存儲(chǔ)索引文件。

在IndexWriter類的addDocument(Document doc, Analyzer analyzer)方法中可以看到,最后調(diào)用了 maybeFlushRamSegments()方法,這個(gè)方法的作用可是很大的,看它的定義:

protected final void maybeFlushRamSegments () throws CorruptIndexException, IOException {
??? if (ramSegmentInfos.size() >= minMergeDocs || numBufferedDeleteTerms >= maxBufferedDeleteTerms) {
?????
flushRamSegments();
??? }
}

這里,minMergeDocs是指:決定了合并索引段文件時(shí)指定的最小的Document的數(shù)量,在IndexWriter類中默認(rèn)值為10,可以在IndexWriter類中查看到:

???? private int minMergeDocs = DEFAULT_MAX_BUFFERED_DOCS;
?? public final static int DEFAULT_MAX_BUFFERED_DOCS = 10;

其中SegmentInfos ramSegmentInfos中保存了Document的數(shù)量的信息,如果Document的數(shù)量小于10,則調(diào)用flushRamSegments()方法進(jìn)行處理,flushRamSegments()方法的定義如下所示:

private final synchronized void flushRamSegments() throws CorruptIndexException, IOException {
??? flushRamSegments (true);
}

在flushRamSegments()方法中又調(diào)用到了該方法的一個(gè)重載的方法,帶一個(gè)boolean型參數(shù)。該重載的方法定義如下:

protected final synchronized void flushRamSegments(boolean triggerMerge)
????? throws CorruptIndexException, IOException {
??? if (ramSegmentInfos.size() > 0 || bufferedDeleteTerms.size() > 0) {
????? mergeSegments (ramSegmentInfos, 0, ramSegmentInfos.size());
????? if (triggerMerge) maybeMergeSegments (minMergeDocs);
??? }
}

同樣,如果Document的數(shù)量小于10,則調(diào)用mergeSegments()方法,先看一下該方法的參數(shù):

private final int mergeSegments(SegmentInfos sourceSegments, int minSegment, int end)

第一個(gè)參數(shù)指定了一個(gè)SegmentInfos(上面調(diào)用傳遞了ramSegmentInfos) ;第二個(gè)參數(shù)是minSegment是最小的索引段數(shù)量(上面調(diào)用傳遞了0,說明如果存在>=0個(gè)索引段文件時(shí)就開始合并索引文件);第三個(gè)參數(shù)是end,指要合并索引段文件的個(gè)數(shù)(上面調(diào)用傳遞了ramSegmentInfos.size(),即對(duì)所有的索引段文件都執(zhí)行合并操作)。

繼續(xù)看mergeSegments()方法的實(shí)現(xiàn):

private final int mergeSegments(SegmentInfos sourceSegments, int minSegment, int end)
??? throws CorruptIndexException, IOException {

??? // doMerge決定了是否執(zhí)行合并操作,根據(jù)end的值,如果end為0說明要合并的索引段文件為0個(gè),即不需要合并,doMerge=false
??? boolean doMerge = end > 0;

/*??? 生成合并的索引段文件名稱,即根據(jù)SegmentInfos的counter值,如果counter=0,則返回的文件名為_0(沒有指定擴(kuò)展名)

??? final synchronized String newSegmentName() {
??????? return "_" + Integer.toString(segmentInfos.counter++, Character.MAX_RADIX);
??? }

*/
??? final String mergedName = newSegmentName();
??? SegmentMerger merger = null;?? // 聲明一個(gè)SegmentMerger變量

??? final List ramSegmentsToDelete = new ArrayList();??? // ramSegmentsToDelete列表用于存放可能要在合并結(jié)束后刪除的索引段文件,因?yàn)楹喜⒌倪^程中需要?jiǎng)h除掉合并完以后存在于內(nèi)存中的這些索引段文件

??? SegmentInfo newSegment = null;

??? int mergedDocCount = 0;
??? boolean anyDeletes = (bufferedDeleteTerms.size() != 0);

??? // This is try/finally to make sure merger's readers are closed:
??? try {

????? if (doMerge) {??? // 如果doMerge=true,即end>0,也就是說至少有1個(gè)以上的索引段文件存在,才能談得上合并
??????? if (infoStream != null) infoStream.print("merging segments");??? // infoStream是一個(gè)PrintStream輸出流對(duì)象,合并完成后要向索引目錄中寫入合并后的索引段文件,必須有一個(gè)打開的輸出流
??????? merger = new SegmentMerger(this, mergedName);??? // 構(gòu)造一個(gè)SegmentMerger對(duì)象,通過參數(shù):當(dāng)前的打開的索引器this和合并后的索引段名稱mergedName(形如_N,其中N為數(shù))關(guān)于SegmentMerger類會(huì)在后面文章學(xué)習(xí)

??????? for (int i = minSegment; i < end; i++) {???? // 循環(huán)遍歷,從SegmentInfos sourceSegments中迭代出每個(gè)SegmentInfo對(duì)象
????????? SegmentInfo si = sourceSegments.info(i);
????????? if (infoStream != null)
??????????? infoStream.print(" " + si.name + " (" + si.docCount + " docs)");??? // SegmentInfo si的name在索引目錄中是唯一的;這里打印出每個(gè) SegmentInfo si的名稱和在這個(gè)索引段文件中Document的數(shù)量
????????? IndexReader reader = SegmentReader.get(si, MERGE_READ_BUFFER_SIZE); ?? // 調(diào)用SegmentReader類的靜態(tài)方法get(),根據(jù)每個(gè)SegmentInfo si獲取一個(gè)索引輸入流對(duì)象;在IndexWriter類中定義了成員MERGE_READ_BUFFER_SIZE=4096
????????? merger.add(reader);??? //?? 將獲取到的SegmentReader reader加入到SegmentMerger merger中
????????? if (reader.directory() == this.ramDirectory) {??? // 如果SegmentReader reader是當(dāng)前的索引目錄,與當(dāng)前的RAMDirectory ramDirectory是同一個(gè)索引目錄
??????????? ramSegmentsToDelete.add(si);???
// 將該SegmentInfo si加入到待刪除的列表ramSegmentsToDelete中
????????? }
??????? }
????? }

????? SegmentInfos rollback = null;
????? boolean success = false;

????? // This is try/finally to rollback our internal state
????? // if we hit exception when doing the merge:
????? try {

??????? if (doMerge) {???? // 如果doMerge=true
????????? mergedDocCount = merger.merge();??? // 通過SegmentMerger merger獲取需要合并的索引段文件數(shù)量

????????? if (infoStream != null) {??? // 打印出合并后的索引段文件的名稱,及其合并了索引段文件的數(shù)量
??????????? infoStream.println(" into "+mergedName+" ("+mergedDocCount+" docs)");
????????? }

????????? newSegment = new SegmentInfo(mergedName, mergedDocCount,
?????????????????????????????????????? directory, false, true);??? // 實(shí)例化一個(gè)SegmentInfo對(duì)象
??????? }
???????
??????? if (sourceSegments != ramSegmentInfos || anyDeletes) {
????????? // 通過克隆,存儲(chǔ)一個(gè)用來回滾用的SegmentInfos實(shí)例,以防合并過程中發(fā)生異常
????????? rollback = (SegmentInfos) segmentInfos.clone();
??????? }

??????? if (doMerge) {?? // 如果doMerge=true
????????? if (sourceSegments == ramSegmentInfos) {?? // 如果傳進(jìn)來的sourceSegments和內(nèi)存中的ramSegmentInfos是同一個(gè)

??????????? segmentInfos.addElement(newSegment);??? // 將合并后的新的SegmentInfo newSegment加入到segmentInfos中進(jìn)行管理,以便之后再對(duì)其操作
????????? } else { // 如果傳進(jìn)來的sourceSegments和內(nèi)存中的ramSegmentInfos不是同一個(gè)
??????????? for (int i = end-1; i > minSegment; i--)???? // 刪除舊的信息,同時(shí)添加新的信息
????????????? sourceSegments.remove(i);

??????????? segmentInfos.set(minSegment, newSegment);
????????? }
??????? }

??????? if (sourceSegments == ramSegmentInfos) {??? // 如果傳進(jìn)來的sourceSegments和內(nèi)存中的ramSegmentInfos是同一個(gè),因?yàn)閰?shù)設(shè)置的原因,可能需要?jiǎng)h除合并以后原來舊的索引段文件
????????? maybeApplyDeletes(doMerge);??? // 調(diào)用 maybeApplyDeletes()方法執(zhí)行合并后的刪除處理
????????? doAfterFlush();
??????? }
???????
??????? checkpoint();?? // 調(diào)用該方法 checkpoint()檢查,確認(rèn)并提交更新

??????? success = true;??? // 如果檢查沒有發(fā)現(xiàn)異常,則置success=true

????? } finally {

??????? if (success) {??? // 如果success =true,表示提交成功,要清理內(nèi)存
????????? if (sourceSegments == ramSegmentInfos) {
??????????? ramSegmentInfos.removeAllElements();
????????? }
??????? } else {??? // 如果發(fā)生異常,則需要回滾操作

????????? if (sourceSegments == ramSegmentInfos && !anyDeletes) {
??????????? if (newSegment != null &&
??????????????? segmentInfos.size() > 0 &&
??????????????? segmentInfos.info(segmentInfos.size()-1) == newSegment) {
????????????? segmentInfos.remove(segmentInfos.size()-1);
??????????? }
????????? } else if (rollback != null) {
??????????? segmentInfos.clear();
??????????? segmentInfos.addAll(rollback);
????????? }

????????? // Delete any partially created and now unreferenced files:
????????? deleter.refresh();
??????? }
????? }
??? } finally {
?????
// 關(guān)閉所有的輸入流(readers),嘗試刪除過時(shí)的廢棄文件
????? if (doMerge) merger.closeReaders();
??? }

??? // 刪除RAM中的索引段文件
??? deleter.deleteDirect(ramDirectory, ramSegmentsToDelete);

??? // 一個(gè)檢查點(diǎn),允許一個(gè)IndexFileDeleter deleter有機(jī)會(huì)在該時(shí)間點(diǎn)上去刪除文件
??? deleter.checkpoint(segmentInfos, autoCommit);

??? if (useCompoundFile && doMerge) {?? // 如果IndexWriter索引器設(shè)置了useCompoundFile=true

????? boolean success = false;

????? try {

??????? merger.createCompoundFile(mergedName + ".cfs");??? //?? 創(chuàng)建復(fù)合索引文件(.cfs),即_N.cfs文件
??????? newSegment.setUseCompoundFile(true);??? // 設(shè)置SegmentInfo newSegment為復(fù)合索引文件的信息
??????? checkpoint();????? // 調(diào)用該方法 checkpoint()檢查,確認(rèn)并提交更新
??????? success = true;

????? } finally {??? // 如果檢查過程中發(fā)生異常,則回滾
??????? if (!success) {??
????????? newSegment.setUseCompoundFile(false);
????????? deleter.refresh();
??????? }
????? }
??????
????? // 一個(gè)檢查點(diǎn),允許一個(gè)IndexFileDeleter deleter有機(jī)會(huì)在該時(shí)間點(diǎn)上去刪除文件
????? deleter.checkpoint(segmentInfos, autoCommit);
??? }

??? return mergedDocCount; // 返回需合并的索引段文件數(shù)量
}

?

?

?

?

?

?

?

?

?

在不帶參數(shù)的flushRamSegments()方法中,調(diào)用了帶參數(shù)的flushRamSegments(boolean triggerMerge),也就是說,默認(rèn)情況下,Lucene指定triggerMerge=true,可以在不帶參數(shù)的flushRamSegments()方法中看到對(duì)該參數(shù)的設(shè)置:

private final synchronized void flushRamSegments() throws CorruptIndexException, IOException {
??? flushRamSegments(true);
}

所以,在帶參數(shù)的flushRamSegments(boolean triggerMerge)方法中,一定會(huì)執(zhí)行maybeMergeSegments()這個(gè)合并索引的方法,如下所示:

if (triggerMerge) maybeMergeSegments(minMergeDocs);

這里,傳遞的參數(shù)minMergeDocs=10(Lucene默認(rèn)值),那么就應(yīng)該有一個(gè)maxMergeDocs的成員與之對(duì)應(yīng),在Lucene 2.2.0版本中,在IndexWriter類中定義了該maxMergeDocs成員的默認(rèn)值:

??? private int maxMergeDocs = DEFAULT_MAX_MERGE_DOCS;
??? public final static int DEFAULT_MAX_MERGE_DOCS = Integer.MAX_VALUE;
??? public static final int?? MAX_VALUE = 0x7fffffff;

maxMergeDocs是合并的最大的Document的數(shù)量,定義為最大的Integer。

因?yàn)橐粋€(gè)索引目錄中的索引段文件的數(shù)量可能大于minMergeDocs=10,如果也要對(duì)所有的索引段文件進(jìn)行合并,則指定合并最小數(shù)量minMergeDocs的Docment是不能滿足要求的,即使用mergeSegments()方法。

因此,maybeMergeSegments()就能實(shí)現(xiàn)合并性能的改善,它的聲明就是需要一個(gè)起始的參數(shù),從而進(jìn)行增量地合并索引段文件。該方法的實(shí)現(xiàn)如下所示:

/** Incremental segment merger. */
private final void maybeMergeSegments(int startUpperBound) throws CorruptIndexException, IOException {
??? long lowerBound = -1;
??? long upperBound = startUpperBound;??? // 使用upperBound存放傳遞進(jìn)來的startUpperBound

??? while (upperBound < maxMergeDocs) {??? // 如果upperBound < maxMergeDocs,一般來說,這個(gè)應(yīng)該總成立的
????? int minSegment = segmentInfos.size();???
//?? 設(shè)置minSegment的值為當(dāng)前的SegmentInfos segmentInfos 的大小
????? int maxSegment = -1;

????? // 查找能夠合并的索引段文件
????? while (--minSegment >= 0) {????
// 就是遍歷SegmentInfos segmentInfos中的每個(gè)SegmentInfo si
??????? SegmentInfo si = segmentInfos.info(minSegment);??? // 從索引位置號(hào)最大的開始往外取

??????? if (maxSegment == -1 && si.docCount > lowerBound && si.docCount <= upperBound) {??? // maxSegment == -1;同時(shí)滿足-1=lowerBound <(一個(gè)索引段文件中Dcoment的數(shù)量si.docCount)<=upperBound = startUpperBound
????????? // start from the rightmost* segment whose doc count is in bounds
????????? maxSegment = minSegment;???
//?? 設(shè)置maxSegment的值為當(dāng)前SegmentInfos的大小
??????? } else if (si.docCount > upperBound) {
????????? // 直到segment中Document的數(shù)量超過了上限upperBound,則退出循環(huán)
????????? break;
??????? }
????? }

??? // 該while循環(huán)只執(zhí)行了一次,執(zhí)行過程中,將maxSegment賦值為segmentInfos.size()-1

????? minSegment++;??? // 上面循環(huán)中一直執(zhí)行--minSegment,則到這里minSegment=-1,設(shè)置其值為0
????? maxSegment++;??? // 因?yàn)閙axSegment=segmentInfos.size()-1,則設(shè)置為maxSegment=segmentInfos.size()
????? int numSegments = maxSegment - minSegment;??? // numSegments = maxSegment - minSegment = segmentInfos.size() ??

????? if (numSegments < mergeFactor) {

??? /* mergeFactor是合并因子,IndexWriter的成員,默認(rèn)設(shè)置為10,mergeFactor的值越大,則內(nèi)存中駐留的Document就越多,向索引目錄中寫入segment的次數(shù)就越少,雖然占用內(nèi)存較多,但是速度應(yīng)該很快的。每向索引文件中加入mergeFactor=10個(gè)Document的時(shí)候,就會(huì)在索引目錄中生成一個(gè)索引段文件(segment) */
??????? break;??? // numSegments < mergeFactor則沒有達(dá)到合并所需要的數(shù)量,不需要合并,直接退出
????? } else {
??????? boolean exceedsUpperLimit = false;??? // 設(shè)置一個(gè)沒有超過上限的boolean型標(biāo)志(false)

??????? // 能夠合并的segments的數(shù)量>=mergeFactor時(shí)
??????? while (numSegments >= mergeFactor) {
????????? // 調(diào)用mergeSegments(即上面的學(xué)習(xí)到的那個(gè)合并的方法)方法, 合并從minSegment開始的mergeFactor個(gè)segment

????????? int docCount = mergeSegments(segmentInfos, minSegment, minSegment + mergeFactor);
????????? numSegments -= mergeFactor; // mergeFactor個(gè)segment已經(jīng)合并完成,剩下需要合并的數(shù)量要減去mergeFactor,在下一次循環(huán)的時(shí)候繼續(xù)合并

????????? if (docCount > upperBound) {??? // 如果上次合并返回的合并后的Document的數(shù)量大于上限
??????????? // 繼續(xù)在該層次合并剩余的segment
??????????? minSegment++;
??????????? exceedsUpperLimit = true;???
//?? 設(shè)置已經(jīng)超過上限,不能再進(jìn)行深一層次的的合并,即本輪合并就是最深層次的合并了
????????? } else { // 如果上次合并返回的合并后的Document的數(shù)量沒有超過上限
???????????
// 考慮進(jìn)行更深層次的合并
??????????? numSegments++;
????????? }
??????? }

??????? if (!exceedsUpperLimit) { // 如果上次合并返回的合并后的Document的數(shù)量大于上限,則終止執(zhí)行本層次合并
????????? break;
??????? }
????? }

????? lowerBound = upperBound;
????? upperBound *= mergeFactor;??? // 如果一個(gè)層次的合并成功后,還可以進(jìn)一步合并,則,上限變?yōu)樵瓉淼?0倍
??? }
}

合并索引段文件就是這樣實(shí)現(xiàn)的,并非只是在一個(gè)層次上合并:

第一層次合并時(shí),每次只能將10個(gè)segment索引段文件合并為1個(gè)新的segment,假設(shè)在這一層生成了500個(gè)經(jīng)過合并以后生成的索引段文件;

第二層次合并時(shí),每次能合并10*mergeFactor=10*10=100個(gè)segment,經(jīng)判斷,上一層次生成了500個(gè)segment還可以進(jìn)行第二層次的合并,現(xiàn)在每次100個(gè)segment文件才可能合并為1個(gè),可見,只能合并生成5個(gè)新的segment;

第三層次合并時(shí),每次能合并10*mergeFactor*mergeFactor=10*10*10=1000個(gè)segment,但是上一層次只是生成了5個(gè),不夠數(shù)量(1000個(gè)),不能繼續(xù)合并了,到此終止。

就是上面的那種原理,實(shí)現(xiàn)索引段文件的合并。如果希望進(jìn)行更深層次的合并,把mergeFactor的值設(shè)置的非常小就可以了,但是I/O操作過于頻繁,速度會(huì)很慢很慢的。

提高合并的速度,是以內(nèi)存空間開銷為代價(jià)的。

通過第一個(gè)合并的方法可以看出,只有當(dāng)為一個(gè)IndexWriter索引器設(shè)置了useCompoundFile=true的時(shí)候,才能生成復(fù)合索引文件_N.cfs,如下所示:

??? if (useCompoundFile && doMerge) {?? // 如果IndexWriter索引器設(shè)置了useCompoundFile=true

????? boolean success = false;

????? try {

??????? merger.createCompoundFile(mergedName + ".cfs");??? //?? 創(chuàng)建復(fù)合索引文件(.cfs),即_N.cfs文件
??????? newSegment.setUseCompoundFile(true);??? // 設(shè)置SegmentInfo newSegment為復(fù)合索引文件的信息
??????? checkpoint();????? // 調(diào)用該方法 checkpoint()檢查,確認(rèn)并提交更新
??????? success = true;

????? } finally {??? // 如果檢查過程中發(fā)生異常,則回滾
??????? if (!success) {??
????????? newSegment.setUseCompoundFile(false);
????????? deleter.refresh();
??????? }
????? }
??????
????? // 一個(gè)檢查點(diǎn),允許一個(gè)IndexFileDeleter deleter有機(jī)會(huì)在該時(shí)間點(diǎn)上去刪除文件
????? deleter.checkpoint(segmentInfos, autoCommit);
??? }

現(xiàn)在知道了,那些_N.cfs文件是合并的索引段文件。

Lucene-2.2.0 源代碼閱讀學(xué)習(xí)(25)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

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

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論