3. 日志存储与索引:Elasticsearch存储模型、索引生命周期管理、冷热数据分离策略
好,咱们进入第三章。说实话,日志存储这块儿,是很多风控系统从「能用」到「扛得住」的分水岭。我见过太多团队,日志一多,ES 集群直接被打挂,查询慢得像蜗牛。嗯,今天咱们就把这块硬骨头啃下来。
3.1 Elasticsearch 存储模型:别把日志当关系数据库用
很多人刚接触 ES,习惯性地拿它当 MySQL 用。我一开始也犯过这错。其实 ES 的存储模型,核心就三个概念:索引(Index)、分片(Shard)、段(Segment)。
- 索引:说白了就是一张「表」,但它是倒排索引结构。风控日志里,我习惯按天建索引,比如
risk-log-2025-04-01。 - 分片:一个索引拆成多个分片,分布在不同的节点上。分片数设多少?我建议按数据量和节点数来算。比如每天 10GB 日志,3 个节点,分片数设 6~9 个就够。太多分片反而浪费资源。
- 段:每个分片内部由多个小段组成。ES 会定期合并段,减少文件数,提升查询速度。但合并时 CPU 和 IO 开销很大,我一般安排在凌晨低峰期。
核心要点:ES 不是关系型数据库,别搞什么关联查询、事务。风控日志就是写进去、查出来,简单粗暴。
举个例子,我曾在项目中遇到一个坑:某团队把风控日志的索引分片数设成了 30 个,结果集群每天凌晨段合并时 CPU 飙到 100%,查询直接超时。后来我改成 6 个分片,问题就解决了。你想想看,分片不是越多越好,得匹配你的数据量和硬件。
3.2 索引生命周期管理:让日志自动「退休」
风控日志有个特点:越新的数据越有价值。比如实时风控决策,查的是最近 5 分钟的日志;事后分析,可能查最近 7 天的;而 30 天前的日志,基本只有审计才会翻。
所以,我们不能让所有索引都永久保留。ES 提供了 ILM(Index Lifecycle Management),说白了就是给索引定个「退休计划」。
我个人习惯把索引分成四个阶段:
| 阶段 | 动作 | 说明 |
|---|---|---|
| Hot | 写入 + 高频查询 | 最近 1 天的日志,SSD 存储,副本数 2 |
| Warm | 只读 + 低频查询 | 2~7 天的日志,HDD 存储,副本数 1 |
| Cold | 只读 + 极少查询 | 8~30 天的日志,压缩存储,副本数 0 |
| Delete | 删除索引 | 超过 30 天的日志,直接清理 |
配置 ILM 策略其实很简单,看下面这段代码:
PUT _ilm/policy/risk_log_policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "1d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "2d",
"actions": {
"allocate": {
"require": {
"data_type": "warm"
}
},
"forcemerge": {
"max_num_segments": 1
},
"shrink": {
"number_of_shards": 1
}
}
},
"cold": {
"min_age": "8d",
"actions": {
"allocate": {
"require": {
"data_type": "cold"
}
},
"freeze": {}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
避坑指南:我曾经把 rollover 的 max_size 设成 10GB,结果一天产生了 20 多个小索引,管理起来非常麻烦。后来改成 50GB,每天 3~4 个索引,刚刚好。记住,索引数量控制在 100 个以内,集群压力会小很多。
3.3 冷热数据分离策略:把钢用在刀刃上
为什么要做冷热分离?说白了就是省钱。SSD 贵,HDD 便宜。热数据(最近 1 天)需要快速写入和查询,必须放 SSD;冷数据(7 天以上)几乎没人查,放 HDD 就行。
我习惯在 ES 集群里打标签。比如给节点打上 data_type: hot 或 data_type: cold,然后通过 ILM 策略自动迁移数据。
下面这张图是我自己画的冷热分离架构,你看一眼就明白了:
配置节点标签也很简单,在 elasticsearch.yml 里加上:
node.attr.data_type: hot # 或者 warm、cold
然后在 ILM 策略里通过 allocate 动作指定数据迁移到哪个标签的节点上。嗯,这里要注意:冷节点上的数据是只读的,不能写入。我曾经有个同事把冷节点也配成可写入,结果数据分布乱成一锅粥。
警告:冷热分离不是万能的。如果你的查询模式是「随机查任意一天的数据」,那冷热分离反而会拖慢查询。风控日志的特点是「近期查询多,远期查询少」,这才适合冷热分离。
3.4 实战经验:索引模板与别名
最后分享一个小技巧。我习惯用 索引模板 配合 别名 来管理日志索引。比如:
PUT _template/risk_log_template
{
"index_patterns": ["risk-log-*"],
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"index.lifecycle.name": "risk_log_policy",
"index.lifecycle.rollover_alias": "risk-log-write"
},
"mappings": {
"properties": {
"timestamp": { "type": "date" },
"user_id": { "type": "keyword" },
"event_type": { "type": "keyword" },
"risk_score": { "type": "float" },
"message": { "type": "text" }
}
}
}
这样,每天新生成的索引会自动套用模板,省去手动配置的麻烦。别名 risk-log-write 指向当前可写的索引,应用程序只管往别名里写数据,底层索引切换完全透明。
个人经验:我曾在一次大促前,忘了给索引模板配 ILM 策略,结果日志量暴增,集群磁盘直接打满。后来我养成了一个习惯:每次上线前,先往测试环境写 100 万条日志,验证 ILM 策略是否生效。你想想看,这步检查能省多少事。
好了,这一章的内容就这些。记住:存储模型是基础,ILM 是自动化工具,冷热分离是省钱利器。把这三点吃透,你的 ES 集群就能稳稳扛住风控日志的洪流。
公众号:蓝海资料掘金营,微信deep3321