这是《Elasticsearch 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 本章通过一组可执行请求熟悉 Elasticsearch 的基础 API:集群检查、创建索引、写入文档、查询文档、更新文档、删除文档、批量操作和搜索分析。
4.1 请求格式
Elasticsearch 提供 REST API,常见形式:
GET /_cluster/health
PUT /products
POST /products/_search
使用 curl:
curl -X GET "http://localhost:9200/_cluster/health?pretty"
curl -X PUT "http://localhost:9200/products" \
-H 'Content-Type: application/json' \
-d '{"settings":{"number_of_shards":1}}'
使用 Kibana Dev Tools 时可以省略主机地址:
GET /_cluster/health
4.2 检查集群
GET /
GET /_cluster/health?pretty
GET /_cat/nodes?v
GET /_cat/indices?v
GET /_cat/shards?v
解释:
| API | 用途 |
|---|---|
GET / |
查看版本与集群名 |
_cluster/health |
查看健康状态 |
_cat/nodes |
查看节点 |
_cat/indices |
查看索引 |
_cat/shards |
查看分片分布 |
4.3 创建索引
PUT /books
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard"
},
"author": { "type": "keyword" },
"price": { "type": "double" },
"tags": { "type": "keyword" },
"published_at": { "type": "date" }
}
}
}
查看设置:
GET /books/_settings
查看 Mapping:
GET /books/_mapping
删除索引:
DELETE /books
4.4 写入第一条文档
自动生成 ID:
POST /books/_doc
{
"title": "Elasticsearch 实战教程",
"author": "张三",
"price": 99.0,
"tags": ["搜索", "Elasticsearch"],
"published_at": "2026-08-25T10:00:00Z"
}
指定 ID:
PUT /books/_doc/1
{
"title": "分布式搜索原理",
"author": "李四",
"price": 129.0,
"tags": ["分布式", "搜索"],
"published_at": "2026-08-20T10:00:00Z"
}
返回示例:
{
"_index": "books",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
PUT /_doc/{id} 是 Index 操作:不存在则创建,存在则整体替换并版本加一。
4.5 读取文档
按 ID 读取:
GET /books/_doc/1
只读 _source:
GET /books/_source/1
选择部分字段:
GET /books/_doc/1?_source=title,price
判断存在:
HEAD /books/_doc/1
不存在文档时:
{
"_index": "books",
"_id": "999",
"found": false
}
4.6 更新文档
局部更新:
POST /books/_update/1
{
"doc": {
"price": 119.0
}
}
脚本更新:
POST /books/_update/1
{
"script": {
"source": "ctx._source.price += params.increase",
"lang": "painless",
"params": {
"increase": 10
}
}
}
upsert:
POST /books/_update/2
{
"doc": {
"price": 89.0
},
"doc_as_upsert": true
}
ES 的更新并不是原地修改字段。流程近似为:
读取旧文档 -> 合并修改 -> 删除旧版本 -> 写入新版本
频繁更新会带来额外索引成本。
4.7 删除文档
DELETE /books/_doc/1
按查询删除:
POST /books/_delete_by_query
{
"query": {
"term": {
"author": "李四"
}
}
}
_delete_by_query 会占用较多资源,生产环境应控制批次和并发,避免影响在线查询。
4.8 批量写入
_bulk API 可以一次提交多个操作。
POST /_bulk
{"index": {"_index": "books", "_id": "3"}}
{"title": "日志检索与可观测性", "author": "王五", "price": 89.0, "tags": ["日志", "运维"], "published_at": "2026-08-19T10:00:00Z"}
{"index": {"_index": "books", "_id": "4"}}
{"title": "聚合分析入门", "author": "赵六", "price": 79.0, "tags": ["分析"], "published_at": "2026-08-18T10:00:00Z"}
{"update": {"_index": "books", "_id": "3"}}
{"doc": {"price": 85.0}}
{"delete": {"_index": "books", "_id": "4"}}
注意:
- 每行必须是完整 JSON;
- 请求体不能格式化为多行 JSON;
- 批量请求不宜过大,常见 5-15MB;
- 返回中需要逐条检查失败项;
- bulk 是批量提交,不保证所有操作在同一事务中成功。
4.9 第一条搜索请求
GET /books/_search
{
"query": {
"match": {
"title": "搜索"
}
}
}
返回结构:
{
"took": 12,
"timed_out": false,
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.87,
"hits": [
{
"_index": "books",
"_id": "3",
"_score": 0.87,
"_source": {
"title": "日志检索与可观测性"
}
}
]
}
}
关键字段:
| 字段 | 含义 |
|---|---|
took |
查询耗时,单位毫秒 |
timed_out |
是否超时收集完结果 |
hits.total |
命中数量 |
max_score |
最高得分 |
hits.hits |
当前页文档 |
_score |
相关性得分 |
_source |
原始文档 |
4.10 组合过滤与排序
GET /books/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "搜索" } }
],
"filter": [
{ "range": { "price": { "lte": 100 } } }
]
}
},
"sort": [
{ "published_at": { "order": "desc" } }
],
"size": 10
}
must 参与相关性计算,filter 不参与评分且更容易被缓存。
4.11 简单聚合
GET /books/_search
{
"size": 0,
"aggs": {
"by_author": {
"terms": {
"field": "author",
"size": 10
}
},
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
说明:
size: 0表示不要返回文档,只要聚合结果;- terms 聚合用于分组;
- avg 聚合用于计算平均值;
- terms 默认按桶内文档数倒序;
- 聚合字段需要 Doc Values,通常是 keyword 或数值类型。
4.12 分页
GET /books/_search
{
"from": 0,
"size": 20,
"query": {
"match_all": {}
}
}
GET /books/_search
{
"size": 20,
"query": { "match_all": {} },
"sort": [
{ "published_at": "desc" },
{ "_id": "asc" }
],
"search_after": ["2026-08-25T10:00:00Z", "1"]
}
浅分页适合产品搜索前几页;深分页应使用 search_after,不要使用巨大的 from。
4.13 请求超时与条件刷新
手动刷新:
POST /books/_refresh
带超时查询:
GET /books/_search?timeout=500ms
{
"query": { "match_all": {} }
}
注意:查询超时并不等于取消所有底层执行,也不保证返回完整结果。生产上应结合慢日志、查询分析和资源隔离治理。
4.14 常见错误
| 错误 | 原因 | 处理 |
|---|---|---|
index_not_found_exception |
索引不存在 | 检查索引名 |
mapper_parsing_exception |
字段类型不匹配 | 检查 Mapping |
illegal_argument_exception |
text 直接聚合 | 使用 keyword 子字段 |
search_phase_execution_exception |
查询语法或字段错误 | 查看返回 failure |
es_rejected_execution_exception |
写入或查询队列满 | 限流、扩容、降低并发 |
cluster_block_exception |
磁盘水位或只读 | 检查磁盘和 block |
4.15 本章小结
- ES 通过 REST API 管理集群、索引、文档和搜索;
PUT _doc/{id}是整体替换,POST _update是局部更新;- 更新和删除都有额外成本,频繁更新场景要谨慎设计;
_bulk是批量写入的核心 API,但必须逐条检查失败;match用于全文搜索,bool.filter用于不影响评分的条件过滤;from/size只适合浅分页,深分页使用search_after。
4.16 思考题
PUT _doc/1连续执行三次,_version会如何变化?- 为什么
_bulk不能把 JSON 格式化成多行? must和filter的核心区别是什么?- 为什么按 ID
GET通常比搜索更快、更确定? - 如果 bulk 返回 HTTP 200,是否代表所有操作都成功?为什么?