⬆︎
×
TOC
Hyplus目录

Elasticsearch分布式搜索引擎基础教程

Elasticsearch是一个基于Lucene的分布式RESTful搜索引擎,提供实时全文检索、结构化查询和数据分析能力。Elasticsearch使用Java开发,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。

官网:www.elastic.co

1 Elasticsearch概述

Elasticsearch具有如下优点:

  • 分布式与扩展性:通过分片和副本实现水平扩展,支持PB级数据存储与高可用。
  • 近实时检索:文档写入后1秒内可查,支持复杂全文检索与聚合分析。
  • 灵活数据模型:原生支持JSON,动态或显式映射定义字段类型,简化数据管理。
  • 生态与易用性:集成ELK栈,提供多语言客户端,配合Kibana实现可视化运维。
  • 高性能优化:基于Lucene索引,支持批量操作与缓存机制,结合SSD提升查询效率。

通过合理设计索引、优化查询和集群配置,可充分发挥Elasticsearch的高性能和高可用性。实际应用中需结合业务场景,利用Kibana等工具进行监控和调优,以满足复杂搜索与分析需求。


2 核心概念

  1. 分布式架构
    • 集群(Cluster):由一个或多个节点组成的集合,共同存储数据并提供服务。集群名称唯一标识,默认名称为elasticsearch
    • 节点(Node):集群中的服务器实例,分为主节点(负责集群管理)、数据节点(存储数据)和协调节点(处理请求分发)。
    • 分片(Shard):索引被划分为多个分片,每个分片是独立的Lucene索引,支持水平扩展和并行操作。
    • 副本(Replica):分片的拷贝,用于高可用性和负载均衡。主分片与副本不能位于同一节点。默认每个索引5个主分片和1个副本。
  2. 数据模型
    • 索引(Index):文档的逻辑集合,类似数据库表。名称需小写且唯一。
    • 文档(Document):存储的基本单位,以JSON格式表示,类似表中的行。7.0+版本后类型(Type)被弃用,文档直接隶属于索引。
    • 字段(Field):文档中的数据项,类似表中的列。字段类型在映射(Mapping)中定义,支持动态映射和显式定义。
  3. 近实时(Near Real-Time,NRT):文档写入后约1秒内可被搜索,通过refresh API控制可见性。

3 基本操作(Java)

使用Elasticsearch Java High-Level REST Client(7.x版本)进行交互,需引入依赖:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.17.7</version>
</dependency>

3.1 客户端初始化

import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.RestClient;

RestHighLevelClient client = new RestHighLevelClient(
    RestClient.builder(new HttpHost("localhost", 9200, "http"))
);

3.2 创建索引

import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;

CreateIndexRequest request = new CreateIndexRequest("products");
CreateIndexResponse response = client.indices().create(request);
boolean acknowledged = response.isAcknowledged();   // 确认索引创建

3.3 插入文档

import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;

String jsonString = "{\"name\":\"iPhone 14\",\"price\":999,\"category\":\"electronics\"}";
IndexRequest request = new IndexRequest("products")
    .id("1")
    .source(jsonString, XContentType.JSON);

IndexResponse response = client.index(request);

3.4 查询文档

import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;

GetRequest request = new GetRequest("products", "1");
GetResponse response = client.get(request);
String sourceAsString = response.getSourceAsString(); // 获取JSON内容

3.5 搜索文档

import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;

SearchRequest searchRequest = new SearchRequest("products");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("category", "electronics"));
sourceBuilder.from(0).size(10); // 分页

searchRequest.source(sourceBuilder);
SearchResponse response = client.search(searchRequest);

3.6 更新文档

import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;

String jsonString = "{\"price\":1099}";
UpdateRequest request = new UpdateRequest("products", "1")
    .doc(jsonString, XContentType.JSON);

UpdateResponse response = client.update(request);

3.7 删除文档

import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;

DeleteRequest request = new DeleteRequest("products", "1");
DeleteResponse response = client.delete(request);

4 查询与聚合

4.1 Query DSL

Elasticsearch通过JSON格式的DSL(Domain Specific Language)定义查询逻辑,分为查询上下文(影响相关性评分)和过滤上下文(不影响评分且可缓存)。可选择如下4种查询方式:

  1. 全文搜索match查询对文本字段分词后匹配,match_phrase精确匹配短语。
  2. 精确查询term匹配单个词项,terms匹配多个词项。
  3. 组合查询bool查询组合must(必须匹配)、filter(过滤)、must_not(必须不匹配)等条件。
  4. 范围查询range查询数值或日期范围。

【例】布尔查询:

BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
    .must(QueryBuilders.matchQuery("name", "iPhone"))
    .filter(QueryBuilders.rangeQuery("price").gte(900));

4.2 聚合分析

聚合用于分组统计或计算指标,如terms桶聚合和avg指标聚合。

【例】按分类统计平均价格:

AggregationBuilder aggregation = AggregationBuilders.terms("category_agg")
    .field("category")
    .subAggregation(AggregationBuilders.avg("price_avg").field("price"));

sourceBuilder.aggregation(aggregation);
SearchResponse response = client.search(searchRequest);
Terms categoryAgg = response.getAggregations().get("category_agg");
for (Terms.Bucket bucket : categoryAgg.getBuckets()) {
    String category = bucket.getKeyAsString();
    double avgPrice = bucket.getAggregations().get("price_avg").getAvg();
}

5 映射与分析

映射(Mapping):定义索引字段的数据类型、分词规则等。动态映射自动检测字段类型,显式映射可自定义配置,如下例所示——

CreateIndexRequest request = new CreateIndexRequest("products");
request.mapping("{\n" +
                "  \"properties\": {\n" +
                "    \"name\": {\n" +
                "      \"type\": \"text\",\n" +
                "      \"analyzer\": \"ik_max_word\"\n" +
                "    },\n" +
                "    \"price\": {\n" +
                "      \"type\": \"scaled_float\",\n" +
                "      \"scaling_factor\": 100\n" +
                "    }\n" +
                "  }\n" +
                "}", XContentType.JSON);

分析器(Analyzer):由字符过滤器、分词器和标记过滤器组成,用于文本分词和标准化。内置分析器如standardkeyword,也可自定义,如下例所示——

request.settings("{\n" +
                 "  \"analysis\": {\n" +
                 "    \"analyzer\": {\n" +
                 "      \"custom_analyzer\": {\n" +
                 "        \"type\": \"custom\",\n" +
                 "        \"tokenizer\": \"standard\",\n" +
                 "        \"filter\": [\"lowercase\", \"stop\"]\n" +
                 "      }\n" +
                 "    }\n" +
                 "  }\n" +
                 "}", XContentType.JSON);

6 分布式原理与优化

分布式搜索流程:

  1. 查询阶段(Query Phase):协调节点将请求广播到所有相关分片,分片返回文档ID和排序信息。
  2. 取回阶段(Fetch Phase):协调节点根据ID从分片拉取完整文档,合并后返回客户端。

性能优化策略:

  • 索引设计:合理设置分片数(建议单个分片不超过30GB),使用时间序列索引管理日志数据。
  • 查询优化:优先使用过滤上下文(filter),避免wildcardregexp等高成本查询,限制返回字段(_source)。
  • 硬件与JVM:分配足够内存(堆内存不超过32GB),使用SSD提升I/O性能,选择G1垃圾回收器。
  • 批量操作:使用BulkRequest批量插入或更新,减少网络开销。

【例】批量插入:

BulkRequest bulkRequest = new BulkRequest();
for (Product product : products) {
    IndexRequest request = new IndexRequest("products")
        .id(product.getId())
        .source(objectMapper.writeValueAsString(product), XContentType.JSON);
    bulkRequest.add(request);
}
BulkResponse bulkResponse = client.bulk(bulkRequest);

7 集群管理与维护

集群健康检查:通过/_cluster/health接口查看集群状态,green表示所有分片可用,yellow表示部分副本未分配,red表示主分片丢失。

分片与副本调整:

  1. 动态调整副本数:
UpdateSettingsRequest request = new UpdateSettingsRequest();
request.settings(Settings.builder().put("index.number_of_replicas", 2));
client.indices().updateSettings(request, RequestOptions.DEFAULT);
  1. 分片分配控制:通过cluster.routing.allocation参数控制分片在节点间的分布。

故障转移与恢复:主分片所在节点宕机时,副本分片自动提升为主分片。需配置discovery.zen.minimum_master_nodes防止脑裂。

发表评论