一、 为什么非 ES 不可?(业务痛点)
在传统的 MySQL 架构中,当我们执行 WHERE title LIKE '%Java%' 时:
-
全表扫描:数据库必须逐行检查,查询效率随数据量增加呈线性下降。
-
无法分词:搜索"Java开发",MySQL 很难精准匹配到只包含"Java"或"开发"的记录。
ES 的核心价值:将搜索延迟从"秒级"降低到"毫秒级",并提供强大的相关性评分(搜索结果谁更匹配排在前面)。
二、 核心架构:像理解数据库一样理解 ES
| Elasticsearch | 关系型数据库 (MySQL) | 说明 |
|---|---|---|
| Index (索引) | Database (数据库) | 数据的逻辑容器 |
| Type (类型) | Table (表) | ES 7.x 后已弱化,通常一个索引对应一类数据 |
| Document (文档) | Row (行) | 最小的数据单元(JSON 格式) |
| Field (字段) | Column (列) | 数据的属性 |
| Mapping (映射) | Schema (表结构) | 定义字段类型(text, keyword 等) |
核心机制:分布式分片
-
Shard (分片):将一个大索引拆分成多个小块,分布在不同服务器上,实现横向扩展。
-
Replica (副本):分片的备份,保证某台服务器宕机时数据不丢失,并提升查询并发能力。
三、 必杀技:倒排索引 (Inverted Index)
这是 ES 为什么快的底层秘密。
-
正排索引:文档 ID -> 文档内容(像看书,想找某个词得翻遍全书)。
-
倒排索引:关键词 -> 文档 ID 列表(像查书后的索引表,直接定位页码)。
倒排索引表组成:
-
Term Dictionary (词典):记录所有不重复的关键词。
-
Posting List (倒排列表):记录该关键词在哪些文档出现过、出现的频率(TF)和位置。
四、 环境搭建:Linux 手动部署要点
对于 Java 全栈开发,理解 Linux 下的内核限制配置是面试常考的运维细节。
1. 核心系统配置
Bash
# 1. 调整最大文件句柄数(ES 需要频繁读写大量小文件)
vim /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
# 2. 调整虚拟内存限制(防止内存溢出)
vim /etc/sysctl.conf
vm.max_map_count=262144
sysctl -p
2. 关键配置 (elasticsearch.yml)
YAML
cluster.name: my-application
node.name: node-1
network.host: 0.0.0.0 # 允许远程访问
http.port: 9200 # REST API 端口
http.cors.enabled: true # 开启跨域,方便前端连接
http.cors.allow-origin: "*"
五、 IK 中分分词器:解决"中文搜索"
ES 自带的分词器会将中文按字拆分(如:重、头、写...),这不符合业务逻辑。
-
ik_smart:粗粒度拆分,节省空间。
-
ik_max_word:最细粒度拆分,搜索更全。
配置词典 :在 IKAnalyzer.cfg.xml 中配置 ext_dict,可以添加公司特有的专业术语(如你的项目名"Antigravity")。
六、 DSL 高级查询实战
这是你后端代码中构建查询逻辑的模版。
1. 布尔查询 (Bool Query) - 业务中最常用
JSON
GET /order/_search
{
"query": {
"bool": {
"must": [
{ "match": { "goods_name": "华为手机" } }
],
"filter": [
{ "range": { "price": { "gte": 2000, "lte": 5000 } } },
{ "term": { "status": "published" } }
]
}
}
}
面试点 :为什么用
filter?答:
filter不计算相关性评分,且有缓存机制 ,性能远高于must。
七、 Spring Boot 3.x 深度集成
现在企业主流使用的是 ElasticsearchJavaClient(替代了已过时的 High Level Client)。
1. 核心依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2. 实战:构建一个搜索 Service
Java
@Service
public class GoodsSearchService {
@Autowired
private ElasticsearchClient client; // 2026年主流客户端
public SearchResponse<Goods> searchGoods(String keyword) throws IOException {
return client.search(s -> s
.index("goods")
.query(q -> q
.bool(b -> b
.must(m -> m.match(t -> t.field("name").query(keyword)))
.filter(f -> f.term(t -> t.field("is_delete").value(0)))
)
), Goods.class);
}
}
八、 2 年经验必看的优化与面试追问
-
数据同步方案:MySQL 和 ES 怎么同步?
-
方案 A (同步双写):代码中写完 MySQL 紧接着写 ES。优点是实时,缺点是耦合。
-
方案 B (异步 MQ):MySQL 成功后发消息给 RabbitMQ,由消费者同步 ES。解耦,保证最终一致性。
-
方案 C (Canal/CDC):监听 MySQL Binlog,全自动同步。对业务代码零侵入(大厂首选)。
-
-
深度分页问题:
-
普通的
from + size在翻到 10000 页以后会极慢(因为要汇总所有分片数据)。 -
对策 :使用
search_after(类似游标)来处理海量数据的连续拉取。
-
-
索引预热:
- 对于商城主页面,可以在系统启动时利用
ApplicationRunner预先执行一次复杂查询,让 ES 将索引数据加载到操作系统的 Page Cache 中。
- 对于商城主页面,可以在系统启动时利用