4、日志存储与索引:Elasticsearch集群搭建、索引生命周期管理(ILM)、冷热数据分离策略

日志存哪儿?怎么存?存多久?

这三个问题,几乎是每个做量化交易系统运维的人,早晚都要面对的。我刚开始管日志那会儿,想法很简单——全扔硬盘里,按天压缩。结果呢?一个月后,查个几小时前的日志,要解压好几个G的文件,grep跑半天。更别提磁盘报警天天响,半夜爬起来清数据,那滋味,真不好受。

后来我引入了Elasticsearch,配合ILM和冷热分离,才算真正把日志这块理顺了。今天我就把这套方案的核心思路和实战细节,跟你好好聊聊。

4.1 为什么是Elasticsearch?

说白了,日志系统要解决三个核心矛盾:写入快、查询快、存得久。传统的关系型数据库,在这三点上很难兼顾。你想想看,每秒几千条日志往里写,MySQL扛得住吗?就算扛住了,查起来也慢。

Elasticsearch天生就是为这种场景设计的。它基于Lucene,倒排索引,近实时搜索。写入时走内存Buffer,然后刷盘;查询时走索引,秒级响应。我个人的习惯是,只要日志量每天超过10GB,就果断上ES。

核心优势:

  • 近实时搜索(NRT):写入后1秒左右可查
  • 分布式架构:天然支持水平扩展
  • RESTful API:接入简单,生态丰富
  • 聚合分析:支持复杂的日志统计和告警

4.2 集群搭建:别一上来就搞三节点

很多人一听说ES集群,就想着至少三台机器。其实不一定。我建议你根据日志量来定。

如果每天日志量在50GB以内,单节点+副本数设为0,完全够用。等量上来了,再慢慢加节点。我在项目中遇到过,有人一开始就搭了5节点集群,结果跑了半年,每个节点磁盘利用率不到10%,纯属浪费。

4.2.1 单节点快速部署(开发/测试用)

# 下载并解压
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.0-linux-x86_64.tar.gz
tar -xzf elasticsearch-7.17.0-linux-x86_64.tar.gz

# 修改配置
cd elasticsearch-7.17.0/config
vim elasticsearch.yml

# 关键配置如下
cluster.name: trading-logs
node.name: node-1
path.data: /data/elasticsearch/data
path.logs: /data/elasticsearch/logs
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node

启动命令很简单:./bin/elasticsearch -d。然后访问 http://你的IP:9200,看到JSON返回,就说明成了。

4.2.2 生产环境:三节点集群

生产环境我建议至少三节点。为什么?因为ES的选举机制需要多数派。两个节点,挂一个就完蛋。三个节点,挂一个还能撑住。

# 每个节点的 elasticsearch.yml 配置示例
cluster.name: trading-logs-prod
node.name: node-1   # 分别改为 node-2, node-3
path.data: /data/elasticsearch/data
path.logs: /data/elasticsearch/logs
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["192.168.1.10:9300", "192.168.1.11:9300", "192.168.1.12:9300"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

注意:ES非常吃内存。我建议给ES的堆内存(heap)设置为机器物理内存的一半,但不要超过32GB。为什么呢?因为JVM的指针压缩技术在32GB以下才生效。超过32GB,性能反而下降。我曾经踩过这个坑,一台64GB的机器,给了48GB堆,结果GC频繁,查询慢得像蜗牛。

4.3 索引生命周期管理(ILM):让日志自动“退休”

日志这东西,越存越多。如果不加管理,磁盘迟早爆掉。ILM就是用来解决这个问题的——它可以让索引按照你设定的策略,自动流转、自动删除。

4.3.1 ILM的四个阶段

阶段 名称 典型操作
Hot 热阶段 频繁写入和查询,使用SSD
Warm 温阶段 不再写入,偶尔查询,使用HDD
Cold 冷阶段 很少查询,可以压缩,使用廉价存储
Delete 删除阶段 到期自动删除,释放空间

4.3.2 实战:定义一个ILM策略

我个人习惯,交易系统的日志保留30天。前7天在热节点,中间15天在温节点,最后8天在冷节点,到期删除。

PUT _ilm/policy/trading_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": {
          "set_priority": {
            "priority": 50
          },
          "allocate": {
            "require": {
              "data_type": "warm"
            }
          }
        }
      },
      "cold": {
        "min_age": "22d",
        "actions": {
          "set_priority": {
            "priority": 0
          },
          "allocate": {
            "require": {
              "data_type": "cold"
            }
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

小技巧:rollover动作很关键。它可以让索引在达到50GB或1天时自动“滚动”,生成新索引。这样每个索引都不会太大,查询和删除都很快。我曾经见过有人不设rollover,一个索引跑到几百GB,删除时卡死整个集群。

4.4 冷热数据分离:把钢用在刀刃上

为什么要做冷热分离?说白了,就是省钱。SSD贵,HDD便宜。热数据需要频繁写入和查询,放SSD上。冷数据几乎不查,放HDD上就行。

4.4.1 节点打标签

首先,给每个节点打上标签,告诉ES这个节点是热、温还是冷。

# 热节点配置
node.attr.data_type: hot

# 温节点配置
node.attr.data_type: warm

# 冷节点配置
node.attr.data_type: cold

4.4.2 创建索引模板,绑定ILM

有了节点标签和ILM策略,还需要一个索引模板,让所有新创建的日志索引自动应用这个策略。

PUT _index_template/trading_logs_template
{
  "index_patterns": ["trading-logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "index.lifecycle.name": "trading_logs_policy",
      "index.routing.allocation.require.data_type": "hot"
    }
  }
}

这样,所有以 trading-logs- 开头的索引,都会自动进入热节点。7天后,ES会自动把它们迁移到温节点。22天后,再迁移到冷节点。30天后,自动删除。

4.5 整体架构图

下面这张图,是我自己画的一个简化版架构。你可以看到日志从产生到消亡的完整流程。

日志存储与索引架构图 交易系统日志 Logstash/Filebeat Elasticsearch 集群 热节点 (SSD) 温节点 (HDD) 冷节点 (廉价存储) ILM 策略 自动删除 Kibana (可视化与查询) 运维人员 / 量化分析师

4.6 避坑指南

最后,分享几个我亲身踩过的坑,希望能帮你少走弯路。

我曾经... 把ILM策略里的 min_age 理解错了。我以为是从索引创建时间开始算,结果它是从rollover之后开始算。导致日志在热节点上只待了不到一天就跑到温节点了。后来查文档才发现,min_age 是相对于索引进入当前阶段的时间,不是创建时间。切记!

另一个坑:shard数量不要设太多。我见过有人设了30个shard,结果每个shard只有几百MB,查询时反而因为shard过多导致性能下降。一般建议每个shard 20-50GB。比如你每天50GB日志,设3个shard就差不多了。

嗯,关于日志存储与索引,今天就聊到这儿。这套方案我在多个交易系统里验证过,稳定运行了两年多,没出过什么大问题。你如果按照这个思路去搭,应该也能省不少心。


专注资料整理