每隔一段时间,Elasticsearch中就会出现意外(或无意)崩溃。对于我的情况,在Elasticsearch的大量IO操作期间是硬件故障(让我们假设我没有任何副本或者我设法使所有集群崩溃)。经过一些研究,我发现它搞砸了许多索引的状态文件(已损坏!)。我想,如果Elasticsearch使用Lucene,我肯定可以加载我的数据并使用Lucene API重新索引它。
重要说明:要使本指南处理索引,必须在映射中启用_source字段的索引。如果您因任何原因禁用了_source字段,则此代码将不适用于您的索引。 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方法中编写所有代码并抛出异常。这取决于您如何构建代码。
<b>import</b> org.apache.lucene.index.DirectoryReader; <b>import</b> org.apache.lucene.index.IndexReader; <b>import</b> org.apache.lucene.store.Directory; <b>import</b> org.apache.lucene.store.FSDirectory; <b>public</b> <b>class</b> Main { <b>public</b> <b>static</b> <b>void</b> main(String[] args) throws IOException { String luceneIndexPath = <font>"my-elasticsearch/data/nodes/0/indices/eCCAJ-x6SOuN6w7vqr4tGQ/0/index"</font><font>; Directory index = FSDirectory.open(Paths.get(luceneIndexPath)); IndexReader reader = DirectoryReader.open(index); System.out.println(reader.maxDoc()); reader.close(); index.close(); } } </font>
运行它,你会看到你的分片文件的数量。就我而言,我的第0个碎片中有952个文档。如果列出所有主分片并对其进行总结,则它将等于您的Elasticsearch索引中的总文档数。
我们可以使用我们的reader变量访问大量信息,例如段信息和文档本身。我们想要的是找到这些文件。让我们编写那部分代码。
<b>for</b>(<b>int</b> i = 0; i < reader.maxDoc(); i++){ <b>if</b>(((DirectoryReader) reader).isCurrent()){ Document document = reader.document(i); String source = document.getBinaryValue(<font>"_source"</font><font>).utf8ToString(); System.out.println(source); } } </font>
我们所做的是一个简单的循环并获得该位置的相应文档。之后,我们只获取JSON所在的_source键。它以二进制形式存储,因此,首先我们需要获取二进制值,然后进行utf8ToString转换。