Elasticsearch 超全入门实战教程
博客前言
Elasticsearch(简称ES)是一款基于Lucene实现的分布式、高扩展、实时全文搜索引擎与数据分析引擎,广泛应用于站内搜索、日志分析、大数据检索、商品搜索、智能推荐等场景。
相比于MySQL模糊查询(like %xxx%),ES基于倒排索引实现分词检索,支持海量数据毫秒级查询、模糊匹配、高亮显示、分组聚合统计,是后端开发必备核心技能。
本文从零梳理ES核心原理、基础运维、全套可复制DSL练习Demo、避坑指南,最后提供SpringBoot 整合ES完整CRUD企业级代码,零基础可直接上手实战、用于项目开发与学习面试。
一、Elasticsearch 核心基础概念(必懂)
为方便理解,将ES核心概念与MySQL做精准类比,快速建立认知:
| ES核心名词 | MySQL对应概念 | 详细说明 |
|---|---|---|
| Index(索引) | Database(数据库) | 数据存储容器,同一类业务数据统一存放,支持分片、副本扩容 |
| Mapping(映射) | Table(数据表结构) | 定义索引内字段名称、数据类型、分词规则、索引规则,约束数据格式 |
| Document(文档) | Row(数据行) | ES最小数据单元,JSON格式存储,每条文档拥有唯一_id |
| Field(字段) | Column(字段列) | 文档中的单个属性,比如商品标题、价格、分类 |
| Shard(分片) | 无对应概念 | 数据拆分存储,解决单节点数据上限,支持分布式扩容 |
| Replica(副本) | 无对应概念 | 分片备份,保障集群高可用,节点宕机不丢失数据 |
1.1 核心字段类型(高频必考)
ES字段类型决定查询方式,90%新手报错都源于类型使用错误:
-
text:分词字段,支持全文模糊检索,不支持精确匹配、排序、聚合。适用于文章内容、商品标题、简介等长文本。
-
keyword:不分词,完整精确匹配,支持排序、分组聚合、去重。适用于分类、标签、状态、手机号、账号等固定文本。
-
数值类型:long、integer、double、float,用于价格、数量、ID,支持范围查询、数值聚合。
-
date:日期类型,支持标准日期格式,可按时间范围检索、时间分组统计。
-
boolean:布尔类型,true/false,用于状态开关。
1.2 核心特性
-
默认端口:服务端口9200(HTTP)、集群通信端口9300(TCP)
-
通信方式:RESTful JSON API,所有操作均通过JSON请求实现
-
数据结构:天然JSON结构化存储,无需提前建表(推荐手动建Mapping)
-
检索核心:倒排索引,通过关键词匹配文档,而非逐行扫描
二、ES集群运维基础DSL(生产常用)
所有语句可直接在Kibana-Dev Tools、Postman、Apifox中执行,开箱即用。
2.1 集群状态查询
Plain
# 查看集群整体健康状态(核心)
GET /_cluster/health
# 查看所有索引列表(带详细信息)
GET /_cat/indices?v
# 查看集群节点信息
GET /_cat/nodes?v
# 查看集群分片分配情况
GET /_cat/shards?v
集群状态说明:
-
green:主分片、副本分片全部正常,集群完全健康
-
yellow:主分片正常,副本分片未分配(单节点集群默认状态,不影响使用)
-
red:主分片丢失,对应索引数据不可用,业务报错
三、索引 Index 全套操作 DSL 练习Demo
3.1 创建索引(手动指定Mapping,生产规范用法)
生产环境禁止动态自动创建索引,必须手动定义字段类型,避免类型混乱导致查询异常。
Plain
# 创建商品索引 products
PUT /products
{
"settings": {
"number_of_shards": 1, # 主分片数,单机测试设1
"number_of_replicas": 0 # 副本数,单机设0,避免yellow状态
},
"mappings": {
"properties": {
"title": {"type": "text", "analyzer": "ik_max_word"}, # 商品标题,中文分词
"desc": {"type": "text", "analyzer": "ik_max_word"}, # 商品描述
"category": {"type": "keyword"}, # 商品分类(精确匹配)
"price": {"type": "double"}, # 商品价格
"stock": {"type": "integer"}, # 库存数量
"isSale": {"type": "boolean"}, # 是否在售
"createTime": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss"} # 创建时间
}
}
}
3.2 索引查询、删除、更新
Plain
# 查询索引结构Mapping
GET /products/_mapping
# 查询索引配置信息
GET /products/_settings
# 删除索引(高危操作,生产谨慎!)
DELETE /products
四、文档 Document CRUD 全套练习Demo
针对products索引,完成增删改查全套实操语句,覆盖日常开发所有场景。
4.1 新增文档
Plain
# 1. 指定ID新增文档
PUT /products/_doc/1
{
"title": "机械游戏键盘",
"desc": "青轴机械键盘,电竞专用,手感清脆",
"category": "数码外设",
"price": 599.9,
"stock": 120,
"isSale": true,
"createTime": "2026-08-28 10:00:00"
}
# 2. 自动生成ID新增文档(POST方式)
POST /products/_doc
{
"title": "无线静音鼠标",
"desc": "办公静音无线鼠标,长续航",
"category": "数码外设",
"price": 199.9,
"stock": 200,
"isSale": true,
"createTime": "2026-08-28 10:10:00"
}
4.2 查询文档
Plain
# 根据ID单条查询
GET /products/_doc/1
# 查询所有文档(size指定返回条数)
GET /products/_search
{
"query": {
"match_all": {}
},
"size": 100
}
4.3 更新文档(两种方式,重点区分)
Plain
# 1. 全量更新(覆盖所有字段,缺失字段会丢失)
PUT /products/_doc/1
{
"title": "机械游戏键盘",
"desc": "青轴机械键盘,电竞专用,手感清脆",
"category": "数码外设",
"price": 559.9,
"stock": 100,
"isSale": true,
"createTime": "2026-08-28 10:00:00"
}
# 2. 局部更新(推荐!只修改指定字段,不影响其他数据)
POST /products/_update/1
{
"doc": {
"price": 529.9,
"stock": 95
}
}
4.4 删除文档
Plain
# 根据ID删除单条文档
DELETE /products/_doc/1
4.5 批量导入数据 _bulk(高效批量操作)
批量新增/更新/删除数据,适合初始化数据、批量同步业务数据,效率远高于单条操作。
Plain
POST /products/_bulk
{"index":{"_id":2}}
{"title":"降噪蓝牙耳机","desc":"无线降噪入耳式耳机","category":"数码影音","price":299.9,"stock":300,"isSale":true,"createTime":"2026-08-28 10:20:00"}
{"index":{"_id":3}}
{"title":"纯棉休闲T恤","desc":"夏季宽松百搭纯棉T恤","category":"服饰穿搭","price":79.9,"stock":500,"isSale":true,"createTime":"2026-08-28 10:30:00"}
{"index":{"_id":4}}
{"title":"电竞游戏耳机","desc":"头戴式降噪电竞耳机","category":"数码外设","price":399.9,"stock":180,"isSale":false,"createTime":"2026-08-28 10:40:00"}
五、核心 Query DSL 高级查询(开发高频)
DSL查询是ES核心,所有复杂检索、过滤、分页、排序、高亮均基于此实现。
5.1 全文分词查询 match(text字段专用)
自动分词匹配,模糊检索,适合搜索商品标题、文章内容。
Plain
# 搜索标题包含「键盘」的商品
GET /products/_search
{
"query": {
"match": {
"title": "键盘"
}
}
}
5.2 精确查询 term(keyword字段专用)
不分词,完全精确匹配,用于分类、标签、状态筛选。
Plain
# 精确查询分类为「数码外设」的商品
GET /products/_search
{
"query": {
"term": {
"category": "数码外设"
}
}
}
5.3 范围查询 range(数值/时间)
Plain
# 查询价格100~600元的在售商品
GET /products/_search
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 600
}
}
}
}
操作符说明:gte≥、lte≤、gt>、lt<
5.4 多条件组合查询 bool(最常用)
-
must:必须匹配,参与相关性得分计算
-
filter:必须匹配,不计算得分、性能更高,纯过滤优先用filter
-
should:或条件,满足其一即可
-
must_not:必须不匹配,排除条件
Plain
# 标题含「数码」、价格200以上、在售商品
GET /products/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "数码"}}
],
"filter": [
{"range": {"price": {"gte": 200}}},
{"term": {"isSale": true}}
]
}
}
}
5.5 分页、排序、高亮查询
Plain
# 分页+排序+关键词高亮
GET /products/_search
{
"query": {
"match": {
"title": "耳机"
}
},
"from": 0,
"size": 5,
"sort": [
{"price": {"order": "desc"}}
],
"highlight": {
"fields": ["title"],
"pre_tags": "<span style='color:red'>",
"post_tags": "</span>"
}
}
5.6 聚合查询(分组统计,类似MySQL group by)
Plain
# 按商品分类分组,统计每组数量、平均价格
GET /products/_search
{
"size": 0,
"aggs": {
"group_by_category": {
"terms": {"field": "category"}
},
"avg_price": {
"avg": {"field": "price"}
}
}
}
六、ES 高频避坑总结(新手必看)
-
字段类型不匹配:text字段不能用term精确查询,keyword字段不支持分词模糊搜索
-
查询性能问题:纯过滤条件务必放filter,不要放must,大幅提升查询效率
-
文档更新原理:ES更新是「删除旧文档+新建文档」,禁止高频单条更新,批量更新用_bulk
-
禁止动态Mapping:自动创建索引会导致字段类型错乱,引发查询、聚合报错
-
分页深度限制:from+size分页不支持超大深度分页,深分页用scroll或search_after
七、SpringBoot 整合 Elasticsearch 完整 CRUD 实战
基于 Spring Data Elasticsearch 实现,代码简洁、适配企业级开发,包含:依赖配置、实体映射、Repository、Service、Controller、完整测试。
7.1 环境版本适配
-
SpringBoot:2.7.x / 3.2.x
-
Elasticsearch:7.x / 8.x(本文兼容主流版本)
-
JDK:1.8+
7.2 Maven 核心依赖
Plain
<!-- SpringBoot ES 启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- 简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
7.3 application.yml 配置文件
Plain
spring:
elasticsearch:
uris: http://127.0.0.1:9200 # ES服务地址
# 8.x版本如需账号密码认证,追加配置
# username: elastic
# password: 123456
7.4 ES实体类(映射索引结构)
精准对应前文products索引Mapping,绑定字段类型与分词规则
Plain
package com.es.demo.entity;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.Date;
@Data
// 绑定索引名、分片、副本
@Document(indexName = "products", shards = 1, replicas = 0)
public class ProductDoc {
// 文档唯一ID
@Id
private String id;
// 商品标题:分词字段
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;
// 商品描述:分词字段
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String desc;
// 商品分类:精确字段
@Field(type = FieldType.Keyword)
private String category;
// 价格
@Field(type = FieldType.Double)
private Double price;
// 库存
@Field(type = FieldType.Integer)
private Integer stock;
// 是否在售
@Field(type = FieldType.Boolean)
private Boolean isSale;
// 创建时间
@Field(type = FieldType.Date)
private Date createTime;
}
7.5 Repository 持久层(ES数据操作层)
继承ElasticsearchRepository,自带基础CRUD,支持自定义DSL查询
Plain
package com.es.demo.repository;
import com.es.demo.entity.ProductDoc;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductRepository extends ElasticsearchRepository<ProductDoc, String> {
// 自定义方法:根据标题模糊分词查询
List<ProductDoc> findByTitle(String title);
// 根据分类精确查询
List<ProductDoc> findByCategory(String category);
}
7.6 Service 业务层(完整CRUD逻辑)
Plain
package com.es.demo.service;
import com.es.demo.entity.ProductDoc;
import com.es.demo.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
private final ElasticsearchOperations elasticsearchOperations;
// 新增/更新文档(ID存在则更新,不存在则新增)
public void saveProduct(ProductDoc productDoc) {
productRepository.save(productDoc);
}
// 根据ID查询
public Optional<ProductDoc> getById(String id) {
return productRepository.findById(id);
}
// 根据ID删除
public void deleteById(String id) {
productRepository.deleteById(id);
}
// 分页查询所有
public Page<ProductDoc> pageList(int pageNum, int pageSize) {
PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize);
return productRepository.findAll(pageRequest);
}
// 标题模糊搜索
public List<ProductDoc> searchByTitle(String title) {
return productRepository.findByTitle(title);
}
// 分类精确查询
public List<ProductDoc> searchByCategory(String category) {
return productRepository.findByCategory(category);
}
}
7.7 Controller 控制层(接口暴露)
Plain
package com.es.demo.controller;
import com.es.demo.entity.ProductDoc;
import com.es.demo.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/es/product")
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
// 新增/更新商品
@PostMapping("/save")
public String save(@RequestBody ProductDoc productDoc) {
productService.saveProduct(productDoc);
return "操作成功";
}
// 根据ID查询
@GetMapping("/{id}")
public Optional<ProductDoc> getById(@PathVariable String id) {
return productService.getById(id);
}
// 根据ID删除
@DeleteMapping("/{id}")
public String delete(@PathVariable String id) {
productService.deleteById(id);
return "删除成功";
}
// 分页查询
@GetMapping("/page")
public Page<ProductDoc> page(@RequestParam int pageNum, @RequestParam int pageSize) {
return productService.pageList(pageNum, pageSize);
}
// 标题搜索
@GetMapping("/search/title")
public List<ProductDoc> searchTitle(@RequestParam String title) {
return productService.searchByTitle(title);
}
// 分类查询
@GetMapping("/search/category")
public List<ProductDoc> searchCategory(@RequestParam String category) {
return productService.searchByCategory(category);
}
}
7.8 高级自定义DSL查询(复杂业务)
适用于多条件组合、范围、排序、高亮等复杂场景,在Service中新增自定义查询方法:
Plain
// 复杂多条件组合查询
public Page<ProductDoc> complexSearch(String keyword, Double minPrice, int pageNum, int pageSize) {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("title", keyword))
.withFilter(QueryBuilders.rangeQuery("price").gte(minPrice))
.withPageable(PageRequest.of(pageNum - 1, pageSize))
.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC))
.build();
return elasticsearchOperations.search(query, ProductDoc.class).getPageable();
}
八、全文总结
-
ES核心优势:基于倒排索引实现高效全文检索,适配海量数据搜索场景,弥补MySQL模糊查询性能短板;
-
实操核心:区分text/keyword字段类型、熟练掌握bool组合查询、聚合统计、批量操作;
-
开发落地:Spring Data ES 简化开发,基础CRUD开箱即用,复杂业务通过原生DSL实现,完全适配企业级搜索需求。
本文所有DSL语句、Java代码均可直接复制运行,可作为学习笔记、项目模板、面试复习资料。