Elasticsearch全文检索服务

Elasticsearch 学习笔记

一、为什么要用 Elasticsearch?(别再让 MySQL 哭了)

想象一下你在淘宝上搜"手机",结果 MySQL 在后台翻遍所有商品表、用户表、评论表......像在字典里一页一页找"手"字一样,慢得让人想砸电脑

Elasticsearch(简称 ES)就是来拯救世界的!它专为全文检索 而生,支持多字段模糊匹配、高性能搜索、近实时响应。

MySQL:我负责存数据;

ES:我负责让你秒搜到!


二、什么是 Elasticsearch?

  • 本质:基于 Lucene 的分布式全文检索服务器。
  • 接口:提供 RESTful API(用 HTTP 就能操作,超友好)。
  • 定位:不是数据库!是搜索引擎!别拿它当主库用(除非你想半夜被报警电话叫醒)。

三、ES 的核心原理:倒排索引(从"目录"找内容)

正排索引(传统方式):

文档 → 关键词

比如:《新华字典》正文 → 你一页一页翻找"奥利给"

倒排索引(ES 的魔法):

关键词 → 文档

比如:先看目录,"奥利给"在第 888 页 → 直接跳过去!

倒排索引表长这样:
Term(关键词) Document ID(文档ID)
手机 1, 3, 5
Python 2
奥利给 4
分词规则(很讲究):
  1. 不重复:同一个词只存一次。
  2. 停用词过滤:"的、地、得、a、an、the"直接扔掉(谁会搜"的"?)。
  3. 非搜索字段不分词 :比如 image 字段只存图片路径,不用分词。

✅ 搜索时,ES 先在 Term 列表里匹配关键词,再返回对应的文档!


四、ES 客户端怎么连?

客户端类型 状态 说明
TransportClient 已淘汰 8.0+ 版本彻底移除,别用了!
RestHighLevelClient ✅ 官方推荐 通过 HTTP 调用,简单安全

⚠️ 注意:Spring Data Elasticsearch 没有注解版 !别指望 @Document 自动同步(那是 MongoDB 的玩法)。

Spring Boot 启动器

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

五、Elasticsearch 安装指南(Linux 虚拟机版)

💡 前提:虚拟机内存 >1.5G,否则 ES 启动直接报错!

步骤 1:创建专用用户(安全第一!)

bash 复制代码
groupadd es
useradd admin
passwd admin          # 设置密码
usermod -G es admin   # 加入 es 组
su admin              # 切换用户
groups                # 查看组(应包含 es)

步骤 2:授权目录

bash 复制代码
su root
chown -R admin:es /usr/upload
chown -R admin:es /usr/local

步骤 3:上传并解压

bash 复制代码
su admin
cd /usr/upload
tar -zxvf elasticsearch-6.2.3.tar.gz -C /usr/local

步骤 4:配置 elasticsearch.yml

yaml 复制代码
cluster.name: power_shop
node.name: power_shop_node_1
network.host: 0.0.0.0        # 允许外网访问
http.port: 9200
transport.tcp.port: 9300
discovery.zen.ping.unicast.hosts: ["192.168.61.135:9300", "192.168.61.136:9300"]
path.data: /usr/local/elasticsearch-6.2.3/data
path.logs: /usr/local/elasticsearch-6.2.3/logs
http.cors.enabled: true       # 允许跨域(前端调试必备)
http.cors.allow-origin: /.*/

步骤 5:修改系统限制(否则启动失败!)

bash 复制代码
# 文件句柄数
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf

# 虚拟内存映射
echo "vm.max_map_count=655360" >> /etc/sysctl.conf
sysctl -p  # 生效

步骤 6:启动 & 测试

bash 复制代码
su admin
cd /usr/local/elasticsearch-6.2.3/bin
./elasticsearch  # 后台运行加 &

关闭:

bash 复制代码
ps -ef | grep elasticsearch
kill -9 <pid>

测试访问:

👉 http://192.168.61.135:9200/

看到 JSON 返回?恭喜!ES 已上线!


六、ES 入门三板斧:Index、Type、Document

📌 注意:ES 7+ 已废弃 Type,但 6.x 还在用(本文基于 6.2.3)

1. Index 管理(相当于数据库)

创建 Index
json 复制代码
PUT /java06
{
  "settings": {
    "number_of_shards": 2,     // 主分片数(提升并发处理能力)
    "number_of_replicas": 0    // 副本数(单机必须设为 0!否则 yellow/red)
  }
}

🔥 重要 :主分片数一旦创建不可修改 !因为文档分配靠 hash(id) % shards

修改副本数(可以改)
json 复制代码
PUT /java06/_settings
{
  "number_of_replicas": 1
}
删除 Index
json 复制代码
DELETE /java06

2. Type 管理(相当于表)

json 复制代码
POST /java06/_mapping/course
{
  "properties": {
    "name": { "type": "text" },
    "description": { "type": "text" },
    "studymodel": { "type": "keyword" }
  }
}

💡 text 用于全文搜索,keyword 用于精确匹配(如状态码、标签)。


3. Document 管理(相当于行数据)

新增
json 复制代码
POST /java06/course/1
{
  "name": "Python从入门到放弃",
  "description": "人生苦短,我用Python",
  "studymodel": "201002"
}
修改(全量覆盖)
json 复制代码
PUT /java06/course/1
{
  "name": "Python从入门到精通",
  "description": "人生苦短,我用Python",
  "studymodel": "201002"
}
删除 & 查询
json 复制代码
DELETE /java06/course/1
GET /java06/course/1

七、IK 分词器:让中文搜索不再"智障"

默认 ES 对中文按单字分词("手机" → "手"、"机"),搜不到!
解决方案:安装 IK 分词器!

安装步骤:

  1. 下载 elasticsearch-analysis-ik-6.2.3.zip
  2. 解压到 /usr/local/elasticsearch-6.2.3/plugins/ik
  3. 重启 ES

自定义词典(让"奥利给"也能搜!)

  • 配置文件:IKAnalyzer.cfg.xml
  • 扩展词典:main.dic → 添加 奥利给
  • 停用词典:stopword.dic → 添加 的、地、得

⚠️ 文件必须 UTF-8 编码!否则乱码!

两种分词模式:

模式 用途 示例("中华人民共和国")
ik_smart 搜索时用(粗粒度) 中华人民共和国
ik_max_word 写入时用(细粒度) 中华、华人、人民、共和国......

使用示例:

json 复制代码
PUT /news
{
  "settings": {
    "analysis": {
      "analyzer": "ik_max_word"
    }
  },
  "mappings": {
    "article": {
      "properties": {
        "title": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        }
      }
    }
  }
}

八、Field 字段详解(设计表结构的关键)

1. 数据类型

  • 文本text(分词)、keyword(不分词,精确匹配)
  • 数字integer, long, float, double
  • 日期date
  • 布尔boolean

2. 关键属性

属性 作用 示例
type 数据类型 "type": "text"
analyzer 写入时分词器 "analyzer": "ik_max_word"
search_analyzer 搜索时分词器 "search_analyzer": "ik_smart"
index 是否建立索引 "index": false(不参与搜索)
_source 是否存储原始文档 可设置 excludes 隐藏敏感字段

3. 设计原则(灵魂三问):

  1. 这个字段需要分词吗? → 决定 typetext 还是 keyword
  2. 这个字段会被搜索吗? → 决定 index 是否为 true
  3. 这个字段需要返回给前端吗? → 决定是否放入 _source.excludes

九、文档搜索:从简单查询到高亮显示

准备工作:先往索引里塞点数据!

9.1 准备测试数据

我们向 java06/course 索引中插入三条课程数据:

json 复制代码
PUT /java06/course/1
{
  "name": "Bootstrap开发",
  "description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
  "studymodel": "201002",
  "price":38.6,
  "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}

PUT /java06/course/2
{
  "name": "java编程基础",
  "description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
  "studymodel": "201001",
  "price":68.6,
  "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}

PUT /java06/course/3
{
  "name": "spring开发基础",
  "description": "spring 在java领域非常流行,java程序员都在用。",
  "studymodel": "201001",
  "price":88.6,
  "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}

9.2 简单搜索(URI Search)

通过 URL 直接传参查询,适合简单场景。

语法:
复制代码
GET /index/type/_search?q=字段:值&sort=字段:asc/desc
示例:
json 复制代码
GET /java06/course/_search?q=name:spring&sort=price:desc

⚠️ 缺点:条件复杂时 URL 会爆炸!比如"价格1000~5000、销量>500、分页第2页每页40条"------根本写不下!

Java 客户端查询单文档:
java 复制代码
@Test
public void getDoc() throws IOException {
    GetRequest getRequest = new GetRequest("java06","course","1");
    GetResponse getResponse = restHighLevelClient.get(getRequest);
    boolean exists = getResponse.isExists();
    System.out.println(exists);
    String source = getResponse.getSourceAsString();
    System.out.println(source);
}

9.3 DSL 搜索(生产首选!)

DSL(Domain Specific Language)是 ES 推荐的 JSON 格式查询方式,功能强大、结构清晰。

基本语法:
json 复制代码
GET /index/type/_search
{
  "query": { ... },
  "from": 0,
  "size": 10,
  "sort": [...],
  "highlight": { ... }
}

9.3.1 match_all 查询(查所有)
API:
json 复制代码
GET /java06/course/_search
{
  "query": {
    "match_all": {}
  }
}
Java 客户端:
java 复制代码
@Test
public void testMatchAll() throws IOException {
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchAllQuery());
    
    SearchRequest request = new SearchRequest("java06");
    request.types("course");
    request.source(builder);
    
    SearchResponse response = restHighLevelClient.search(request);
    for (SearchHit hit : response.getHits()) {
        System.out.println(hit.getSourceAsString());
    }
}

9.3.2 分页查询
API:
json 复制代码
GET /java06/course/_search
{
  "query": { "match_all": {} },
  "from": 1,   // 从第1条开始(0起始)
  "size": 3,   // 返回3条
  "sort": [{ "price": "asc" }]
}
Java 客户端:
java 复制代码
@Test
public void testSearchPage() throws Exception {
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchAllQuery())
           .from(1)
           .size(5)
           .sort("price", SortOrder.ASC);
    
    searchRequest.source(builder);
    searchResponse = restHighLevelClient.search(searchRequest);
}

9.3.3 match 查询(全文检索)

将搜索词分词后匹配,支持 operator 控制逻辑。

API 示例:
json 复制代码
// OR 逻辑(默认):包含"spring"或"开发"即可
GET /java06/course/_search
{
  "query": {
    "match": {
      "name": "spring开发"
    }
  }
}

// AND 逻辑:必须同时包含"spring"和"开发"
GET /java06/course/_search
{
  "query": {
    "match": {
      "name": {
        "query": "spring开发",
        "operator": "and"
      }
    }
  }
}
Java 客户端:
java 复制代码
@Test
public void testMatchQuery() throws Exception {
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchQuery("name", "spring开发")
                          .operator(Operator.AND));
    searchRequest.source(builder);
    searchResponse = restHighLevelClient.search(searchRequest);
}

9.3.4 multi_match 查询(多字段匹配)

在多个字段中搜索同一个关键词。

API:
json 复制代码
GET /java06/course/_search
{
  "query": {
    "multi_match": {
      "query": "开发",
      "fields": ["name", "description"]
    }
  }
}
Java 客户端:
java 复制代码
@Test
public void testMultiMatchQuery() throws Exception {
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.multiMatchQuery("开发", "name", "description"));
    searchRequest.source(builder);
    searchResponse = restHighLevelClient.search(searchRequest);
}

9.3.5 bool 查询(组合条件)

mustshouldmust_not 组合复杂逻辑。

API 示例:
json 复制代码
// 必须 name 包含"开发" 且 price 在 50~100 之间
GET /java06/course/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "开发" } },
        { "range": { "price": { "gte": 50, "lte": 100 } } }
      ]
    }
  }
}
Java 客户端:
java 复制代码
@Test
public void testBooleanMatch() throws IOException {
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
        .must(QueryBuilders.matchQuery("name", "开发"))
        .must(QueryBuilders.rangeQuery("price").gte(50).lte(100));
    
    SearchSourceBuilder builder = new SearchSourceBuilder().query(boolQuery);
    searchRequest.source(builder);
    SearchResponse response = restHighLevelClient.search(searchRequest);
}

9.3.6 filter 查询(高效过滤)

filter 不计算相关度分数,性能更高,适合范围、精确匹配等场景。

API 对比:
json 复制代码
// 普通查询(计算分数)
"must": [ { "range": { "price": { "gte": 10, "lte": 100 } } } ]

// filter(不计算分数,更快)
"filter": { "range": { "price": { "gte": 10, "lte": 100 } } }

完整示例:

json 复制代码
GET /java06/course/_search
{
  "query": {
    "bool": {
      "must": [ { "match": { "name": "开发" } } ],
      "filter": { "range": { "price": { "gte": 1, "lte": 100 } } }
    }
  }
}
Java 客户端:
java 复制代码
@Test
public void testFilterQuery() throws IOException {
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
        .must(QueryBuilders.matchQuery("name", "开发"))
        .filter(QueryBuilders.rangeQuery("price").gte(10).lte(100));
    
    SearchSourceBuilder builder = new SearchSourceBuilder().query(boolQuery);
    searchRequest.source(builder);
    searchResponse = restHighLevelClient.search(searchRequest);
}

9.3.7 highlight 高亮显示

让搜索关键词在结果中高亮显示,提升用户体验!

API:
json 复制代码
GET /java06/course/_search
{
  "query": { "match": { "name": "开发" } },
  "highlight": {
    "pre_tags": ["<font color='red'>"],
    "post_tags": ["</font>"],
    "fields": { "name": {} }
  }
}
Java 客户端(查询 + 遍历):
java 复制代码
@Test
public void testHighLightQuery() throws Exception {
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchQuery("name", "spring"));
    
    HighlightBuilder highlight = new HighlightBuilder();
    highlight.preTags("<font color='red'>")
             .postTags("</font>")
             .field("name");
    builder.highlighter(highlight);
    
    searchRequest.source(builder);
    searchResponse = restHighLevelClient.search(searchRequest);
}

@After
public void displayDoc() {
    for (SearchHit hit : searchResponse.getHits()) {
        System.out.println("原始数据: " + hit.getSourceAsString());
        
        HighlightField nameHighlight = hit.getHighlightFields().get("name");
        if (nameHighlight != null) {
            System.out.println("高亮结果: " + nameHighlight.getFragments()[0]);
        }
    }
}

💡 高亮返回的是片段(fragments) ,不是整个字段!记得取 fragments[0].toString()


十、ES 集群搭建(高可用不是梦)

步骤:

  1. 复制一份 ES 到另一台机器(或同一台不同目录)

  2. 删除 data 目录(避免冲突)

  3. 修改 elasticsearch.yml

    yaml 复制代码
    node.name: power_shop_node_2
    network.host: 0.0.0.0
    http.port: 9201
    transport.tcp.port: 9301

集群健康状态:

颜色 含义 说明
🟢 Green 完美 所有主分片和副本都正常
🟡 Yellow 警告 主分片正常,副本缺失(单节点常见)
🔴 Red 危险 主分片丢失,数据不完整!

实验验证:

  1. 设置 replicas=1 → 两节点 → Green
  2. 关掉一个节点 → Yellow(副本没了,但数据还在)
  3. replicas=0 再关节点 → Red(主分片没了!)

十一、总结:ES 使用 Checklist

✅ 单机开发:replicas=0

✅ 中文搜索:必装 IK 分词器

✅ 字段设计:问清"是否搜索""是否展示"

✅ 集群部署:至少 3 节点防脑裂

✅ 不要拿 ES 当数据库!它不保证事务!

✅ 复杂查询用 DSL ,别用 URI!

✅ 范围/精确过滤用 filter ,性能更高!

✅ 关键词高亮用 highlight,用户体验拉满!


相关推荐
babytiger18 小时前
Gitea 重安装 + Snap 数据迁移完整流程总结
linux·elasticsearch·gitea
不是株1 天前
ElasticSearch
大数据·elasticsearch·搜索引擎
逸Y 仙X1 天前
文章三十四:ElasticSearch Script脚本实战
大数据·elasticsearch·搜索引擎·全文检索
Mr YiRan1 天前
Android构建优化:基于Git Diff+TaskGraph
android·git·elasticsearch
Huazzi.1 天前
Git本地和远程历史不一致问题解决步骤
大数据·git·elasticsearch
RemainderTime1 天前
(十二)Spring Cloud Alibaba 2023.x:基于 Filebeat 构建轻量级 ELK日志追踪体系
分布式·elk·elasticsearch·微服务·架构·logback
什么都会一点儿的自动驾驶工程狮1 天前
Jetson Orin Nano Super + Ubuntu 22.04 + ROS2 Humble + Autoware Universe
linux·ubuntu·elasticsearch
heimeiyingwang1 天前
【架构实战】ElasticSearch搜索集群:全文检索的艺术
elasticsearch·架构·全文检索
逸Y 仙X1 天前
文章三十三:Elasticsearch 文本分词器深入实战
java·大数据·elasticsearch·搜索引擎·全文检索