3. Elasticsearch 基础:索引、文档、分片、副本与 REST API 入门

好,咱们直接切入正题。Elasticsearch 说白了就是一个分布式的搜索引擎和存储引擎。刚开始接触的人容易被一堆术语吓住——索引、文档、分片、副本……其实拆开看,没你想的那么复杂。我当年第一次搭建 ELK 巡检环境时,就是因为没搞明白分片和副本的关系,导致集群压力全压在一个节点上,巡检日志写入直接超时。嗯,今天就把这些概念一次讲透。

3.1 核心概念:从文档到索引

文档(Document) 是 ES 里最小的数据单元。一条 Nginx 日志、一个系统指标、一段错误堆栈,存进去就是一个文档。它的格式是 JSON。举个例子:

{
  "@timestamp": "2025-04-07T10:15:30Z",
  "source": "web01",
  "message": "GET /api/health 200 12ms",
  "level": "info"
}

每一个文档都有一个唯一标识 _id,你可以自己指定,也可以让 ES 自动生成。我个人习惯让 ES 自动生成,除非你需要用业务 ID 做去重。

索引(Index) 就是文档的集合。你可以把它类比成 MySQL 里的一个数据库表,但 ES 的索引更灵活,每个文档的字段可以不一样。你想想看,日志巡检的数据源五花八门,Nginx 有 status,系统有 cpu_usage,如果硬要定死字段,那得多难受。

下面这张图梳理了 ES 的核心分层结构:

Elasticsearch 核心概念分层 集群(Cluster) 节点(Node)—— 每台服务器就是一个节点 索引(Index)—— 逻辑上的文档集合 主分片(Primary Shard) 副本分片(Replica Shard) 每个索引 = 多个主分片 + 每个主分片可拥有多个副本分片

3.2 分片与副本:分布式的基础

分片(Shard) 是 ES 把索引拆成小块的方式。比如你有一个索引叫 nginx-logs,数据量有 1TB。ES 不会把这 1TB 全塞在一个节点上,它会切成 N 个分片,分散到集群里。每个分片本质上就是一个 Lucene 索引,独立负责搜索和存储。

默认情况下,一个索引有 5 个主分片。我个人建议,生产环境至少按节点数的 3 倍来设主分片数。比如 3 个节点,主分片设 9 个,这样扩容到 5 个节点时不用 reindex。

这里有个坑我曾经踩过:主分片数一旦创建就不能改了。如果你把主分片设成 1,后面数据涨起来想扩容,只能新建索引再重新导入。所以日志巡检场景下,我习惯给按天的索引设 3~6 个主分片,按周的就设 12~18 个。别省这点规划时间,后面运维省大事。

副本(Replica) 是主分片的冗余拷贝。每个主分片可以有 0 到多个副本。副本有两个作用:一是高可用——主节点挂了,副本顶上;二是提升查询性能——搜索请求可以同时发到主分片和副本上。

下面这个表格把分片和副本的区别讲得更清楚:

特性 主分片 副本分片
写入能力 只接受写入请求 不接受写入(副本由主同步)
查询能力 参与搜索 参与搜索,分担负载
数量修改 创建索引后不可改 随时可以增删
默认值 5 1(即每个主分片一个副本)
占用空间 一份数据 等于主分片数据大小 × 副本数

3.3 REST API 操作入门

ES 几乎所有操作都是通过 RESTful API 完成。你不需要安装任何客户端,一个 curl 就能搞定。我平时调试时就直接在 Kibana 的 Dev Tools 里写,比 curl 方便,还带语法提示。

3.3.1 查看集群健康状态

GET /_cluster/health

返回 JSON 里关注 status 字段:green(健康)、yellow(副本未分配)、red(主分片丢失)。巡检时看到 yellow 就别慌,可能是节点太少,副本没法分配;看到 red 就要马上处理了。

3.3.2 操作索引

创建一个索引:

PUT /daily-logs-20250407
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}

查看所有索引:

GET /_cat/indices?v

删除索引(我劝你别在线上随便敲这个命令):

DELETE /daily-logs-20250407

个人小技巧:用 _cat/indices 比用 _cluster/state 更直观,返回的是表格格式,一眼就能看出每个索引的大小、文档数、分片状态。你可以在巡检脚本里定期跑一下:
curl -s localhost:9200/_cat/indices?v >> indices_check.txt

3.3.3 写入与查询文档

新增文档(自动生成 ID):

POST /daily-logs-20250407/_doc
{
  "@timestamp": "2025-04-07T10:15:30Z",
  "source": "web01",
  "message": "GET /api/health 200 12ms"
}

指定 ID 写入(用于更新或覆盖):

PUT /daily-logs-20250407/_doc/abc123
{
  "message": "timeout detected"
}

根据 ID 查询:

GET /daily-logs-20250407/_doc/abc123

搜索所有文档(默认返回 10 条):

GET /daily-logs-20250407/_search
{
  "query": {
    "match_all": {}
  }
}

嗯,到这里你应该明白,索引是顶层的逻辑容器,分片是物理存储单元,副本是容错和查询加速器。我们做日志巡检时,每天每小时有海量写入,副本数不能设太高——写多读少的场景,副本多了反而拖慢写入。我一般设 0 或 1,等巡检查询时再临时增加副本数量,查完再降回去。

核心要点回顾:

  • 文档 = 一条日志,JSON 格式
  • 索引 = 文档的集合,类似数据库表
  • 主分片 = 数据切分单元,创建后不可改
  • 副本 = 主分片的冗余,可动态调整
  • REST API 是 ES 的操作入口,用 GET/PUT/POST/DELETE 搞定

你想想看,如果上面这些概念没理解透,后面做索引模板、生命周期管理、滚动索引时一定会出问题。我见过不少项目,分片数设得太小,导致单个分片超过 50GB,查询慢得像蜗牛。或者副本设了 3 个,三个节点全是副本,写入性能直接腰斩。所以,花点时间把地基打牢,值。