Elasticsearch 日志索引设计:按小时切分 + 小时内 Rollover 兜底(ILM 实战,附完整脚本)

Elasticsearch 日志索引设计:按小时切分 + 小时内 Rollover 兜底(ILM 实战,附完整脚本)

日志场景下 Elasticsearch 索引怎么切?按天建索引,大流量日单索引轻松突破几百 GB,merge 变慢、查询抖动;只靠 ILM 按大小 rollover,索引边界又和业务时间对不上,排障时想拉某个小时的日志都费劲。本文给出一套外层按整点小时切分 + 内层 Rollover 兜底的双层别名方案,ILM 策略、索引模板、cron 脚本、应用层写入路由全部可复制。

一、为什么日志索引推荐按小时切分

先看两种常见做法的坑:

做法 问题
单一大索引(按月/永久) 索引无限膨胀,segment merge 停不下来,删除只能 delete_by_query,集群越用越慢
按天切分 常规流量没问题,但高峰日单索引可到几百 GB;排查问题想精确定位某小时,仍要在巨型索引里扫
只用 ILM rollover 索引边界由数据量决定,与时间无关,对账、归档、按时间段删除都不方便

日志数据有强时间特征:排查看小时、对账按小时、归档删过期也按时间。所以理想方案是索引边界严格对齐整点小时,同时在单小时数据突增时还能自动拆分------这就是双层设计。

二、方案总览:双层别名架构

  • 外层(小时级):每整点由 cron 新建一个独立索引,名字带小时戳
  • 内层(rollover 级):同一小时内数据量超阈值时自动 rollover 出新物理索引
  • 双层 alias:外层别名按小时命名,承载写入和查询;内层 rollover 由 ILM 维护
text 复制代码
logs-2026061114-000001 ─┐
logs-2026061114-000002 ─┼─ alias: logs-2026061114 (is_write_index: 当前可写)
logs-2026061115-000001 ──── alias: logs-2026061115 (is_write_index: 当前可写)

应用层只认 logs-YYYYMMDDHH 这个小时级别名,rollover 发生时别名自动切换,写入方零改动

需要的资源一共 5 项:

资源类型 名称 数量 作用
ILM Policy logs-policy 1 份 全集群共用,管 rollover 和过期删除
Index Template logs-template 1 份 自动应用 mappings + settings
Cron 脚本 create_hourly_index.sh 1 个 每整点执行
外层 Alias logs-YYYYMMDDHH 每小时 1 个 写入与查询入口
内层 Alias 索引名即别名 每物理索引 1 个 rollover 维护

三、部署步骤

3.1 创建 ILM 策略(全集群一份)

bash 复制代码
PUT _ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "20gb",
            "max_docs": 100000000
          }
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": { "delete": {} }
      }
    }
  }
}

阶段说明:

  • hot:可写阶段,单索引到 20GB 或 1 亿文档即 rollover,防止索引过大
  • warm:3 天后强制段合并为 1 段,省存储、提查询
  • delete:30 天后自动删除

⚠️ 不要配 max_age 。整点切索引已经承担了时间维度的切分,ILM 里再配 max_age 会和小时边界冲突,出现"不到整点就滚"的脏边界。

3.2 创建索引模板

bash 复制代码
PUT _index_template/logs-template
{
  "index_patterns": ["logs-*"],
  "priority": 100,
  "template": {
    "settings": {
      "number_of_shards": 10,
      "number_of_replicas": 1
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "level":      { "type": "keyword" },
        "message":    { "type": "text" },
        "service":    { "type": "keyword" },
        "host":       { "type": "keyword" }
      }
    }
  }
}

注:mappings 按实际日志字段补充。ILM 绑定和 rollover_alias 不要 写在模板里,留到每小时的种子索引上单独指定,避免污染所有 logs-* 索引。

3.3 Cron 脚本:每整点创建种子索引

脚本路径 /opt/scripts/create_hourly_index.sh

bash 复制代码
#!/bin/bash
set -euo pipefail

ES_HOST="${ES_HOST:-http://es-host:9200}"
HOUR=$(date -u +%Y%m%d%H)
INDEX="logs-${HOUR}-000001"
ALIAS="logs-${HOUR}"

# 幂等检查:索引已存在则跳过
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -u "${ES_USER}:${ES_PASS}" \
  -X HEAD "${ES_HOST}/${INDEX}")

if [ "$STATUS" = "200" ]; then
  echo "[$(date)] Index ${INDEX} already exists, skip."
  exit 0
fi

if [ "$STATUS" != "404" ]; then
  echo "[$(date)] Unexpected status ${STATUS} for ${INDEX}, abort."
  exit 1
fi

# 创建种子索引,同时挂外层 alias 和 ILM 配置
RESP=$(curl -s -u "${ES_USER}:${ES_PASS}" \
  -X PUT "${ES_HOST}/${INDEX}" \
  -H 'Content-Type: application/json' \
  -d "{
    \"aliases\": {
      \"${ALIAS}\": { \"is_write_index\": true }
    },
    \"settings\": {
      \"index.lifecycle.name\": \"logs-policy\",
      \"index.lifecycle.rollover_alias\": \"${ALIAS}\"
    }
  }")

echo "[$(date)] Create ${INDEX} alias ${ALIAS}: ${RESP}"

crontab 配置:

bash 复制代码
# 每小时 0 分 5 秒执行(避开整点集群压力尖峰)
5 * * * * /opt/scripts/create_hourly_index.sh >> /var/log/es-hourly-index.log 2>&1

两个细节:脚本里 date -u 用 UTC,全链路统一时区;crontab 建议主备双机部署,防止单点漏建。

四、应用层接入

4.1 写入路由:按当前小时选别名

Java 示例:

java 复制代码
String hour = ZonedDateTime.now(ZoneOffset.UTC)
    .format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
String alias = "logs-" + hour;

IndexRequest req = new IndexRequest(alias)
    .source(jsonMap, XContentType.JSON);

Python 示例:

python 复制代码
from datetime import datetime, timezone

def current_alias():
    hour = datetime.now(timezone.utc).strftime("%Y%m%d%H")
    return f"logs-{hour}"

es.index(index=current_alias(), document=log_entry)

4.2 查询姿势

bash 复制代码
# 单小时查询
GET logs-2026061114/_search

# 跨小时:明确列出,别用通配符(展开慢)
GET logs-2026061113,logs-2026061114/_search

# 时间范围查询兜底
GET logs-*/_search
{
  "query": {
    "range": { "@timestamp": { "gte": "now-2h", "lte": "now" } }
  }
}

五、Rollover 行为详解

触发时机:小时内数据满足任一条件自动 rollover------单索引 ≥20GB,或文档数 ≥1 亿。

状态变化

text 复制代码
触发前:
  logs-2026061114-000001 ← 可写 (is_write_index: true)

触发后:
  logs-2026061114-000001 ← 只读
  logs-2026061114-000002 ← 可写 (is_write_index: true)

小时别名 logs-2026061114 自动指向 000002,应用层无感知。

与下个整点的衔接

  • 14:59:59 数据仍写入 logs-2026061114-000002
  • 15:00:05 cron 执行,新建 logs-2026061115-000001
  • 应用层下次写入按当前小时自动切到 logs-2026061115-*

六、验证与日常运维命令

bash 复制代码
# 查看某小时索引的 ILM 进度
GET logs-2026061114/_ilm/explain

# 查看别名当前指向
GET _alias/logs-2026061114

# 列出最近索引的大小与文档数
GET _cat/indices/logs-*?h=index,docs.count,store.size&s=index

测试时手动触发一次 rollover 验证链路:

bash 复制代码
POST logs-2026061114/_rollover
{
  "conditions": { "max_docs": 1 }
}

七、监控告警配置

监控项 阈值 级别
某小时索引缺失 当前时间 +5 分钟该小时索引不存在 Critical
ILM 阶段卡住 phase 5 分钟无变化 Warning
集群分片数 接近 cluster.max_shards_per_node Warning
磁盘使用率 ≥80% Warning

监控建议接 Prometheus + ElastAlert 或自研 watcher,重点盯索引缺失------cron 失败意味着应用写入直接报错。

八、踩坑清单

后果 应对
cron 失败整点索引缺失 应用层写入失败 监控告警 + 应用层降级写 logs-* 通配兜底
单小时数据爆增 索引过大性能劣化 rollover 自动兜底(max_size 20gb)
rollover_alias 配置缺失 ILM 静默不工作 种子索引脚本里强制写入该配置
跨小时查询用 logs-* 通配展开慢 应用层按需拼接具体小时别名
应用节点时区不一致 写错小时索引 全链路统一 UTC

九、部署 Checklist

  • 创建 ILM policy logs-policy
  • 创建 Index template logs-template
  • 部署 create_hourly_index.sh 到独立调度机
  • 配置 crontab(主备双机)
  • 应用层改造写入路由逻辑
  • 配置监控告警(缺失索引、ILM 卡住、磁盘)
  • 测试环境完整跑一次 rollover 验证

十、版本兼容性

ES 版本 支持情况
7.8+ ✅ 推荐,ILM + rollover 稳定
7.0 - 7.7 ⚠️ 部分 API 行为有差异,建议升级
8.x ✅ 兼容本方案全部 API

总结

这套方案的本质是让时间边界和数据量边界各管各的:整点切分满足排障、对账、归档的时间对齐需求,ILM rollover 兜住流量突增,双层别名把两者对应用层透明。全部资源就一份 ILM 策略 + 一份模板 + 一个 cron 脚本,复制文中 DSL 即可落地。

你们的日志索引是按天切、按小时切,还是纯 rollover?有没有遇到过单索引几百 GB 的场面?评论区聊聊你的切法。


文中脚本与 DSL 在 ES 7.17/8.x 环境验证通过,字段映射请按实际业务调整。

相关推荐
Elasticsearch2 小时前
生产环境向量搜索的一个设置:vectordb_document 模式如何自动调优 Elasticsearch
elasticsearch
【赫兹威客】浩哥2 小时前
基于SpringBoot+Vue3的舆情文本分析系统|Elasticsearch检索+情感分析+话题热度追踪 毕设项目
spring boot·elasticsearch·课程设计
天涯明月19932 小时前
ray深度研究报告
大数据·人工智能·分布式·ray
玉&心3 小时前
在grafana中加入elasticsearch的日志dashboard
elasticsearch·grafana
迪康coolmu3 小时前
企业文件外发防泄漏实践:从通道封堵到三层全链路管控
大数据·运维·网络·人工智能·安全·阿里云
Elasticsearch4 小时前
让大模型思考,让小模型执行:在 Elastic Workflows 中拆分 LLM 成本
elasticsearch
数字融合11 小时前
透明化地铁线视频孪生综合监控项目技术
大数据·人工智能·virtualenv
东莞和裕包装13 小时前
高边压强度定制重型纸箱在汽车零部件堆叠运输中的不可替代性
大数据·汽车
JoyCong199813 小时前
ToDesk个人版、专业版、游戏版、设计版、性能版、团队版权益介绍
大数据·运维·游戏·远程工作·远程操作