4、日志存储与索引:Elasticsearch 索引设计原则、分片与副本策略、生命周期管理
日志数据存到 Elasticsearch 里,说白了就是两个问题:怎么写进去,怎么查出来。写快了怕丢数据,查快了怕撑爆磁盘。我这些年踩过的坑,十有八九都跟索引设计有关。
今天咱们就聊聊索引设计的三板斧:分片与副本、生命周期管理,以及那些容易忽略的设计原则。
4.1 索引设计原则:别让 ES 替你背锅
很多人上来就建索引,字段全用 text,分片数随便填个 5。结果查询慢得像蜗牛,还怪 ES 性能差。其实问题出在设计上。
4.1.1 字段类型选对了吗?
日志里最常见的字段是时间戳、IP、状态码、请求路径。我建议你按这个原则来:
- 时间戳:用
date类型,别用text。否则你没法做时间范围聚合。 - IP 地址:用
ip类型。ES 原生支持 CIDR 匹配,查询效率高一个数量级。 - 状态码:用
keyword。你不需要对它做全文搜索,只需要精确匹配。 - 请求路径:用
keyword或text + keyword子字段。既支持精确匹配,又支持模糊搜索。
重要原则:能用 keyword 就别用 text。text 字段会分词,占用大量存储空间,而且查询性能差。只有真正需要全文搜索的字段才用 text。
4.1.2 索引模板:批量创建的利器
日志数据通常是按天或按小时创建索引的。手动建索引?不存在的。用索引模板自动搞定。
PUT _template/logs_template
{
"index_patterns": ["logs-*"],
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "30s"
},
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"client_ip": { "type": "ip" },
"status_code": { "type": "keyword" },
"request_path": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" }
}
},
"response_time_ms": { "type": "integer" }
}
}
}
这样,只要索引名匹配 logs-*,ES 就会自动套用模板。省心又规范。
4.2 分片与副本策略:多一分则肥,少一分则瘦
分片是 ES 的并行单元。副本是分片的备份。两者配合好了,性能和可靠性都能兼顾。
4.2.1 分片数怎么定?
我见过有人给一个 10GB 的索引分了 50 个分片。结果每个分片只有 200MB,查询时集群要协调 50 个分片,反而更慢。
我的经验是:每个分片控制在 20GB~50GB 之间。比如你每天产生 100GB 日志,分 3~5 个分片就够了。
| 数据量 | 建议分片数 | 说明 |
|---|---|---|
| < 50GB | 1~3 | 小索引,分片多了浪费资源 |
| 50GB~200GB | 3~5 | 适中,兼顾查询和写入 |
| 200GB~1TB | 5~10 | 大索引,需要更多并行度 |
| > 1TB | 10~20 | 超大索引,建议按时间拆分 |
小技巧:分片数一旦设定,后期很难修改。所以建索引前先估算好数据量。如果拿不准,宁可少分,也别多分。少了可以加节点,多了只能重建索引。
4.2.2 副本数:1 还是 2?
副本的作用有两个:高可用和提升查询吞吐。但副本多了,写入压力也会增大。
我个人习惯:生产环境至少 1 个副本。如果集群节点数足够,可以设 2 个。但别超过 3 个,否则写入性能会明显下降。
举个例子:你有一个 3 节点的集群,索引有 3 个主分片。设 1 个副本,总共 6 个分片,每个节点 2 个。设 2 个副本,总共 9 个分片,每个节点 3 个。写入时,每个分片都要写副本,压力翻倍。
4.3 生命周期管理:让 ES 自动「断舍离」
日志数据有个特点:越老越不值钱。昨天的日志可能还要查,去年的日志基本没人看。但 ES 不会自动清理,你得告诉它怎么做。
ES 的 ILM(Index Lifecycle Management) 就是干这个的。它把索引的生命周期分成四个阶段:
- Hot:热数据,频繁写入和查询。用 SSD 存储,分片数多。
- Warm:温数据,不再写入,偶尔查询。用 HDD 存储,可以合并分片。
- Cold:冷数据,很少查询。可以降低副本数,甚至只保留一份。
- Delete:删除数据。超过保留期限的直接删掉。
4.3.1 配置一个 ILM 策略
假设你的日志保留 30 天,前 7 天在 Hot 阶段,7~30 天在 Warm 阶段,30 天后删除。可以这样配:
PUT _ilm/policy/logs_policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "1d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"forcemerge": {
"max_num_segments": 1
},
"shrink": {
"number_of_shards": 1
},
"allocate": {
"require": {
"data_type": "warm"
}
}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
这个策略做了几件事:
- Hot 阶段:索引超过 50GB 或 1 天就滚动。避免单个索引过大。
- Warm 阶段:7 天后合并段、收缩分片、迁移到慢速节点。节省存储和内存。
- Delete 阶段:30 天后自动删除。不用你手动操心。
注意:ILM 的 rollover 动作需要索引别名配合。你写入数据时应该写别名,而不是直接写索引名。否则滚动不会生效。
4.3.2 我曾经踩过的坑
有一次,我配置了 ILM 策略,但忘了设置 rollover 的 max_age。结果一个索引跑了 3 个月,涨到 500GB。查询时 ES 直接 OOM 了。
后来我学乖了:永远给 rollover 设置一个时间上限。哪怕数据量很小,也要按天滚动。这样即使某个索引出了问题,影响范围也有限。
4.4 知识体系总览
下面这张图总结了本章的核心逻辑。你可以把它当作索引设计的检查清单:
4.5 总结
日志存储与索引,说白了就是三个字:规、分、管。
- 规:规范字段类型,用模板统一管理。
- 分:合理分片,控制副本数,别贪多。
- 管:用 ILM 自动管理生命周期,让 ES 自己干活。
做到这三点,你的 ES 集群就能稳定运行,不会动不动就 OOM 或者查询超时。嗯,这就是我这些年总结出来的经验。
公众号:蓝海资料掘金营,微信deep3321