4、日志存储与索引:Elasticsearch基础、日志索引策略、分片与副本设计
日志存进去了,怎么快速查出来?
这是个大问题。我见过不少团队,日志量一上来,查询直接卡死。不是ES不行,是没用好。说白了,Elasticsearch就是为日志场景量身定做的搜索引擎。但用好它,你得懂它的脾气。
4.1 Elasticsearch基础:倒排索引与全文搜索
ES为什么快?核心就是倒排索引。
传统数据库查"error",得一行行扫。ES不一样,它提前把每个词出现在哪些文档里记好了。你搜"error",它直接告诉你:第1、3、5行有。这就是倒排索引。
核心概念速览:
- 文档(Document):一条日志记录,JSON格式
- 索引(Index):同类文档的集合,类似数据库的表
- 分片(Shard):索引拆成的小块,每个分片是一个Lucene实例
- 副本(Replica):分片的备份,保证高可用
举个例子。一条Nginx日志,在ES里长这样:
{
"@timestamp": "2024-01-15T10:30:00+08:00",
"level": "ERROR",
"message": "Connection timeout to backend service: user-service",
"service": "api-gateway",
"duration_ms": 5023
}
ES会自动对message字段做分词。你搜"timeout",它瞬间定位到所有包含这个词的日志。我刚开始用ES时,觉得这没啥。直到有一次线上故障,几亿条日志里,我3秒就找到了那个"OOM"关键字——嗯,那一刻我才真正服了。
4.2 日志索引策略:别让索引变成垃圾场
日志索引策略,说白了就是怎么给你的日志分门别类、定时清理。我见过最惨的案例:有人把所有日志塞进一个索引,半年不清理,查询慢得像蜗牛。
我的建议:按时间滚动索引。
常用的策略:
| 日志类型 | 滚动周期 | 保留天数 | 说明 |
|---|---|---|---|
| 业务日志 | 每天 | 30天 | 按天建索引,方便删除旧数据 |
| 系统日志 | 每周 | 90天 | 量不大,周级别够用 |
| 审计日志 | 每月 | 365天 | 合规要求,必须存够一年 |
索引命名规范也很重要。我个人习惯用这种格式:
logs-业务名-环境-日期
# 例如:
logs-api-gateway-prod-2024.01.15
logs-user-service-prod-2024.01.15
为什么要这么搞?你想想看,哪天要查1月15号api-gateway的日志,直接搜这个索引就行。不用在全量数据里大海捞针。
小技巧:用ILM(Index Lifecycle Management)自动管理索引生命周期。设置好策略,ES会自动滚动、压缩、删除。省心得很。
4.3 分片与副本设计:别多也别少
分片和副本,是ES性能的关键。我踩过不少坑,这里直接说结论。
分片数怎么定?
一个分片就是一个Lucene实例。分片太多,集群管理开销大;分片太少,单分片数据量太大,查询慢。
我的经验公式:
- 单分片数据量控制在20-40GB。超过50GB,查询性能明显下降
- 分片总数 = 节点数 × 2~3。比如3个节点,分片数6-9个
- 每个节点的分片数不超过20。超过20,节点压力会很大
举个例子。假设每天日志量200GB,3个节点:
分片数 = 200GB / 30GB ≈ 7个
每个节点分片 = 7 / 3 ≈ 2.3个
嗯,7个分片,每个节点2-3个,合理。
副本数怎么定?
副本的作用:一是高可用,二是分担查询压力。
- 生产环境:副本数=1。保证一台挂了,数据不丢
- 查询量大:副本数=2。更多副本分担读请求
- 不要超过节点数-1。比如3个节点,副本最多2个
注意:副本不是越多越好。每个副本都要占用磁盘和内存。我曾经把副本设成3,结果磁盘直接爆了。嗯,血的教训。
4.4 实战:一个完整的日志索引配置
说了这么多,直接上配置。这是我常用的模板:
PUT /logs-api-gateway-prod-2024.01.15
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1,
"refresh_interval": "30s",
"translog.durability": "async",
"translog.sync_interval": "30s"
},
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"level": {
"type": "keyword"
},
"message": {
"type": "text",
"analyzer": "standard"
},
"service": {
"type": "keyword"
},
"duration_ms": {
"type": "integer"
}
}
}
}
几个关键点:
- refresh_interval设30秒:日志场景不需要实时可见,30秒刷新一次,写入性能提升明显
- translog异步刷盘:允许丢失少量数据,换取写入速度。日志丢了可以重打,但性能慢了影响业务
- keyword类型用于精确匹配:level、service这些字段,不需要分词,用keyword更快
避坑指南:我曾经把message字段设成keyword,结果搜"timeout"死活搜不到。为什么?keyword不分词,必须精确匹配整个字符串。所以日志内容一定要用text类型,配合分词器。
4.5 性能调优:让ES飞起来
最后分享几个我常用的调优手段:
- 批量写入:别一条一条插。用bulk API,每批5000条或5MB,写入速度提升10倍
- 禁用不需要的字段索引:有些字段只是存着,不查。把index设为false,省空间省性能
- 使用force merge:索引滚动后,手动合并段文件。查询能快30%
- 冷热数据分离:热节点用SSD,冷节点用HDD。7天前的日志自动迁移到冷节点
举个例子,禁用字段索引:
"properties": {
"raw_log": {
"type": "text",
"index": false // 只存不索引,省空间
}
}
嗯,ES调优是个细活。但只要你把分片、副本、索引策略搞明白了,90%的问题都能避免。剩下的10%,遇到了再说——反正日志都在,查就是了。