Elasticsearch实战指南

Elasticsearch 实战指南

Elasticsearch 是搜索、日志分析领域的事实标准,也是 CSDN 后端与大数据板块的高流量话题。本文从核心概念讲起,覆盖索引与文档、分词器、查询 DSL、聚合分析、Spring Boot 集成、日志平台(ELK)、性能优化与常见坑,全程带命令和代码。


一、Elasticsearch 是什么

1.1 定位

基于 Lucene 的分布式搜索引擎------本质是一个"存文档 + 全文检索 + 聚合分析"的 NoSQL 数据库。

和 MySQL 的区别:

维度 MySQL Elasticsearch
查询 精确匹配、关系型 全文检索、模糊搜索
性能 千万级还行 亿级毫秒响应
扩展 分库分表麻烦 天然分布式
一致性 强一致(事务) 近实时(秒级可见)
典型场景 业务数据存储 搜索、日志、分析

1.2 典型场景

  • 站内搜索:电商商品搜索、文档搜索、论坛;
  • 日志分析:ELK(Elasticsearch + Logstash + Kibana);
  • 指标监控:APM、业务指标;
  • 推荐/补全:搜索建议、纠错。

1.3 核心概念

复制代码
Elasticsearch          = MySQL 的"实例/集群"
Index(索引)          = 数据库(database)
Type(类型,已废弃)   = 表(7.x 移除)
Document(文档)       = 一行记录(JSON)
Field(字段)          = 一列
Mapping(映射)        = 表结构(字段类型定义)

一条文档示例:

json 复制代码
{
  "_index": "products",
  "_id": "1001",
  "name": "iPhone 16 Pro",
  "price": 7999.00,
  "tags": ["手机", "苹果"],
  "created_at": "2026-09-30T10:00:00"
}

二、安装与快速上手

2.1 安装

bash 复制代码
# Docker 最快
docker run -d --name es \
  -p 9200:9200 -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
  docker.elastic.co/elasticsearch/elasticsearch:8.13.0

验证:

bash 复制代码
curl http://localhost:9200

2.2 索引操作

bash 复制代码
# 创建索引
PUT /products
{
  "settings": { "number_of_shards": 3, "number_of_replicas": 1 }
}

# 查看索引
GET /products

# 删除索引(慎用!)
DELETE /products

2.3 文档 CRUD

bash 复制代码
# 新增/覆盖文档
PUT /products/_doc/1001
{
  "name": "iPhone 16 Pro",
  "price": 7999,
  "tags": ["手机", "苹果"]
}

# 查询
GET /products/_doc/1001

# 更新(局部)
POST /products/_update/1001
{
  "doc": { "price": 7499 }
}

# 删除
DELETE /products/_doc/1001

三、Mapping:字段类型设计

3.1 核心类型

类型 用途
text 全文检索(分词、模糊匹配)
keyword 精确匹配(等值、排序、聚合)
integer / long 整数
float / double 浮点
boolean 布尔
date 日期
object / nested 嵌套对象

3.2 为什么要设计 Mapping

ES 会自动推断类型,但默认把字符串设为 text + keyword------text 不能排序和聚合,keyword 不能全文检索。生产必须显式定义:

json 复制代码
PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "fields": {
          "keyword": { "type": "keyword" }   // name.keyword 可精确匹配/排序
        }
      },
      "price": { "type": "double" },
      "tags": { "type": "keyword" },
      "description": { "type": "text" },
      "created_at": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" }
    }
  }
}

关键点:

  • 加字段容易,改字段类型要重建索引(reindex);
  • 线上先想清楚字段类型,别依赖自动映射。

3.3 重建索引(reindex)

bash 复制代码
# 建新索引(新 mapping)
PUT /products_v2 { ... }

# 数据搬迁
POST /_reindex
{
  "source": { "index": "products" },
  "dest": { "index": "products_v2" }
}

# 改别名
POST /_aliases
{
  "actions": [
    { "remove": { "index": "products", "alias": "products_alias" } },
    { "add": { "index": "products_v2", "alias": "products_alias" } }
  ]
}

生产规范:业务永远用别名访问,重建索引无感切换。


四、分词器(Analyzer)

4.1 为什么需要分词

搜索"苹果手机",如果文档是"iPhone",不分词就匹配不上。分词器把文本拆成词元(token):

复制代码
"苹果手机很好用" → [苹果, 手机, 很好, 用]

4.2 中文分词(IK)

ES 自带 standard 分词器对中文是逐字拆,效果差------中文场景必须装 IK 分词器:

bash 复制代码
./bin/elasticsearch-plugin install analysis-ik

测试:

bash 复制代码
POST /_analyze
{
  "analyzer": "ik_max_word",
  "text": "中华人民共和国成立了"
}
# 输出: [中华人民共和国, 中华人民, 中华, 华人, 人民共和国, 人民, 共和国, 共和, 国, 成立, 了]

IK 两种模式:

  • ik_max_word:最细粒度(索引用,召回全);
  • ik_smart:最粗粒度(搜索用,精确)。

4.3 自定义分词

json 复制代码
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "filter": ["lowercase"]
        }
      }
    }
  }
}

五、查询 DSL(重点)

5.1 基本查询结构

json 复制代码
GET /products/_search
{
  "query": { ... },        // 查询条件
  "from": 0, "size": 10,   // 分页
  "sort": [ { "price": "desc" } ],   // 排序
  "_source": ["name", "price"]       // 只返回指定字段
}

5.2 match(全文检索)

json 复制代码
{ "query": { "match": { "name": "苹果手机" } } }

match 与 term 的区别(必考):

  • match:先分词再匹配,适合 text 字段;
  • term:精确匹配(不分词),适合 keyword 字段:
json 复制代码
{ "query": { "term": { "tags": "苹果" } } }

坑 :term 查 text 字段基本查不到(text 被分词了)。

5.3 组合查询 bool

json 复制代码
{
  "query": {
    "bool": {
      "must":    [ { "match": { "name": "手机" } } ],      // AND
      "filter":  [ { "term": { "tags": "苹果" } } ],        // 过滤(不算分,快)
      "should":  [ { "match": { "description": "拍照" } } ], // OR(加分)
      "must_not": [ { "term": { "status": "下架" } } ]       // 排除
    }
  }
}

filter vs must:filter 不参与相关度评分,可缓存,性能更好------纯过滤条件用 filter。

5.4 范围与多值

json 复制代码
{ "query": { "range": { "price": { "gte": 1000, "lte": 8000 } } } }

{ "query": { "terms": { "tags": ["苹果", "华为"] } } }

5.5 分页与深分页

json 复制代码
{ "from": 0, "size": 20 }

深分页问题(同 MySQL):

  • from+size 超过 1 万条会被拒绝(默认 max_result_window=10000);
  • 深度翻页用 search_after(游标):
json 复制代码
POST /products/_search
{
  "size": 20,
  "sort": [ { "price": "asc" }, { "_id": "asc" } ],
  "search_after": [ 4999, "1001" ]   // 上一页最后一条的值
}

5.6 高亮

json 复制代码
{
  "query": { "match": { "name": "苹果" } },
  "highlight": {
    "fields": { "name": {} },
    "pre_tags": ["<em>"], "post_tags": ["</em>"]
  }
}

六、聚合分析(Aggregation)

6.1 三种聚合

  • 桶(bucket):分组,类似 GROUP BY;
  • 指标(metric):统计,类似 MAX/AVG/SUM;
  • 管道(pipeline):聚合结果再聚合。

6.2 实战:电商统计

json 复制代码
POST /orders/_search
{
  "size": 0,      // 只要聚合结果
  "aggs": {
    "by_category": {                    // 按分类分组
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } },   // 每组均价
        "total_sales": { "sum": { "field": "amount" } }, // 每组总额
        "by_status": {                   // 每组内再按状态分
          "terms": { "field": "status" }
        }
      }
    }
  }
}

6.3 日期直方图(按时间统计)

json 复制代码
{
  "size": 0,
  "aggs": {
    "sales_per_day": {
      "date_histogram": {
        "field": "created_at",
        "calendar_interval": "day"
      }
    }
  }
}

七、Spring Boot 集成

7.1 依赖与配置

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
yaml 复制代码
spring:
  elasticsearch:
    uris: http://localhost:9200

7.2 实体与 Repository

java 复制代码
@Document(indexName = "products")
public class Product {
    @Id
    private String id;
    private String name;
    private Double price;
    private List<String> tags;
    // getter/setter
}
java 复制代码
public interface ProductRepository
        extends ElasticsearchRepository<Product, String> {

    // 方法名自动生成查询
    List<Product> findByNameContaining(String keyword);

    // 复杂查询用 @Query(Query DSL 字符串)
    @Query("{\"bool\": {\"must\": [{\"match\": {\"name\": \"?0\"}}]}}")
    List<Product> searchByName(String keyword);
}

7.3 自定义查询

java 复制代码
@RestController
public class ProductSearchController {

    @Autowired
    private ElasticsearchOperations esOps;

    public List<Product> search(String keyword, double minPrice) {
        Query query = NativeQuery.builder()
            .withQuery(q -> q.bool(b -> b
                .must(m -> m.match(t -> t.field("name").query(keyword)))
                .filter(f -> f.range(r -> r
                    .field("price").gte(JsonData.of(minPrice))))
            ))
            .build();
        return esOps.search(query, Product.class).stream()
            .map(hit -> hit.getContent()).toList();
    }
}

八、ELK 日志平台

8.1 架构

复制代码
应用日志 → Filebeat(采集)→ Logstash(处理)→ Elasticsearch(存储索引)
                                                      ↓
                                                  Kibana(可视化)
  • Filebeat:轻量采集器,读日志文件发到 ES/Logstash;
  • Logstash:解析、过滤、转换(grok 解析日志格式);
  • Kibana:搜索、图表、仪表盘。

8.2 Logstash 配置示例

ruby 复制代码
input {
  beats { port => 5044 }
}

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:log_time} %{LOGLEVEL:level} %{GREEDYDATA:msg}" }
  }
  date {
    match => ["log_time", "yyyy-MM-dd HH:mm:ss,SSS"]
  }
}

output {
  elasticsearch {
    hosts => ["http://es:9200"]
    index => "app-logs-%{+YYYY.MM.dd}"
  }
}

8.3 日志索引生命周期

日志量大,按天建索引,配合索引生命周期管理(ILM):

  • 3 天内:热节点(读写快);
  • 30 天内:温节点(压缩);
  • 之后:删除。

九、性能优化

9.1 写入优化

  • 批量写入(_bulk):一次几千条,别一条一条写;
  • 调大 refresh_interval:默认 1s 刷新,可设 30s(日志场景);
  • 关闭副本再写:导入数据时 replicas=0,导完恢复。
json 复制代码
PUT /logs/_settings
{ "index": { "refresh_interval": "30s" } }

9.2 查询优化

  • 用 filter 不用 must(纯过滤可缓存);
  • 不要深分页(用 search_after);
  • 只返回需要的字段(_source 裁剪);
  • 避免 wildcard 模糊查询(性能差);
  • 大聚合限制 size。

9.3 分片设计

  • 单分片 30-50GB 为宜;
  • 分片数 = 节点数 × 1~2(每节点 3-5 个分片合适);
  • 分片太多:资源浪费、查询慢。

9.4 内存设置

yaml 复制代码
# jvm.options
-Xms4g
-Xmx4g

红线:堆不要超过物理内存一半,留一半给操作系统页缓存(ES 大量用 OS cache)。


十、常见坑速查

现象 原因 解决
中文搜不到 没装 IK 分词器 装 IK,字段用 ik 分词
term 查 text 查不到 text 被分词 用 keyword 字段或 match
聚合报错 text 字段不能聚合 用 .keyword 字段
深分页报错 超过 max_result_window search_after
索引变黄/红 副本分配失败/磁盘满 看集群健康、加磁盘
写入慢 单条写入/refresh 频繁 批量 + 调 refresh_interval
数据不实时 近实时(1s 刷新) 接受或调 refresh
字段类型错了 依赖自动映射 显式 mapping,reindex

本章小结

Elasticsearch 的实战核心:Mapping 设计(text/keyword)、查询 DSL(match/term/bool/range)、聚合分析、分页策略(search_after)、性能优化。先学会用 curl 玩转 DSL,再套框架(Spring Data/ELK)。

下一篇讲设计模式------Java 面试与代码质量的常青话题。

(全文完)

相关推荐
老A的AI实验室1 小时前
赛博月刊 #2026年9月
大数据·人工智能·深度学习·ai·llm
卷毛迷你猪1 小时前
快速实验篇(B03) 用户行为漏斗与转化率
大数据·hive·hadoop
IT大白鼠2 小时前
搜索系列 · 第 04 篇——部署实操:内网集群落地
运维·elasticsearch·nosql
径硕科技JINGdigital2 小时前
出海企业想要借助统一平台接入国际主流基础模型,可选哪些云上生成式 AI 平台?
大数据·人工智能
Elastic 中国社区官方博客2 小时前
使用 Jev 作为 search reranker:基准测试与实现方法
大数据·运维·elasticsearch·全文检索
卷毛迷你猪2 小时前
快速实验篇(B01)用户行为日志画像与 DWD 标准化
大数据·hive·hadoop
四季豆333 小时前
中翰软件调整战略定位:以数据与知识“双治理”搭建企业AI落地桥梁
大数据
T06205143 小时前
【2026年更新】全国自然保护区矢量数据,列表+边界+功能区
大数据
LaughingZhu3 小时前
Product Hunt 每日热榜 | 2026-10-01
数据库·人工智能·深度学习·神经网络·搜索引擎