2. Elasticsearch核心概念与日志场景映射

在日志集中运维平台中,Elasticsearch 并不是一个“装好就能用”的黑盒。日志写入是否顺畅、查询是否够快、磁盘是否扛得住、节点故障时数据是否还在,几乎都由五个核心概念决定:索引、文档、分片、副本和倒排索引。理解它们,关键不是背定义,而是把它们映射到日志场景中的真实对象和参数上。

一、索引:日志的“时间容器”,而不是数据库表

在关系型数据库中,我们习惯先建表再插数据。Elasticsearch 的索引(Index)虽然常被类比为表,但在日志场景中,它更像一个按时间切分的容器。典型做法是按天创建索引,例如 logs-app-2025.06.01、logs-app-2025.06.02。这样做的好处是:删除过期日志只需删除整个索引,比逐条删除文档快得多;查询最近一小时日志时,只需命中当天索引,避免扫描历史数据。

索引名设计要包含业务标识和时间维度。例如 logs-order-service-2025.06.01 比 logs-2025.06.01 更利于后续按服务做权限隔离和生命周期管理。如果所有日志都写进一个索引,随着数据量增长,分片无法再拆分,查询和写入都会逐渐恶化。

二、文档:一条日志就是一篇文档

文档(Document)是 Elasticsearch 中最小数据单元,用 JSON 表示。一条 Nginx 访问日志写入后大致如下:

{
  "@timestamp": "2025-06-01T10:23:45.123Z",
  "service": "order-service",
  "host": "10.0.3.21",
  "level": "ERROR",
  "message": "payment timeout after 3000ms",
  "trace_id": "a1b2c3d4e5",
  "duration_ms": 3000
}

每个字段都有类型。日志场景中,@timestamp 必须是 date 类型,level 和 service 适合 keyword,message 适合 text 并配合分词器。trace_id 虽然看起来像文本,但实际用于精确匹配,应设为 keyword。如果误把 trace_id 设为 text,标准分词器会把它拆成多个片段,导致按完整 trace_id 查询时命中失败。这是日志平台开发中最常见的映射错误之一。

三、分片:决定写入并行度和查询扇出

索引只是逻辑概念,真正存储数据的是分片(Shard)。一个索引由若干主分片组成,每个主分片是一个独立的 Lucene 实例。写入时,Elasticsearch 根据文档 _id 或自动生成的 ID 计算路由:shard = hash(routing) % number_of_primary_shards。这意味着主分片数量在索引创建后不能随意修改,否则历史数据路由会全部错乱。

日志场景中,分片数量要结合每日数据量和节点数估算。经验上,单个分片大小控制在 10GB~50GB 较为合适。假设每天产生 200GB 日志,副本数为 1,则主分片加副本共需约 400GB 存储。若设置 6 个主分片,每个主分片约 33GB,落在 3 个数据节点上,每节点承担 2 个主分片和 2 个副本分片,负载相对均衡。若只设 1 个主分片,写入只能落在一个节点,无法利用集群并行能力。

四、副本:故障时的数据保障与查询扩容

副本(Replica)是主分片的拷贝。副本分片不会与主分片分配在同一节点,因此当某个节点宕机时,副本可以提升为新的主分片,保证服务不中断。副本还能分担查询压力,因为搜索请求可以同时打到主分片和副本分片。

日志平台通常设置 number_of_replicas: 1。如果集群只有单节点,副本无法分配,集群会处于 yellow 状态,此时虽然能写入和查询,但没有冗余能力。对于核心业务日志,建议至少 3 个数据节点并保持 1 个副本;对于可重新采集的调试日志,可以临时设为 0 副本以节省磁盘。

日志索引 → 分片 → 副本 → 倒排索引 映射关系 索引 Index:logs-order-service-2025.06.01 按天切分 · 生命周期管理 · 删除过期数据只需删索引 主分片 P0 hash(routing) % 6 = 0 约 33GB / 天 节点 node-1 主分片 P1 hash(routing) % 6 = 1 约 33GB / 天 节点 node-2 主分片 P2 hash(routing) % 6 = 2 约 33GB / 天 节点 node-3 副本 R0 P0 的拷贝 节点 node-2 副本 R1 P1 的拷贝 节点 node-3 副本 R2 P2 的拷贝 节点 node-1 倒排索引 Inverted Index(每个分片内部独立构建) term → posting list:ERROR → [doc1, doc5, doc9] | timeout → [doc1, doc7] | order-service → [doc1, doc2, doc3] 查询时先查倒排索引定位文档 ID,再取回 _source,避免全表扫描 示例参数:6 主分片 · 1 副本 · 3 数据节点 · 单分片约 33GB

五、倒排索引:让“从日志里找关键词”变成毫秒级

倒排索引是 Elasticsearch 查询快的根本原因。正排索引是“文档 → 词项”,倒排索引反过来,是“词项 → 文档列表”。写入一条日志时,Elasticsearch 对 text 字段做分词,把每个词项记录到倒排表中。例如 message 为 “payment timeout after 3000ms”,标准分词后得到 payment、timeout、after、3000ms 等词项,每个词项都指向包含它的文档 ID。

查询 message: "timeout" 时,Elasticsearch 不需要扫描所有日志,而是直接在倒排索引中定位 timeout 对应的文档列表,再取回 _source。这就是为什么日志平台能在 TB 级数据中快速检索错误信息。但要注意:keyword 字段不分词,按完整值建索引,适合精确匹配和聚合;text 字段分词,适合全文检索。如果对 level 字段做聚合统计,必须用 keyword,否则 “ERROR” 可能被拆成 “error” 或其它形式,聚合结果会失真。

六、选型影响:从参数到结果的判断依据

分片数、副本数和字段映射共同决定日志平台的可用性与成本。以下是一组可参考的选型对照:

场景 主分片数 副本数 关键字段映射 预期结果
单节点开发调试 1 0 message 为 text,level 为 keyword 写入可用,无冗余,集群 yellow
3 节点生产日志 6 1 trace_id 为 keyword,@timestamp 为 date 写入并行,查询可分担,单节点故障不丢数据
高吞吐访问日志 12 1 client_ip 为 ip,status 为 integer 写入分散到更多分片,避免单分片过热
可重建调试日志 3 0 仅保留必要字段,关闭 _source 可选 节省磁盘,但节点故障后需重新采集

判断选型是否合理,可以观察三个信号:一是写入时是否出现 429 Too Many Requests,说明分片或节点写入压力过大;二是查询延迟是否随数据增长明显上升,可能是分片过大或查询未命中索引;三是集群健康状态是否为 green,yellow 表示副本未分配,red 表示主分片缺失。日志平台开发中,建议把索引模板、生命周期策略和字段映射统一纳入代码管理,避免人工创建索引时参数不一致。

日志场景中,@timestamp、service、level、trace_id 是最常用的查询和聚合字段。建议在索引模板中显式声明它们的类型,不要依赖动态映射。动态映射可能把 trace_id 识别为 text,导致精确查询失效。

把索引看作按天切分的日志容器,把文档看作一条结构化日志,把分片看作并行写入和查询的物理单元,把副本看作故障冗余和查询扩容手段,把倒排索引看作关键词检索的加速结构——这五个概念就真正落到了日志平台的日常开发和运维判断中。后续设计索引模板、生命周期策略和查询 DSL 时,都应回到这些参数和映射关系上做取舍。