第三章 Elasticsearch 数据索引:索引管理,映射,文档
说实话,很多新手上来就写告警规则,结果发现查询慢、数据不对、字段类型报错。为什么?因为没搞懂 ES 里的数据是怎么存、怎么管的。这一章我就带你把这些基础打牢。
3.1 索引管理——你的数据仓库
索引在 ES 里相当于关系数据库里的数据库 + 表。给它起个好名字,后面查起来才顺手。
3.1.1 创建索引
最简单的创建方式:
PUT /alerts-nginx-error
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
我个人习惯把告警相关的索引按 类型-日期 命名,比如 alerts-nginx-2025-04-18。这样后续做 ILM(索引生命周期管理)也方便。
3.1.2 删除与关闭索引
删除索引:DELETE /alerts-nginx-error。注意,生产环境千万别手滑。我记得有一次同事不小心删错了,还好有快照才能恢复。
关闭索引可以保留数据但禁止读写,适合临时维护:
POST /alerts-nginx-error/_close
3.1.3 索引别名
别名是个好东西。你可以把多个索引指向同一个别名,查询时用别名就行。比如每天建一个索引,但告警查询永远指向 alerts-current:
POST /_aliases
{
"actions": [
{ "add": { "index": "alerts-nginx-2025-04-18", "alias": "alerts-current" } },
{ "remove": { "index": "alerts-nginx-2025-04-17", "alias": "alerts-current" } }
]
}
3.2 映射(Mapping)——字段类型决定一切
映射定义了字段的类型、分词规则、是否被索引。它直接影响查询性能和数据准确性。
3.2.1 动态映射 vs 显式映射
| 对比项 | 动态映射 | 显式映射 |
|---|---|---|
| 定义 | ES 根据第一份文档自动推断类型 | 创建索引时手动指定字段类型 |
| 优点 | 快,零配置 | 精度高,类型可控 |
| 缺点 | 容易把时间戳推断为文本,导致排序、聚合失败 | 需要额外代码维护 |
| 推荐场景 | 临时测试、日志简单场景 | 生产环境、告警相关索引 |
我在项目里踩过一个坑:动态映射把 error_count 识别成了字符串,结果告警规则里做 range 查询直接报错。从那以后,所有生产索引我都用显式映射。
3.2.2 定义显式映射
PUT /alerts-nginx-error
{
"mappings": {
"properties": {
"timestamp": { "type": "date" },
"host": { "type": "keyword" },
"status": { "type": "integer" },
"message": { "type": "text", "analyzer": "standard" },
"error_code":{ "type": "keyword" }
}
}
}
注意:keyword 适合精确匹配(告警里的 status == 500),text 适合全文搜索(告警里的 message LIKE "%timeout%")。
GET /test-index/_mapping 查看 ES 推断的结果,再修改正式映射。3.3 文档——你要操作的核心
文档就是 ES 里的一条数据,类似关系数据库的一行。每个文档有一个唯一 _id。
3.3.1 增删改查
- 新增(索引)文档:
PUT /alerts-nginx-error/_doc/1 {"host":"web01","status":500} - 查询文档:
GET /alerts-nginx-error/_doc/1 - 更新文档:
POST /alerts-nginx-error/_update/1 {"doc":{"status":502}} - 删除文档:
DELETE /alerts-nginx-error/_doc/1
注意,ES 的更新本质上是先删除旧文档再索引新文档,并非原位修改。你想想看,如果频繁更新同一文档,性能开销会很大。
3.3.2 批量写入(Bulk API)
告警数据通常秒级产生,用单条写入肯定扛不住。Bulk API 可以把多条写入合并成一个请求:
POST /_bulk
{"index":{"_index":"alerts-nginx-error","_id":"1"}}
{"host":"web01","status":500,"timestamp":"2025-04-18T10:00:00Z"}
{"index":{"_index":"alerts-nginx-error","_id":"2"}}
{"host":"web02","status":503,"timestamp":"2025-04-18T10:00:01Z"}
我曾经用 Bulk API 把写入吞吐从每秒 500 条提升到了 8000 条,关键就是设置了合适的 batch_size(通常 1000~5000 条一批)。
3.4 用于告警的数据源设计
好的数据源设计能让告警规则又准又快。反例:把所有字段塞进一个超大索引,查询时到处扫描。
3.4.1 典型索引模式
| 模式名称 | 说明 | 适用场景 |
|---|---|---|
| 单索引 + 类型字段 | 所有告警日志放在同一个索引,用 service 字段区分 | 小规模,< 10 个服务 |
| 按服务分索引 | alerts-nginx、alerts-webapp 等 | 中等规模,查询隔离性好 |
| 按时间分索引 | alerts-2025-04-18 每天一个 | 数据量大,配合 ILM 自动删除旧数据 |
| 推荐组合 | alerts-{service}-{date} 例如 alerts-nginx-2025-04-18 | 生产环境通用 |
3.4.2 字段设计建议
- 时间字段:必须用
date类型,建议用 ISO8601 格式。告警规则里的@timestamp字段就是靠它来划定时间窗口。 - 状态码/级别:用
keyword类型。因为告警查询经常是status: 500这种精确匹配。 - 错误消息:用
text类型并配合match查询。注意:如果把消息也设为keyword,那就没法做模糊搜索了。 - 数值指标:比如
response_time用float或integer,方便做聚合统计。
"index": true。不用于查询的字段(比如原始日志的冗长堆栈)可以设为 "index": false,减少存储开销。3.5 本章知识体系
下面这张图帮你理清索引、映射、文档、数据源和告警规则之间的关系。只有搞清楚数据是怎么进来的、类型是什么、放在哪个索引里,你写的告警规则才能真正发挥作用。