臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

一、💡 一句话理解

核心结论

Elasticsearch,简称 ES,是一个专门为搜索和分析设计的分布式引擎。它不是所有查询都比 MySQL 快,而是在全文检索、相关度排序、多条件搜索和大规模横向扩展方面更有优势。

最容易记住的区别是:

MySQL:根据字段值找记录
ES:根据关键词找文档

二、🧭 理论:ES 是什么

2.1 核心概念

ES 会把数据保存成 JSON 文档,并将文档放入索引中。

Elasticsearch可以类比为 MySQL说明
Index表一组结构相近的文档,但两者并不完全等价
Document一行记录一条 JSON 数据
Field字段文档中的一个属性
Mapping表结构定义字段类型和索引方式
Query DSLSQLES 的 JSON 查询语言

例如,一篇文章在 ES 中可能是:

{
  "title": "Elasticsearch Search Tutorial",
  "author": "Afyke",
  "viewCount": 100
}

2.2 先带着一个商城搜索场景往下看

假设商城只有三件商品:

商品编号商品名称价格库存
101男士防水轻便羽绒服59923
102女士加厚保暖羽绒服6990
103户外防水冲锋衣49915

用户在搜索框输入:

防水 羽绒服

系统真正要做的事情是:

  1. 找到包含“防水”或者“羽绒服”的商品。
  2. 同时包含两个词的商品 101 应该排得更靠前。
  3. 商品 102 虽然包含“羽绒服”,但库存为 0,应该被过滤掉。
  4. 用户还可能继续选择品牌、价格范围和销量排序。

这就是典型的 全文匹配 + 条件过滤 + 相关度排序。后面的倒排索引、分词和 _score,都会继续使用这个例子说明。

如果用户已经知道商品编号 101,只是根据主键查询这一件商品,MySQL 通常已经非常快,不需要为了这种查询专门使用 ES。

2.3 ES 适合解决什么问题

ES 适合:

  • 根据标题、正文等大段文本搜索关键词。
  • 按匹配程度给结果排序。
  • 搜索日志并统计错误数量、响应时间等指标。
  • 在大量数据上执行过滤、聚合和近实时分析。
  • 通过节点和分片扩展搜索能力。

ES 不擅长替代关系型数据库完成:

  • 转账、扣库存等强事务操作。
  • 复杂的多表关联查询。
  • 需要强一致性的核心业务数据写入。

常见架构是:

三、⚙️ 理论:为什么 ES 搜索通常更快

3.1 MySQL 的普通索引擅长精确值和范围查询

继续使用商城例子。以默认的 InnoDB 存储引擎为例,普通索引通常使用我们常说的 B+Tree 结构。它非常适合商品编号、价格范围等明确条件:

SELECT * FROM product WHERE id = 101;
 
SELECT * FROM product
WHERE price BETWEEN 500 AND 700;

但下面的查询以通配符开头,普通 B+Tree 索引通常无法有效缩小范围:

SELECT * FROM product
WHERE name LIKE '%防水%羽绒服%';

由于搜索内容前面带有 %,普通 B+Tree 索引无法像主键查询一样直接确定查找起点,商品很多时可能需要检查大量记录。

而且真实用户还可能输入“羽绒服 防水”,顺序与商品名称不同。继续堆叠 LIKE 会越来越难处理分词、顺序、相关度和多字段权重。更多 B+Tree 原理可以参考 ../../数据库/mysql/MySQL 索引。

为什么官方文档经常写 B-tree

MySQL 官方把这一大类索引统称为 B-tree,并说明这是对一类索引设计的泛称。学习 InnoDB 的具体结构时,通常称为 B+Tree:索引记录存放在叶子页,叶子页适合按顺序访问。两种写法出现的语境不同;这篇笔记讨论 InnoDB 的具体实现,因此统一写 B+Tree。

不要绝对化

MySQL 也支持 FULLTEXT 全文索引,所以不是“MySQL 完全不能全文搜索”。两者应该结合业务规模、检索能力、运维成本和一致性要求进行选择。

3.2 ES 使用倒排索引

3.2.1 先纠正一个容易误解的名字

倒排索引不是“索引”的反义词,它本身就是索引的一种。

“索引”是一个大类,意思是为了避免扫描全部数据,提前建立一份方便查找的目录。B+Tree 索引、倒排索引都属于索引,只是目录的组织方式和适用问题不同。

索引
├── B+Tree 索引:擅长完整字段的等值、范围和前缀查询
└── 倒排索引:擅长从大段文本中寻找关键词

3.2.2 先看 MySQL 的 B+Tree 索引

商品表原始数据如下:

商品编号商品名称
101男士防水轻便羽绒服
102女士加厚保暖羽绒服
103户外防水冲锋衣

如果给 id 建立 B+Tree 主键索引,可以把它简化理解为:

101 → 商品 101 的完整记录
102 → 商品 102 的完整记录
103 → 商品 103 的完整记录

现在执行:

SELECT * FROM product WHERE id = 102;

MySQL 可以拿着完整的值 102 查目录,快速定位商品 102。

如果给商品名称建立普通 B+Tree 索引,它主要是按照完整字段值的顺序组织目录,适合完整值或从字段开头开始的前缀查询。例如:

WHERE name = '男士防水轻便羽绒服'
 
WHERE name LIKE '男士%'

但用户搜索的是商品名称中间的一个词:

WHERE name LIKE '%防水%'

因为不知道“防水”前面有什么,MySQL 无法直接从按完整名称排列的普通目录中确定起点。这就像一本通讯录按姓名第一个字排序,你却想找“名字中间含有伟的人”,只能检查很多名字。

3.2.3 再看 ES 的倒排索引

ES 会先把商品名称拆成词:

商品编号商品名称拆出的词
101男士、防水、轻便、羽绒服
102女士、加厚、保暖、羽绒服
103户外、防水、冲锋衣

原始数据的阅读方向是:

商品 101 → [男士, 防水, 轻便, 羽绒服]
商品 102 → [女士, 加厚, 保暖, 羽绒服]
商品 103 → [户外, 防水, 冲锋衣]

这是“商品指向它包含的词”。为了让用户能够根据词找商品,ES 把方向反过来,建立:

防水   → [商品 101, 商品 103]
轻便   → [商品 101]
羽绒服 → [商品 101, 商品 102]
保暖   → [商品 102]

这就是“倒排”的含义:

原始方向:文档 → 词
倒排方向:词 → 文档

现在搜索“防水 羽绒服”,ES 可以分别查目录:

“防水”对应   → [101, 103]
“羽绒服”对应 → [101, 102]
两个词都出现 → [101]

商品 101 同时包含两个词,所以通常比只包含一个词的商品 102、103 更相关。

3.2.4 把两种索引放在一起比较

对比项MySQL 普通 B+Tree 索引ES 倒排索引
目录中的键通常是完整字段值文本经过分析后得到的词
简化结构商品编号 → 一条记录关键词 → 多篇文档
擅长的问题“编号 102 是哪件商品?”“哪些商品包含防水?”
适合查询=、范围、左前缀全文搜索、相关度排序
不擅长的问题在长文本任意位置找词强事务、复杂多表关联

一句话记忆

B+Tree 像“根据完整编号找商品”的目录;倒排索引像“根据关键词找所有相关文章或商品”的目录。

倒排索引主要包含:

组成大白话解释
Term Dictionary排好序的关键词词典,例如“防水”“羽绒服”
Posting List每个关键词出现在哪些文档中,例如“防水 → 101、103”
词频和位置等信息帮助计算相关度、完成短语匹配

3.3 文本写入时会先经过分析器

文本字段写入 ES 时,会经过 Analyzer(分析器):

查询全文字段时,查询文本也会经过相应分析,再使用产生的词项查询倒排索引。

例如商品名称“男士防水轻便羽绒服”,经过合适的中文分析器后,可能得到:

原始文本:男士防水轻便羽绒服
分词结果:[男士, 防水, 轻便, 羽绒服]

用户输入“防水羽绒服”时,查询文本也会被分析成类似的词项:

查询文本:防水羽绒服
分词结果:[防水, 羽绒服]

两边产生的词能对应上,ES 才能从倒排索引中找到商品 101。分析器选得不合适时,“羽绒服”可能被错误切分,搜索效果也会变差,所以中文搜索不能只安装 ES 就结束了,还需要测试分词结果。

这意味着 ES 把很多搜索准备工作提前放到了写入阶段。因此搜索更快,但代价是:

  • 写入时需要分析文本并维护索引。
  • 索引需要额外磁盘空间和内存。
  • 更新和删除也要维护对应的索引结构。
  • 新写入的数据通常是近实时可搜索,而不是绝对实时。

3.4 ES 会计算相关度

数据库查询通常关心“是否符合条件”,搜索引擎还需要回答“哪条结果更相关”。

ES 使用 _score 表示相关度,默认相似度算法是 BM25。它会综合考虑:

  • 查询词在当前文档中出现的次数。
  • 查询词在全部文档中是否罕见。
  • 当前文档的长度。

因此,ES 不仅能找到结果,还能把更可能符合用户意图的结果排在前面。

还是搜索“防水 羽绒服”:

商品命中情况排序倾向
101:男士防水轻便羽绒服同时命中“防水”和“羽绒服”更靠前
102:女士加厚保暖羽绒服只命中“羽绒服”靠后
103:户外防水冲锋衣只命中“防水”靠后

这里的“更靠前”只是帮助理解相关度,不代表固定分数。实际 _score 还会受到分析器、词频、字段长度、字段权重和查询写法影响。

3.5 ES 可以分片并行搜索

ES 可以把一个索引拆成多个 Shard(分片),并分布到多个 Node(节点):

Replica(副本分片)既能提供数据冗余,也能增加处理读取请求的能力。

可以把它想成排查线上日志。订单、支付、库存服务产生了大量日志,现在要搜索:

payment timeout

如果日志分布在三个分片上,三个分片可以同时查找自己保存的日志,协调节点再合并结果。这类似让三个人同时检查三个档案柜,最后汇总结果,而不是让一个人从第一个柜子一直翻到最后一个柜子。

类比边界

“三个人查档案柜”只是帮助理解并行搜索。真实 ES 还需要处理网络通信、各分片评分以及结果归并,并不是分片增加三倍就一定快到三倍。

分片不是越多越好

分片过多会增加内存占用、调度成本和结果合并开销。生产环境需要根据数据量和访问压力规划分片,不能照搬示例。

3.6 ES 并不总比 MySQL 快

场景通常更合适的选择原因
根据主键查询一条订单MySQLInnoDB 的 B+Tree 主键索引已经很高效
转账、扣库存MySQL需要事务和强一致性
多表 JOINMySQL关系模型和查询优化器更适合
标题和正文全文搜索ES倒排索引、分词和相关度排序
日志关键词检索与聚合ES适合搜索和分析大量文档
搜索结果按相关度排序ES原生提供 _score 和相关度算法

所以,准确的说法不是“ES 比 MySQL 快”,而是:

在为全文检索设计好 Mapping 和查询的前提下,ES 通常比使用 MySQL 普通索引或模糊匹配搜索大段文本更合适。

四、🚀 实践:从准备到验证

4.1 前置准备

4.1.1 准备 Docker

本练习需要 Docker Compose。官方推荐 Docker Compose V2 或更高版本。

下面的快速安装仅适用于本地学习和测试,不能直接用于生产环境:

curl -fsSL https://elastic.co/start-local | sh

启动完成后可以访问:

  • Elasticsearch:http://localhost:9200
  • Kibana:http://localhost:5601

进入 Kibana 后,打开 Dev Tools,后续请求都可以在那里执行。

4.1.2 认识字段类型

本例主要使用三种类型:

类型是否分词适用场景
text是标题、正文等全文搜索
keyword否作者、状态、编号等精确匹配和聚合
integer不适用整数范围查询和排序

4.2 可以拿来干什么

4.2.1 全文搜索

前提:目标字段的 Mapping 是 text,并且已经写入文档。

GET /articles/_search
{
  "query": {
    "match": {
      "title": "Elasticsearch Search"
    }
  }
}

match 会分析查询文本,再到倒排索引中查找词项。输出包含命中文档和 _score,适合文章、商品和知识库搜索。

4.2.2 精确过滤

前提:需要精确匹配的字段应使用 keyword 等合适类型。

GET /articles/_search
{
  "query": {
    "term": {
      "author": "Afyke"
    }
  }
}

term 不会像 match 那样对查询值进行全文分析,适合状态、标签、作者编号等精确值。

4.2.3 组合全文搜索和过滤

GET /articles/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "Elasticsearch"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "viewCount": {
              "gte": 50
            }
          }
        }
      ]
    }
  }
}

当前例子会查找标题包含 Elasticsearch,并且浏览量大于等于 50 的文档。must 参与匹配和评分,filter 只判断是否满足条件。

4.3 完整实践:创建、写入和查询

操作范围

以下请求会在本地 ES 中创建 articles 索引并写入两条测试文档。只在个人测试环境执行,不要直接用于共享或生产集群。

4.3.1 创建索引和 Mapping

在 Kibana Dev Tools 中执行:

PUT /articles
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "author": {
        "type": "keyword"
      },
      "viewCount": {
        "type": "integer"
      }
    }
  }
}

预期结果:

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "articles"
}

4.3.2 写入测试文档

refresh=wait_for 会等待文档变得可搜索,便于练习时立即查询。高吞吐生产写入不应在每条请求中随意使用它。

POST /articles/_doc/1?refresh=wait_for
{
  "title": "Elasticsearch Search Tutorial",
  "author": "Afyke",
  "viewCount": 100
}
POST /articles/_doc/2?refresh=wait_for
{
  "title": "MySQL Transaction Tutorial",
  "author": "Afyke",
  "viewCount": 80
}

响应中的 result 为 created,表示文档创建成功。

4.3.3 执行全文搜索

GET /articles/_search
{
  "query": {
    "match": {
      "title": "Elasticsearch Search"
    }
  }
}

预期命中第一篇文档。响应中重点观察:

字段说明
took本次搜索耗时,单位为毫秒
hits.total命中文档数量
hits.hits命中文档列表
_score文档的相关度得分
_source写入时保存的原始 JSON

4.3.4 查看分析结果

下面的 API 可以帮助理解“写入前会先分词”:

POST /_analyze
{
  "analyzer": "standard",
  "text": "Elasticsearch Search Tutorial"
}

响应的 tokens 中可以看到分析后产生的词项。中文搜索通常还需要根据业务选择适合的中文分析方案,不能直接假设默认分析器一定满足中文分词需求。

4.4 常见问题排查

4.4.1 connection refused

可能原因:ES 尚未启动,或者 9200 端口未监听。

检查 Docker 容器是否正常运行,并重新访问 http://localhost:9200。

4.4.2 resource_already_exists_exception

表示 articles 索引已经存在。不要重复执行创建索引请求;如果只是继续练习,可以直接写入和查询。

4.4.3 刚写入的数据搜索不到

ES 是近实时搜索。练习时可以在写入请求中使用:

?refresh=wait_for

它会等待本次写入变得可搜索后再返回。

4.4.4 term 查询不到看起来相同的文本

检查字段是不是 text 类型。term 查询不会分析查询文本;全文字段通常使用 match,精确值字段通常使用 keyword 配合 term。

五、📌 快速回顾

  • ES 是搜索和分析引擎,不是 MySQL 的直接替代品。
  • ES 通过“关键词 → 文档编号”的倒排索引加速全文搜索。
  • Analyzer 负责把文本处理成可以建立索引和查询的词项。
  • _score 用于表达结果和查询的相关程度。
  • 分片让 ES 能把数据和查询压力分布到多个节点。
  • MySQL 更适合事务、关系和权威数据;ES 更适合搜索副本。

一句话总结:

MySQL 的普通索引像按字段排序的目录,ES 的倒排索引像“每个关键词出现在哪些文章中”的目录;ES 用更多写入、存储和维护成本,换取更强的全文检索能力。

六、🔗 官方资料