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飞起来

最后分享几个我常用的调优手段:

  1. 批量写入:别一条一条插。用bulk API,每批5000条或5MB,写入速度提升10倍
  2. 禁用不需要的字段索引:有些字段只是存着,不查。把index设为false,省空间省性能
  3. 使用force merge:索引滚动后,手动合并段文件。查询能快30%
  4. 冷热数据分离:热节点用SSD,冷节点用HDD。7天前的日志自动迁移到冷节点

举个例子,禁用字段索引:

"properties": {
  "raw_log": {
    "type": "text",
    "index": false  // 只存不索引,省空间
  }
}

嗯,ES调优是个细活。但只要你把分片、副本、索引策略搞明白了,90%的问题都能避免。剩下的10%,遇到了再说——反正日志都在,查就是了。