亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

用Lucene構(gòu)建實時索引的文檔更新問題

系統(tǒng) 1597 0

1、Lucene刪除文檔的幾種方式

?

  • IndexReader.deleteDocument(int docID)是用 IndexReader 按文檔號刪除。??
  • IndexReader.deleteDocuments(Term? term)是用 IndexReader 刪除包含此詞(Term)的文檔。??
  • IndexWriter.deleteDocuments(Term? term)是用 IndexWriter 刪除包含此詞(Term)的文檔。??
  • IndexWriter.deleteDocuments(Term[]? terms)是用 IndexWriter 刪除包含這些詞(Term)的文檔。??
  • IndexWriter.deleteDocuments(Query? query)是用 IndexWriter 刪除能滿足此查詢(Query)的文檔。??
  • IndexWriter.deleteDocuments(Query[] queries)是用 IndexWriter 刪除能滿足這些查詢(Query)的文檔。

刪除文檔既可以用reader進行刪除,也可以用writer進行刪除,不同的是,reader進行刪除后,此reader馬上能夠生效,而用writer刪除后,會被緩存,只有寫入到索引文件中,當(dāng)reader再次打開的時候,才能夠看到。

2、Lucene文檔更新的幾個問題

?

2.1、使用IndexReader還是IndexWriter進行刪除

既然IndexReader和IndexWriter都能夠進行文檔刪除,那么到底是應(yīng)該用哪個來進行刪除呢?

本文的建議是,用IndexWriter來進行刪除。

因為用IndexReader可能存在以下的問題:

(1) 當(dāng)有一個IndexWriter打開的時候,IndexReader的刪除操作是不能夠進行的,否則會報LockObtainFailedException

(2) 當(dāng)IndexReader被多個線程使用的時候,一個線程用其進行刪除,會使得另一個線程看到的索引有所改變,使得另一個線程的結(jié)果帶有不確定性。

(3) 對于更新操作,在Lucene中是先刪除,再添加的,然而刪除的被立刻看到的,而添加卻不能夠立刻看到,造成了數(shù)據(jù)的不一致性。

(4) 即便以上問題可以通過鎖來解決,然而背后的操作影響到了搜索的速度,是我們不想看到的。

2.2、如何在內(nèi)存中緩存文檔的刪除

在上一節(jié)中,為了能夠做到實時性,我們使用內(nèi)存中的索引,而硬盤上的索引則不經(jīng)常打開,即便打開也在背后線程中打開。

而要刪除的文檔如果在硬盤索引中,如果不重新打開則看不到新的刪除,則需要將刪除的文檔緩存到內(nèi)存中。

那如何將緩存在內(nèi)存中的文檔刪除在不重新打開IndexReader的情況下應(yīng)用于硬盤上的索引呢?

在Lucene中,有一種IndexReader為FilterIndexReader,可以對一個IndexReader進行封裝,我們可以實現(xiàn)一個自己的FilterIndexReader來過濾掉刪除的文檔。

一個例子如下:

?

public class MyFilterIndexReader extends FilterIndexReader {

? OpenBitSet dels;

? public MyFilterIndexReader(IndexReader in) {

??? super(in);

??? dels = new OpenBitSet(in.maxDoc());

? }

? public MyFilterIndexReader(IndexReader in, List<String> idToDelete) throws IOException {

??? super(in);

??? dels = new OpenBitSet(in.maxDoc());

??? for(String id : idToDelete){

????? TermDocs td = in.termDocs(new Term("id", id));? //如果能在內(nèi)存中Cache從Lucene的ID到應(yīng)用的ID的映射,Reader的生成將快得多。

????? if(td.next()){

??????? dels.set(td.doc());

????? }

??? }

? }

? @Override

? public int numDocs() {

??? return in.numDocs() - (int) dels.cardinality();

? }

? @Override

? public TermDocs termDocs(Term term) throws IOException {

??? return new FilterTermDocs(in.termDocs(term)) {

????? @Override

????? public boolean next() throws IOException {

??????? boolean res;

??????? while ((res = super.next())) {

????????? if (!dels.get(doc())) {

??????????? break;

????????? }

??????? }

??????? return res;

????? }

??? };

? }

? @Override

? public TermDocs termDocs() throws IOException {

??? return new FilterTermDocs(in.termDocs()) {

????? @Override

????? public boolean next() throws IOException {

??????? boolean res;

??????? while ((res = super.next())) {

????????? if (!dels.get(doc())) {

??????????? break;

????????? }

??????? }

??????? return res;

????? }

??? };

? }

}

?

2.3、文檔更新的順序性問題

Lucene的文檔更新其實是刪除舊的文檔,然后添加新的文檔。如上所述,刪除的文檔是緩存在內(nèi)存中的,并通過FilterIndexReader應(yīng)用于硬盤上的索引,然而新的文檔也是以相同的id加入到索引中去的,這就需要保證緩存的刪除不會將新的文檔也過濾掉,將緩存的刪除合并到索引中的時候不會將新的文檔也刪除掉。

Lucene的兩次更新一定要后一次覆蓋前一次,而不能讓前一次覆蓋后一次。

所以內(nèi)存中已經(jīng)硬盤中的多個索引是要被保持一個順序的,哪個是老的索引,哪個是新的索引,緩存的刪除自然是應(yīng)該應(yīng)用于所有比他老的索引的,而不應(yīng)該應(yīng)用于他自己以及比他新的索引。

3、具有更新功能的Lucene實時索引方案

3.1、初始化

首先假設(shè)我們硬盤上已經(jīng)有一個索引FileSystemIndex,被事先打開的,其中包含文檔1,2,3,4,5,6。

我們在內(nèi)存中有一個索引MemoryIndex,新來的文檔全部索引到內(nèi)存索引中,并且是索引完IndexWriter就commit,IndexReader就重新打開,其中包含文檔7,8。

繪圖8

?

3.2、更新文檔5

這時候來一個新的更新文檔5, 需要首先將文檔5刪除,然后加入新的文檔5。

需要做的事情是:

  • 首先在內(nèi)存索引中刪除文檔5,當(dāng)然沒有文檔5,刪除無效。
  • 其次將對文檔5的刪除放入內(nèi)存文檔刪除列表,并與硬盤的IndexReader組成FilterIndexReader
  • 最后,將新的文檔5加入內(nèi)存索引,這時候,用戶可以看到的就是新的文檔5了。
  • 將文檔5放入刪除列表以及將文檔5提交到內(nèi)存索引兩者應(yīng)該是一個原子操作,好在這兩者都是比較塊的。

注:此處對硬盤上的索引,也可以進行對文檔5的刪除,由于IndexReader沒有重新打開,此刪除是刪不掉的,我們之所以沒有這樣做,是想保持此次更新要么全部在內(nèi)存中,要么全部在硬盤中,而非刪除部分已經(jīng)應(yīng)用到硬盤中,而新文檔卻在內(nèi)存中,此時,如果系統(tǒng)crash,則新的文檔5丟失了,而舊的文檔5也已經(jīng)在硬盤上被刪除。我們將硬盤上對文檔5的刪除放到從內(nèi)存索引向硬盤索引的合并過程。

更新文檔5

如果再有一次對文檔5的更新,則首先將內(nèi)存索引中的文檔5刪除,添加新的文檔5,然后將文檔5加入刪除列表,發(fā)現(xiàn)已經(jīng)存在,則不必刪除。

3.3、合并索引

然而經(jīng)過一段時間,內(nèi)存中的索引需要合并到硬盤上。

在合并的過程中,需要重新建立一個空的內(nèi)存索引,用于合并階段索引新的文檔,而合并中的索引的IndexReader以及硬盤索引和刪除列表所組成的FilterIndexReader仍然保持打開,對外提供服務(wù),而合并階段從后臺進行。

后臺的合并包括以下幾步:

  • 將刪除列表應(yīng)用到硬盤索引中。
  • 將內(nèi)存索引合并到硬盤索引中。
  • IndexWriter提交。

合并

3.4、合并的過程中更新文檔5

在合并的過程中,如果還有更新那怎么辦呢?

  • 首先將合并中索引的文檔5刪除,此刪除不會影響合并,因為合并之前,合并中索引的IndexReader已經(jīng)打開,索引合并中索引的文檔5還是會合并到硬盤中去的。此刪除影響的是此后的查詢在合并中索引是看不到文檔5的。
  • 然后將文檔5的刪除放入刪除列表,并同合并中索引的刪除列表,已經(jīng)硬盤索引一起構(gòu)成FilterIndexReader。
  • 將新的文檔5添加到內(nèi)存中索引。
  • 提交在合并中索引對文檔5的刪除,將文檔5添加到刪除列表,提交在內(nèi)存索引中對文檔5的添加三者應(yīng)該是一個原子操作,好在三者也是很快的。

合并時更新

3.5、重新打開硬盤索引的IndexReader

當(dāng)合并中索引合并到硬盤中的時候,是時候重新打開硬盤上的索引了,新打開的IndexReader是可以看到文檔5的刪除的。

如果這個時候有新的更新,也是添加到內(nèi)存索引和刪除列表的,比如我們更新文檔6.

重新打開

3.6、替代IndexReader ?

當(dāng)IndexReader被重新打開后,則需要刪除合并中的索引及其刪除列表,將硬盤索引原來的IndexReader關(guān)閉,使用新的IndexReader。

替換IndexReader

?

用Lucene構(gòu)建實時索引的文檔更新問題


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

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

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

【本文對您有幫助就好】

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

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 一级大黄视频 | 在线有码 | 国产亚洲精品久久久久久久软件 | 久久久久久88色愉愉 | 99在线观看国产 | 久久久久久一级毛片免费无遮挡 | 女人18一级毛片免费观看 | 免费中文字幕在线国语 | 大学生一级毛片 | 四虎亚洲 | 成年超爽大片免费视频播放 | 久久人人网 | 99re8免费视频精品全部 | 97久久曰曰久久久 | 一级女性全黄生活片看看 | 色婷婷久| 91视频原创 | 日韩第1页 | 国产黄色影院 | 久久久久久久久久免费视频 | 五月婷婷激情综合 | 伊人色综合久久 | 国产护士一级毛片高清 | 久久久7777888精品 | 精品在线一区二区 | 欧美成人看片一区二区三区 | 亚洲精品中文字幕一区在线 | 久久99精品国产麻豆不卡 | 欧美一区二区影院 | 99热精品久久只有精品30 | 综合色好色 | 午夜视频在线观看网站 | 亚洲 欧美 成人日韩 | 欧美高清视频www夜色资源 | 亚洲一区二区三区高清不卡 | 亚洲一区二区三区四区热压胶 | 黄视频网站观看 | 精品视频在线观看一区二区 | 成人精品视频一区二区三区 | 日本精品高清一区二区2021 | 国产精品久久久久久久久夜色 |