4. 日志存储与索引:Elasticsearch集群搭建、ILM、冷热分离与分片策略
做高频交易日志分析,说白了就是跟海量数据打交道。我最早接手这个系统时,一天就能产生几个TB的日志。你想想看,几万笔订单、几十万次行情快照、再加上各种系统心跳——如果没有一个靠谱的存储方案,光查一条日志就能让你等到怀疑人生。
今天我们就来聊聊Elasticsearch这套方案。我个人习惯把它当作高频交易日志的“数据湖”。但光有湖不行,你得会挖渠、会分池子、还得知道什么时候该放水。嗯,这就是ILM、冷热分离和分片策略要做的事。
4.1 Elasticsearch集群搭建:别小看这一步
很多人觉得ES集群搭建就是装个包、改个配置、启动就完事了。我在项目中踩过这个坑——第一次搭建时,节点配置没调好,结果上线第一天就OOM了。
这里我建议你至少准备3个节点。为什么是3?因为ES的选举机制需要多数派。2个节点挂一个就废了,3个节点还能扛住一个故障。
节点角色要分清楚:
- Master节点:负责集群管理,别让它存数据。我一般用3台小规格机器专门做Master。
- Data节点:真正存日志的地方。高频交易日志写入量大,建议用SSD。
- Ingest节点:做数据预处理。比如解析日志格式、过滤字段。
核心配置示例(elasticsearch.yml):
# 节点角色配置
node.roles: [ master, data, ingest ]
# 内存配置 - 这是最关键的
bootstrap.memory_lock: true
ES_JAVA_OPTS="-Xms32g -Xmx32g"
# 防止脑裂
discovery.seed_hosts: ["node1:9300", "node2:9300", "node3:9300"]
cluster.initial_master_nodes: ["node1", "node2", "node3"]
我的经验:堆内存不要超过32GB。超过这个值,JVM的压缩指针就失效了,性能反而下降。我见过有人给128GB,结果GC时间比查询时间还长。
4.2 索引生命周期管理(ILM):让数据自动流转
高频交易日志有个特点:越新的数据越有价值。昨天的日志可能还要查,上周的日志基本就归档了。手动去删索引?别傻了,一天几百个索引,手会断的。
ILM就是干这个的。它定义了索引从生到死的完整流程。我习惯分四个阶段:
| 阶段 | 时间 | 动作 | 存储介质 |
|---|---|---|---|
| Hot | 当前-1小时 | 写入+高频查询 | SSD |
| Warm | 1小时-1天 | 只读+偶尔查询 | SSD |
| Cold | 1天-7天 | 只读+极少查询 | HDD |
| Delete | 7天后 | 删除 | - |
ILM策略怎么写?看这个例子:
PUT _ilm/policy/hft_logs_policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "1h"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "1h",
"actions": {
"allocate": {
"require": {
"data_type": "warm"
}
},
"forcemerge": {
"max_num_segments": 1
},
"shrink": {
"number_of_shards": 1
}
}
},
"cold": {
"min_age": "1d",
"actions": {
"allocate": {
"require": {
"data_type": "cold"
}
}
}
},
"delete": {
"min_age": "7d",
"actions": {
"delete": {}
}
}
}
}
}
注意:rollover的条件要设合理。我曾经设了max_docs=1000万,结果日志量暴增时,一个索引还没rollover就撑爆了磁盘。建议用max_size做主条件,max_age做兜底。
4.3 冷热数据分离:把钱花在刀刃上
SSD贵啊。高频交易日志一天几个TB,全放SSD?老板会杀了你的。冷热分离的核心思想就是:热数据放SSD,冷数据放HDD。
实现方式其实很简单——给节点打标签:
# 热节点配置
node.attr.data_type: hot
# 冷节点配置
node.attr.data_type: cold
然后在ILM策略里,通过allocate动作把数据路由到对应节点。就像上面代码里写的,warm阶段要求data_type=warm,cold阶段要求data_type=cold。
这里有个细节:热节点和冷节点的硬件配置要差异化。热节点CPU核数多、内存大、SSD快;冷节点CPU可以弱一些,内存给个16GB就够了,磁盘用大容量HDD。
我的做法:热节点用NVMe SSD,冷节点用SATA SSD或者7200转的HDD。成本能降60%以上,但查询冷数据的延迟也就多个几十毫秒——对于历史复盘来说完全可以接受。
4.4 分片与副本策略:多一分则肥,少一分则瘦
分片是ES的并行单元。分片太多,管理开销大;分片太少,写入瓶颈明显。我见过最夸张的一个索引分了200个分片,结果查询时光合并结果就花了半分钟。
高频交易日志的写入特点是:持续高吞吐、单条数据小。基于这个特点,我建议:
- 分片数:每个分片20-40GB。比如一天产生500GB日志,分15-25个分片比较合适。
- 副本数:1个就够了。高频交易日志丢了可以重放,不需要像业务数据那样搞3副本。
- 分片大小:控制在50GB以内。超过50GB,查询和恢复都会变慢。
举个例子,假设你一天产生300GB日志:
PUT _template/hft_logs_template
{
"index_patterns": ["hft-logs-*"],
"settings": {
"number_of_shards": 10,
"number_of_replicas": 1,
"routing.allocation.require.data_type": "hot"
}
}
避坑指南:我曾经把副本数设成2,结果集群总数据量变成了3倍。磁盘报警器响了一整夜。后来我改成1副本,写入性能反而提升了——因为副本同步的开销小了。
4.5 整体架构图
说了这么多,画张图帮你理清思路。这是我在项目中实际使用的架构:
这张图里,数据从左边流入,经过Ingest节点清洗,然后路由到Hot节点。ILM策略像一只无形的手,把数据从Hot推到Warm,再推到Cold,最后删除。整个过程全自动,你只需要在Kibana上看看监控面板就行。
4.6 性能调优的几个关键点
最后分享几个我在实战中总结的调优经验:
- 写入调优:把refresh_interval设成30秒甚至更久。高频交易日志不需要实时可见,延迟30秒完全能接受。这样能大幅减少segment合并的开销。
- 批量写入:用bulk API,每批5000-10000条。我测试过,批量比单条写入快10倍以上。
- 禁用不需要的特性:如果不需要全文搜索,把_all字段关掉。不需要聚合分析,把doc_values关掉。每关一个特性,写入性能都能提升20%。
- 监控关键指标:重点关注GC次数、磁盘I/O等待、segment数量。这三个指标能反映集群的健康状况。
曾经踩过的坑:有一次我发现写入越来越慢,查了半天发现是磁盘I/O被打满了。原因是某个同事在Cold节点上跑了大量聚合查询。从那以后,我强制把Cold节点的查询线程数限制到2个——历史数据查询慢点就慢点,别影响写入就行。
好了,ES这块的内容就聊到这儿。记住一句话:架构设计决定了上限,细节调优决定了下限。ILM和冷热分离是架构层面的设计,分片和副本是细节层面的调优。两者缺一不可。
公众号:蓝海资料掘金营,微信deep3321