目录
1、核心概念
Elasticsearch 是一个开源的分布式搜索和分析引擎,它基于 Apache Lucene 构建,能够快速地存储、搜索和分析海量数据。
| 概念 | 描述 |
|---|---|
| 集群 (Cluster) | 由一个或多个节点组成,共同持有整个数据,提供联合索引和搜索能力。 |
| 节点 (Node) | 集群中的一个服务器,存储数据并参与索引和搜索。节点的类型可以不同(如主节点、数据节点、协调节点等)。 |
| 索引 (Index) | 具有相似特性的文档集合,可类比为数据库中的表。例如,可以有一个"用户"索引,一个"产品"索引。索引由一个名称(必须全部小写)标识。 |
| 文档 (Document) | 索引中的基本数据单元,使用JSON格式表示。每个文档都有一个唯一的 ID。 |
| 分片和副本 (Shards & Replicas) | 索引可以被分成多个分片以支持分布式存储,副本是分片的拷贝,用于提供高可用性。创建索引时,需要指定分片数量,之后无法修改。可以在创建索引后随时动态地更改副本数量。 |
| 映射 (Mapping) | 用于定义索引中的文档结构和字段类型。 |
| 查询 (Query) | 用于在索引中搜索和检索文档的操作。 |
核心技术:倒排索引
-
通过内容中的关键词找到其所在的文档 ID。
Keyword -> Document IDs -
例子:
文档1:
{ "content": "The quick brown fox" }文档2:
{ "content": "Jumped over the lazy dog" }文档3:
{ "content": "The quick red fox" }
倒排索引列表(简化版) :
the-> [1, 2, 3]
quick-> [1, 3]
brown-> [1]
fox-> [1, 3]... ...
当搜索 "quick fox" 时,ES 会分别查找 "quick" 和 "fox" 的文档ID列表,然后取交集,最终快速定位到文档1和文档3。
2、核心价值
Elasticsearch 的核心价值可以总结为三点:搜索 、日志处理 和 数据分析。
-
强大的搜索能力
-
全文搜索: 支持复杂的查询,包括模糊查询、短语匹配、同义词、高亮等。
-
结构化搜索: 对数字、日期、范围等结构化数据进行查询。
-
相关性评分: 内置 TF-IDF 和 BM25 等算法,能够根据查询与文档的相关性对结果进行智能排序。
-
-
可扩展性和可靠性
-
分布式: 天生为分布式设计,能够轻松处理 PB 级别的数据。
-
高可用: 通过分片副本机制,确保在部分节点故障时,服务依然可用。
-
-
近实时分析
- 数据在写入后1秒内即可被搜索和分析,非常适合监控、日志分析等需要快速洞察的场景。
3、主要功能
-
数据索引与搜索: 核心功能,提供丰富的 RESTful API 进行数据的 CRUD 和复杂的查询(如
match,term,bool,range等)。 -
聚合:
-
类比: SQL 中的
GROUP BY,但功能强大得多。 -
功能: 提供从数据中提取统计信息的能力,如计算平均值、求和、创建直方图、进行分组等。这是其作为"分析引擎"的关键。
-
-
自动完成与搜索建议: 通过 Completion Suggester 和 Search-as-you-type 等字段类型,实现高效的自动补全和搜索纠错("你的意思是?")功能。
-
地理位置搜索与分析: 原生支持 Geo-point 和 Geo-shape,可以轻松实现"查找我附近1公里的餐厅"或"判断点是否在多边形内"等功能。
-
数据可视化: 与 Kibana 紧密集成,可以将搜索和聚合的结果以丰富的图表、仪表盘形式展现出来。
-
机器学习: 集成机器学习功能,可以自动检测时间序列数据中的异常,或进行预测性分析。
-
监控与指标采集: ELK Stack 中的核心组件,专门用于收集和分析日志、指标数据。
-
安全特性: 提供身份认证、授权、加密通信等安全功能。
4、限制
-
非事务性:
-
问题: 不支持 ACID 事务。无法保证跨多个文档的写入操作的原子性。
-
场景: 不适合金融交易、库存扣减等需要强一致性的场景。
-
-
实时性:
- 问题: 近实时而非严格实时。对于要求毫秒级数据可见性的场景(如极高频的交易系统)可能不适用。
-
更新开销大:
- 问题: 更新一个文档实际上是"删除旧文档,索引新文档"的过程。频繁更新代价很高。
-
资源消耗:
- 问题: 对内存和 CPU 消耗较高,尤其是为了提升性能而将索引加载到内存中时。
-
数据关系处理弱:
- 问题: 不擅长处理复杂的关联关系(如多表 JOIN)。虽然有
nested和join数据类型,但性能损耗大,使用复杂。它更适合"宽表"和"扁平化"的数据模型。
- 问题: 不擅长处理复杂的关联关系(如多表 JOIN)。虽然有
-
脑裂问题:
- 问题: 在分布式集群中,如果网络发生分区,可能导致出现多个"主节点",造成数据不一致。需要通过合理的集群配置来避免。
5、应用场景
-
网站搜索 / 电商平台搜索
-
描述: 为网站或应用提供强大的搜索功能,支持关键词搜索、过滤器、排序、相关性排名、搜索建议等。
-
例子: 维基百科、GitHub、电商网站的商品搜索。
-
-
日志管理与分析
-
描述: ELK Stack 的经典应用。收集来自各种服务器、应用程序、网络的日志,进行集中存储、搜索、分析和可视化。
-
例子: 运维人员通过 Kibana 仪表板实时监控系统错误、追踪性能瓶颈、进行安全审计。
-
-
应用程序性能监控
-
描述: 收集应用程序的性能指标(如响应时间、错误率、吞吐量),通过聚合和分析来监控应用健康状态。
-
例子: 使用 Elasticsearch 和 APM 服务来追踪微服务架构中的请求链路。
-
-
安全分析
- 描述: 分析来自防火墙、入侵检测系统、终端安全软件的安全事件日志,以检测潜在威胁和异常行为。
-
商业智能与数据分析
- 描述: 借助 Elasticsearch 强大的聚合(Aggregations) 功能,从数据中提取复杂的统计信息,进行用户行为分析或业务指标统计。
-
全文检索与文档管理系统
- 描述: 为企业内部文档、知识库、法律文书等提供全文检索能力。
-
内容推荐
- **描述:**根据用户的历史行为和偏好,Elasticsearch 可以助力内容推荐系统,例如在音乐或视频平台上推荐相似内容。
6、使用代码示例(Java)
添加依赖:
XML
<!-- 新的 Elasticsearch Java API Client (推荐用于8.x及以上版本的ES服务端) -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.11.3</version>
</dependency>
<!-- 需要同时引入RestClient和Jackson -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>8.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
重要提示 :选择客户端时,必须严格遵循官方发布的版本对照表,确保客户端与服务端版本兼容。例如,不能使用8.x的Java API Client连接6.x的ES实例。
初始化客户端:
java
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
public class ElasticsearchClientFactory {
public static ElasticsearchClient createClient() {
// 创建底层 REST client,配置ES服务器地址和端口
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200, "http")
).build();
// 使用Jackson作为JSON映射器创建Transport层
RestClientTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper()
);
// 创建并返回类型安全的ElasticsearchClient
return new ElasticsearchClient(transport);
}
}
索引操作:创建索引、设置映射(Mapping)
java
import co.elastic.clients.elasticsearch.indices.CreateIndexTemplateRequest;
import co.elastic.clients.elasticsearch.indices.IndexTemplate;
import co.elastic.clients.elasticsearch.indices.put_index_template.IndexTemplateMapping;
// 使用经典模板方式创建索引和Mapping
CreateIndexTemplateRequest request = CreateIndexTemplateRequest.of(b -> b
.name("my-index-template")
.indexPatterns("my-index-*")
.template(IndexTemplateMapping.of(m -> m
.mappings(mapping -> mapping
.properties("id", prop -> prop.long_(l -> l))
.properties("name", prop -> prop.text(t -> t.store(true).analyzer("ik_smart"))) // 使用IK分词器
.properties("age", prop -> prop.integer(i -> i.store(true)))
.properties("desc", prop -> prop.text(t -> t.store(true).analyzer("ik_max_word"))) // 使用IK分词器
.properties("registerTime", prop -> prop.date(d -> d.format("yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")))
)
)
);
esClient.indices().createIndexTemplate(request);
文档操作:添加/更新文档
java
import co.elastic.clients.elasticsearch.core.IndexRequest;
import co.elastic.clients.elasticsearch.core.IndexResponse;
// 准备一个简单的POJO作为文档数据,或者使用Map
public class User {
private String id;
private String name;
private Integer age;
private String desc;
private String registerTime;
// ... getters and setters
}
User user = new User();
user.setId("1");
user.setName("不焦躁的程序员");
user.setAge(10);
user.setDesc("这是一个测试用户");
user.setRegisterTime("2023-10-25 12:00:00");
IndexRequest<Object> indexRequest = IndexRequest.of(b -> b
.index("testusers") // 索引名称
.id(user.getId()) // 文档ID
.document(user) // 文档源
);
IndexResponse response = esClient.index(indexRequest);
文档操作:查询文档
java
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
// 构建一个match查询
SearchRequest searchRequest = SearchRequest.of(s -> s
.index("testusers")
.query(q -> q
.match(t -> t
.field("name")
.query("程序员")
)
)
);
SearchResponse<User> searchResponse = esClient.search(searchRequest, User.class);
// 处理搜索结果
for (Hit<User> hit : searchResponse.hits().hits()) {
User foundUser = hit.source();
System.out.println("找到用户: " + foundUser.getName());
}
文档操作:删除文档
java
import co.elastic.clients.elasticsearch.core.DeleteRequest;
import co.elastic.clients.elasticsearch.core.DeleteResponse;
DeleteRequest deleteRequest = DeleteRequest.of(b -> b
.index("testusers")
.id("1")
);
DeleteResponse deleteResponse = esClient.delete(deleteRequest);