在JAVA中將Elasticsearch索引載入到Lucene API

banq發表於2019-01-09

每隔一段時間,Elasticsearch中就會出現意外(或無意)崩潰。對於我的情況,在Elasticsearch的大量IO操作期間是硬體故障(讓我們假設我沒有任何副本或者我設法使所有叢集崩潰)。經過一些研究,我發現它搞砸了許多索引的狀態檔案(已損壞!)。我想,如果Elasticsearch使用Lucene,我肯定可以載入我的資料並使用Lucene API重新索引它。

重要說明:要使本指南處理索引,必須在對映中啟用_source欄位的索引。如果您因任何原因禁用了_source欄位,則此程式碼將不適用於您的索引。Elasticsearch建議始終啟用它。
在我們開始之前,讓我們引用兩個重要的術語。更多細節可以在這裡找到。

  • 索引:用於在Elasticsearch中儲存資料的位置。
  • Shard分片或Lucene索引例項(!):每個索引由一個或多個分片組成。它是一個獨立的搜尋引擎,可以索引和處理Elasticsearch叢集中資料子集的查詢。

正如你在這裡看到,我們的資料駐留在索引和這些分佈到多個分片,後者實際上是一個Lucene索引 !
讓我們看一下Elasticsearch資料的結構,並嘗試找到這些Lucene索引。您可以配置資料路徑elasticsearch.yml與關鍵path.data。


索引indices目錄下有一個名為類似eCCAJ-x6SOuN6w7vqr4tGQ格式的資料夾。(這些專案的說明是完全獨立的主題。為了簡單起見,只需要知道狀態檔案是由Elasticsearch生成的,它是一個SMILE-encode檔案,其中包含Elasticsearch後設資料,如索引名稱,節點ID等。您可以使用十六進位制編輯器來檢查檔案)

讓我們透過Curl我們的Elasticsearch例項來列出我們的索引。

$ curl elastic-host:9200/_cat/indices
yellow open bad_ip eCCAJ-x6SOuN6w7vqr4tGQ 5 1 4609 0 1.2mb 1.2mb


這是我的索引,其中索引了一些惡意IP資料。我們來看看那個目錄。

$ ls my-elasticsearch/data/nodes/0/indices/eCCAJ-x6SOuN6w7vqr4tGQ
0      1      2      3      4      _state


這是我們索引的分片,或者是我們要載入的Lucene索引例項。

提示:如果此時Elasticsearch例項崩潰,您只需在狀態檔案上執行cat命令即可檢視它們所屬的索引。所有檔案都不可讀,但至少你應該看到你的索引名稱。

重要說明#2:在繼續執行程式碼之前,您應該確保您的Lucene索引沒有損壞。為了檢查和修復您的Lucene索引(或Elasticsearch分片),我強烈建議您使用CheckIndex工具。


現在,我們找到了Lucene索引,讓我們做一些編碼來拯救我們的資料。我將建立一個示例Maven專案。這是我們需要的依賴項:

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>7.5.0</version>
</dependency>

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-queryparser</artifactId>
    <version>7.5.0</version>
</dependency>


重要提示#3:您應該檢視您的Elasticsearch例項的Lucene版本,並在此處使用相同的Lucene API版本。執行該命令curl elastic-host:9200 並查詢lucene_version金鑰。

為了簡單起見,我將在main方法中編寫所有程式碼並丟擲異常。這取決於您如何構建程式碼。

import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class Main {
    public static void main(String[] args) throws IOException {
        String luceneIndexPath = "my-elasticsearch/data/nodes/0/indices/eCCAJ-x6SOuN6w7vqr4tGQ/0/index";
        Directory index = FSDirectory.open(Paths.get(luceneIndexPath));

        IndexReader reader = DirectoryReader.open(index);

        System.out.println(reader.maxDoc());
        
        reader.close();
        index.close();
    }
}

執行它,你會看到你的分片檔案的數量。就我而言,我的第0個碎片中有952個文件。如果列出所有主分片並對其進行總結,則它將等於您的Elasticsearch索引中的總文件數。
  • Directory類來自Lucene API,但它與Java i / o庫沒有太大區別。它只是為了簡化不同來源的實現,例如儲存在資料庫中的索引。我的索引的實際例項是 MMapDirectory。如果您有不同的配置,可能會有所不同。(同樣,這是另一個話題。)
  • IndexReader是一個用於訪問索引的視點的抽象類。

我們可以使用我們的reader變數訪問大量資訊,例如段資訊和文件本身。我們想要的是找到這些檔案。讓我們編寫那部分程式碼。

for(int i = 0; i < reader.maxDoc(); i++){
    if(((DirectoryReader) reader).isCurrent()){
      Document document = reader.document(i);

      String source = document.getBinaryValue("_source").utf8ToString();
      System.out.println(source);
    }
}


我們所做的是一個簡單的迴圈並獲得該位置的相應文件。之後,我們只獲取JSON所在的_source鍵。它以二進位制形式儲存,因此,首先我們需要獲取二進位制值,然後進行utf8ToString轉換。

 

相關文章