假设你做了一个博客网站,里面有十万篇文章。读者输入“Docker 安装”,希望找到标题或正文中相关的内容,还想按分类筛选、按发布时间排序。
这时,搜索功能需要做的事情就比“查找某个字段等于某个值”多了:理解关键词、找到相关文章、决定谁排在前面,再把结果分页返回。Elasticsearch 就是常用于处理这类需求的工具。当然,在企业级项目中也会常常将ES作为日志系统搭建的中间件。
一、Elasticsearch 到底是什么
Elasticsearch,通常简称 ES,是基于 Apache Lucene 的搜索与分析引擎。Lucene 提供底层检索能力,Elasticsearch 将这些能力封装成 HTTP 接口,并支持多台机器协同处理数据。常见用途包括文章搜索、商品搜索和日志查询。它也能做统计,例如“每个分类有多少篇文章”“某个时间段出现了多少次错误”。
它与 MySQL 的侧重点不同。MySQL 常用于保存订单、账户等业务数据,处理事务和关系查询;Elasticsearch 常用于全文检索、相关性排序和搜索分析。数据库本身也有全文检索能力,是否需要引入 ES,要看实际需求和维护成本。
一般常见的做法:业务数据仍存放在 MySQL,将需要搜索的字段同步到 ES。搜索请求查询 ES,交易操作继续由业务数据库处理。这样会增加数据同步工作,还要考虑同步失败和延迟,不能期待两边天然一致。
先理解几个概念
| 概念 | 含义 |
|---|---|
| 文档(Document) | 一条 JSON 数据 |
| 索引(Index) | 一组文档及其配置 |
| Mapping | 字段类型和索引方式的定义 |
| 主分片(Primary shard) | 索引数据的一部分,每个分片是一个 Lucene 索引 |
| 副本分片(Replica shard) | 主分片的副本 |
| 节点(Node) | 一个 Elasticsearch 实例 |
| 集群(Cluster) | 协同工作的多个节点,也可以只有一个节点 |
二、它为什么可以快速找到你想要的内容
1、倒排索引:从“词”找到“文章”
假设有三篇文章,标题分别是:
| 文档 ID | 标题 |
|---|---|
| 1 | Docker 安装教程 |
| 2 | Elasticsearch 安装教程 |
| 3 | Docker 网络排查 |
按“文章 → 标题”保存内容,很容易回答“第 1 篇文章叫什么”。但读者搜索“Docker”时,需要反过来回答:“哪些文章包含这个词?”。为此,可以提前整理一份查找表:
| 词 | 包含它的文档 ID |
|---|---|
| docker | 1、3 |
| elasticsearch | 2 |
| 安装 | 1、2 |
| 教程 | 1、2 |
| 网络 | 3 |
| 排查 | 3 |
这就是倒排索引的基本思路:记录一个词出现在哪些文档中。搜索 Docker 时,就能先找到文档 1 和 3,不必每次逐篇扫描全文。搜索“Docker 安装”时,如果要求两个词都出现,就取两个列表的交集,得到文档 1;如果允许任意一个词出现,就会有更多候选结果,再根据相关性排序。
2、分词:先把句子变成可搜索的词
建立倒排索引前,ES 要先处理文本,这个过程叫分析,通常包含分词、大小写转换等步骤。负责这些工作的配置叫分析器(Analyzer)。例如,标准分析器会把Docker Install Guide处理成docker、install、guide这样的词。后面安装好环境,可以亲自查看:
POST /_analyze
{
"analyzer": "standard",
"text": "Docker Install Guide"
}//响应结果
{
"tokens": [{
"token": "docker",
"start_offset": 0,
"end_offset": 6,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "install",
"start_offset": 7,
"end_offset": 14,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "guide",
"start_offset": 15,
"end_offset": 20,
"type": "<ALPHANUM>",
"position": 2
}
]
}
3、分片与副本:数据多了,怎么分给多台机器
一个 Elasticsearch 实例叫一个节点(Node),多个节点可以组成集群(Cluster)。当索引很大时,可以把它分成几份,每一份叫分片(Shard)。例如把文章索引划成 3 个主分片,各自存放一部分文档;ES 通过路由决定一条文档属于哪个主分片。副本是主分片的复制件。3 个主分片,每个配置 1 个副本,就一共有 6 个分片副本实例,占用的存储和资源也会相应增加。
主分片与它自己的副本不能放在同一个节点上。这样一个节点故障时,集群可以利用其他节点上的有效副本恢复服务;
4、一次写入和搜索,大致经历什么
写入时,接收请求的节点找到目标主分片,由主分片处理写入,再复制到同步副本。正常情况下,相关同步副本处理完成后,才完成写入确认。
搜索时,接收请求的节点负责协调,将查询交给相关分片的某个可用副本执行,再合并结果、获取需要的文档,返回给客户端。搜索不需要把同一份数据的主分片和全部副本都查一遍。
因此,副本既能帮助应对故障,也可以分担搜索流量,但会增加存储和写入复制成本。(其实在一定程度上有点类似于MYSQL主从结构,主库写入完成同步到从库,后续读取也可以从从库进行数据读取)
三、用 Docker 启动单节点环境
1、准备运行环境
先安装适合操作系统的 Docker Engine 或 Docker Desktop,确认命令可用:
docker version2、 创建网络和持久化卷
以下命令在 Bash 或 Zsh 中执行。先创建一个工作目录,再准备专用网络与数据卷:
mkdir -p elasticsearch-lab cd elasticsearch-lab export STACK_VERSION=8.18.6 docker network create es-lab-net docker volume create es-lab-data docker volume create es-lab-config docker pull docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}两个卷分别保存索引数据,以及节点配置与证书。这里的配置卷属于当前节点,不要让多个节点共享同一个配置卷。
3、启动 Elasticsearch
docker run -dit \ --name es-lab \ --hostname es-lab \ --network es-lab-net \ --restart unless-stopped \ --memory 2g \ --ulimit nofile=65535:65535 \ --log-driver json-file \ --log-opt max-size=20m \ --log-opt max-file=5 \ -p 127.0.0.1:9200:9200 \ -e discovery.type=single-node \ -v es-lab-data:/usr/share/elasticsearch/data \ -v es-lab-config:/usr/share/elasticsearch/config \ docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}这段配置中,有几个地方值得理解:
discovery.type=single-node用于单节点实验环境;组建多节点集群时需要重新规划发现与安全配置。127.0.0.1:9200:9200只把 REST API 发布到宿主机回环地址。节点间通信使用的9300端口不需要在本示例中发布。- 数据卷让数据独立于容器生命周期。配置卷保留首次启动生成的证书、密钥库和配置;空命名卷首次挂载时会由 Docker 填充镜像中对应目录的内容。
4、验证是否启动成功
打开浏览器,访问9200地址:http://127.0.0.1:9200/,如果可以正常访问,则表示启动成功。
四、常用命令:模拟一个小例子
为了方便ES的可视化管理,建议开发测试可以使用轻量级的Elasticvue工具,如果是正式环境可以使用功能强大的Kibana工具:https://www.elastic.co/cn/kibana/https://www.elastic.co/cn/kibana/
1、创建索引
控制台请求由“方法、路径、请求体”组成。例如PUT /articles-v1表示向该路径发送 PUT 请求,后面的 JSON 描述索引配置。命令中的/是接口路径,不是本地文件目录。
PUT /articles-v1 //请求参数 { "settings": { "number_of_shards": 1, "number_of_replicas": 0 }, "mappings": { "properties": { "title": { "type": "text" }, "category": { "type": "keyword" }, "published_at": { "type": "date" }, "views": { "type": "long" } } } } //响应参数 { "acknowledged": true, "shards_acknowledged": true, "index": "articles-v1" }这个索引用一个主分片、零副本,适合单节点学习。生产环境应按可用性要求配置副本,并提供能够承载副本的其他节点。提前定义 Mapping,可以避免日期、数字被错误识别成字符串。查看配置:
GET /articles-v1/_mapping
GET /articles-v1/_settings
2、写入文档:给文章一个 ID
PUT /articles-v1/_doc/1?refresh=wait_for //请求参数 { "title": "Docker 安装教程", "category": "运维", "published_at": "2026-10-01T10:00:00+08:00", "views": 120 } //响应参数 { "_index": "articles-v1", "_id": "1", "_version": 1, "result": "created", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "_seq_no": 0, "_primary_term": 1 }- _id:文档ID,向同一索引、同一 ID 再次执行这个 PUT,会替换该文档,而不是新增第二条。
- 如果只允许新建、不允许覆盖,使用
PUT /articles-v1/_create/1;ID 已存在时会返回冲突。让 ES 自动生成 ID,则使用POST /articles-v1/_doc。 refresh=wait_for用于方便后续搜索验证
3、根据 ID 读取文章
GET /articles-v1/_doc/1 //响应参数 { "_index": "articles-v1", "_id": "1", "_version": 3, "_seq_no": 2, "_primary_term": 1, "found": true, "_source": { "title": "Docker 安装教程", "category": "运维", "published_at": "2026-10-01T10:00:00+08:00", "views": 120 } }响应中的_source包含原始业务字段,found表示是否找到文档。
4、批量写入:一次添加多篇文章
POST /articles-v1/_bulk?refresh=wait_for {"index":{"_id":"2"}} {"title":"Elasticsearch 安装教程","category":"搜索","published_at":"2026-10-02T10:00:00+08:00","views":80} {"index":{"_id":"3"}} {"title":"Docker 网络排查","category":"运维","published_at":"2026-10-03T10:00:00+08:00","views":200}Bulk 使用 NDJSON,即一行一个 JSON。上例每两行是一组:第一行说明操作和 ID,第二行提供文档。通过 curl 或程序发送时,请求体最后也要以换行符结束,内容类型使用application/x-ndjson。
5、全文搜索:找标题包含 Docker 的文章
POST /articles-v1/_search //请求参数 { "query": { "match": { "title": "Docker" } } } //响应参数 { "took": 29, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 2, "relation": "eq" }, "max_score": 0.4700036, "hits": [ { "_index": "articles-v1", "_id": "1", "_score": 0.4700036, "_source": { "title": "Docker 安装教程", "category": "运维", "published_at": "2026-10-01T10:00:00+08:00", "views": 120 } }, { "_index": "articles-v1", "_id": "3", "_score": 0.4700036, "_source": { "title": "Docker 网络排查", "category": "运维", "published_at": "2026-10-03T10:00:00+08:00", "views": 200 } } ] } }match会分析搜索文本,再查找匹配的文档。在当前样例中,应匹配文章 1 和 3。返回结果主要看三处:
| 字段 | 作用 |
|---|---|
hits.total | 匹配总数及其计数关系;大结果集可能只是下界 |
hits.hits | 当前返回的文档列表 |
_score | 相关性分数;默认搜索通常按分数从高到低返回 |
6、精确筛选:只看运维类文章
POST /articles-v1/_search { "query": { "bool": { "filter": [ { "term": { "category": "运维" } } ] } } }term匹配字段索引中的精确词项,适合这里的keyword分类字段。filter用于判断“是否满足条件”,不计算相关性得分。
7、排序与分页:按浏览量取前两篇
POST /articles-v1/_search { "query": { "match_all": {} }, "sort": [ { "views": "desc" } ], "from": 0, "size": 2, "_source": ["title", "views"] }from 是跳过多少条,size 是本次取多少条,_source 限定返回哪些业务字段。第二页可以把 from 改为 2。
8、修改文档:只更新浏览量
POST /articles-v1/_update/1?refresh=wait_for { "doc": { "views": 180 } }这会把浏览量设置为 180,其他字段保留。它与前面的PUT /_doc/1全量替换不同。
9、 删除数据:分清文档和索引
删除一条ID为3的文档:
DELETE /articles-v1/_doc/3?refresh=wait_for
删除索引(索引下的所有文档都会被删除):
DELETE /articles-v1
五、常用命令速查
| 目的 | 命令 |
|---|---|
| 查看版本与基本信息 | GET / |
| 检查分词结果 | POST /_analyze,附分析器和文本 |
| 创建索引 | PUT /articles-v1,附 settings 与 mappings |
| 查看字段类型 | GET /articles-v1/_mapping |
| 写入或替换文档 | PUT /articles-v1/_doc/1,附完整文档 |
| 只允许新建 | PUT /articles-v1/_create/1,附文档 |
| 按 ID 查询 | GET /articles-v1/_doc/1 |
| 搜索 | POST /articles-v1/_search,附查询条件 |
| 统计文档数量 | GET /articles-v1/_count |
| 局部更新 | POST /articles-v1/_update/1,附 doc 或 script |
| 批量操作 | POST /articles-v1/_bulk,附 NDJSON |
| 删除一条文档 | DELETE /articles-v1/_doc/1 |
| 删除整个索引 | DELETE /articles-v1 |
| 查看集群健康 | GET /_cluster/health |
六、小结
以上是本次对于Elasticsearch原理、Docker 安装与常用命令的一个介绍和使用,欢迎各位同学交流学习!