xancel的自留地
文章
技术

Elasticsearch 入门

2026年3月16日 4 分钟阅读 浏览 1 喜欢 0 评论 0
AI 摘要

Elasticsearch是一个基于Lucene的分布式搜索引擎,用于解决传统数据库在全文检索、海量数据搜索和实时分析方面的不足。它通过倒排索引、分词器和相关性评分等核心特性实现高效搜索,并可与Spring Boot集成进行开发。在生产环境中,需注意数据同步、高可用部署及深度分页等进阶问题。

引入

参考整理自up主鱼皮的教程:https://www.bilibili.com/opus/1179932214682451986

1.基础学习

一、 什么是 Elasticsearch?

Elasticsearch(简称 ES) 是一个基于 Lucene 的开源、分布式、RESTful 搜索引擎。它不仅是一个数据库,更是为了解决传统关系型数据库(如 MySQL)在全文检索海量数据搜索实时分析方面的不足而设计的。

为什么不用 MySQL 的 LIKE 查询?

  • 性能差LIKE '%关键词%' 无法利用索引,数据量大时查询极慢。
  • 结果不精准:无法根据相关性进行排序(比如“最匹配”的内容排在前面)。
  • 缺乏分词能力:难以处理复杂的自然语言搜索。

二、 核心概念与实战操作

1. 核心术语对照

ES 概念MySQL 对应概念说明
Index (索引)Table (表)存储数据的容器。
Mapping (映射)Schema (表结构)定义字段类型、分词规则等。
Document (文档)Row (行)以 JSON 格式存储的具体数据。

2. 基础操作(DSL 语法)

ES 使用 JSON 格式的 DSL(领域特定语言)进行交互,建议配合可视化工具 Kibana 使用。

  • 创建索引:需指定字段类型,也就是映射mapping。text 用于全文检索(会分词),keyword 用于精确匹配(不分词)。

  • 插入数据:直接发送 POST 请求提交 JSON 文档。

  • 搜索数据:使用 match 进行全文搜索,ES 会自动分词并计算匹配度。

3. Java 客户端开发

在生产中,通常使用 Spring Data Elasticsearch。它允许开发者通过定义接口(继承 ElasticsearchRepository)并使用注解(如 @Document)来操作 ES,就像使用 MyBatis-Plus 一样简单。后文会有代码示例


三、 ES 为什么这么快?(核心特性)

1. 倒排索引 (Inverted Index)

这是 ES 的灵魂。它不直接存整行数据,而是记录单词 -> 文档 ID 列表的映射。搜索时,系统先对搜索词进行分词,然后通过倒排索引直接定位包含这些词的文档,无需全表扫描。

2. 分词器 (Analyzer)

  • 标准分词器:对中文支持较差(按字拆分)。
  • IK 分词器:中文搜索必备,提供 ik_smart(智能切分)和 ik_max_word(最细粒度切分)模式。

3. 相关性评分

ES 默认采用 BM25 算法,通过词频(TF)逆文档频率(IDF)文档长度来计算分数(_score),确保用户最想要的结果排在最前面。


四、 生产环境的挑战与方案

1. 数据同步

由于 ES 和 MySQL 是独立系统,需要解决数据一致性问题:

  • 定时任务:适合实时性要求不高的场景。
  • 双写:代码层面同时写入两端,但需处理写入失败。
  • Canal 监听:实时监听 MySQL 的 Binlog 变更并同步,延迟低。

2. 高可用与扩展

  • 集群部署:通过多节点(主节点、数据节点)保证服务不宕机。
  • 分片 (Shard):将大索引拆分存放在不同节点,提升存储上限和并发能力。
  • 副本 (Replica):分片的备份,防止节点损坏导致数据丢失。

3. 常见进阶问题

  • 深度分页:ES 默认限制查询前 10,000 条。深层次翻页建议使用 search_after
  • ELK 生态:Elasticsearch (存储) + Logstash (收集) + Kibana (展示),是目前主流的日志分析方案。

建议:学习 ES 最好的方式是动手实践,先在本地搭建一个单机版环境,尝试通过 Kibana 练习各种 DSL 查询。

2.代码实现

我们通常会选择在 Spring Boot 项目中集成 Spring Data Elasticsearch。这是目前 Java 后端最主流、也是最高效的开发方式。

1. 环境准备与配置

首先,在项目的 pom.xml 中引入依赖:

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

application.yml 中配置 ES 服务器地址:

yaml
spring:
  elasticsearch:
    uris: http://localhost:9200


2. 定义实体类(Mapping 映射)

通过注解将 Java 对象映射为 ES 的索引结构。text 类型用于搜索,keyword 用于精确过滤。

java
@Data
@Document(indexName = "article") // 指定索引名称
public class Article {
    @Id
    private Long id;

    // type = Text 表示支持全文检索,analyzer 指定分词器
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String title;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String content;

    // type = Keyword 表示不分词,直接匹配
    @Field(type = FieldType.Keyword)
    private String category;

    @Field(type = FieldType.Date, format = DateFormat.basic_date_time)
    private LocalDateTime createTime;
}


3. 编写 Repository 接口

这是 Spring Data 的精髓,你只需要定义方法名,框架会自动帮你生成 DSL 查询语句。

java
public interface ArticleRepository extends ElasticsearchRepository<Article, Long> {
    
    // 自动解析:根据标题全文检索
    List<Article> findByTitle(String title);
    
    // 自动解析:根据分类精确匹配
    List<Article> findByCategory(String category);
    
    // 复合查询:标题匹配且分类一致
    List<Article> findByTitleAndCategory(String title, String category);
}


4. 高级搜索:使用 ElasticsearchTemplate

如果 Repository 的方法名无法满足复杂的业务逻辑(如高亮、聚合、复杂的布尔过滤),可以使用 ElasticsearchTemplate

示例:带高亮和过滤的复合搜索

java
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;

public void complexSearch(String keyword) {
    // 1. 构建查询条件(类似 SQL 的 WHERE)
    NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.boolQuery()
                    .must(QueryBuilders.matchQuery("title", keyword)) // 必须匹配标题
                    .should(QueryBuilders.matchQuery("content", keyword))) // 内容匹配可加分
            .withHighlightFields(new HighlightBuilder.Field("title")) // 设置高亮字段
            .withPageable(PageRequest.of(0, 10)) // 分页
            .build();

    // 2. 执行搜索
    SearchHits<Article> searchHits = elasticsearchRestTemplate.search(searchQuery, Article.class);

    // 3. 处理高亮结果
    searchHits.getSearchHits().forEach(hit -> {
        Article article = hit.getContent();
        List<String> highlightTitle = hit.getHighlightField("title");
        if (!highlightTitle.isEmpty()) {
            article.setTitle(highlightTitle.get(0)); // 替换为带 <em> 标签的内容
        }
        System.out.println("搜索结果: " + article);
    });
}


5. 核心逻辑总结

在实际开发中,代码流程通常是:

  1. MySQL 写入:文章发布成功。
  2. 触发同步:通过 articleRepository.save(article) 同步到 ES。
  3. 用户搜索:调用自定义的 search 方法,获取经过分词和评分排序后的结果。

注意:
在本地调试时,请确保已经安装了 IK 分词器 插件,否则代码中的 analyzer = "ik_max_word" 会导致 ES 报错。

喜欢 0
评论区在赶来的路上...