时间序列建模

时间序列建模 #

时间序列数据(如日志、指标、事件流)是 Easysearch 的常见用例。这类数据有几个特点:文档数量快速增长、基本不更新、主要查询最近的数据。本页介绍如何为时间序列数据设计索引结构。

时间序列数据的特点 #

与传统的搜索场景不同,时间序列数据有以下特点:

  • 文档数量快速增长:日志、指标等数据持续写入,不会停顿
  • 文档基本不更新:写入后很少修改,主要是追加
  • 查询集中在最近数据:大多数查询关注最近几小时、几天或几周的数据
  • 旧数据逐渐失去价值:随着时间推移,旧数据的查询频率降低

按时间范围索引 #

如果我们为此种类型的文档建立一个超大索引,我们可能会很快耗尽存储空间。日志事件会不断的进来,不会停顿也不会中断。

我们可以使用 scroll 查询和批量删除来删除旧的事件。但这种方法非常低效。当你删除一个文档,它只会被标记为被删除。在包含它的段被合并之前不会被物理删除。

替代方案是,我们使用一个时间范围索引。你可以着手于一个按年的索引 (logs_2014) 或按月的索引 (logs_2014-10)。也许当你的数据变得十分繁忙时,你需要切换到一个按天的索引 (logs_2014-10-24)。删除旧数据十分简单:只需要删除旧的索引。

这种方法有这样的优点,允许你在需要的时候进行扩容。你不需要预先做任何艰难的决定。每天都是一个新的机会来调整你的索引时间范围来适应当前需求。

应用相同的逻辑到决定每个索引的大小上。起初也许你需要的仅仅是每周一个主分片。过一阵子,也许你需要每天五个主分片。这都不重要——任何时间你都可以调整到新的环境。

使用别名管理时间序列索引 #

别名可以帮助我们更加透明地在索引间切换。当创建索引时,你可以将 logs_current 指向当前索引来接收新的日志事件,当检索时,更新 last_3_months 来指向所有最近三个月的索引:

POST /_aliases
{
  "actions": [
    { "add":    { "alias": "logs_current",  "index": "logs_2014-10" }},
    { "remove": { "alias": "logs_current",  "index": "logs_2014-09" }},
    { "add":    { "alias": "last_3_months", "index": "logs_2014-10" }},
    { "remove": { "alias": "last_3_months", "index": "logs_2014-07" }}
  ]
}

这样,写入操作始终使用 logs_current 别名,查询操作可以使用 last_3_months 别名来查询最近三个月的数据。

时间序列索引的最佳实践 #

1. 选择合适的索引粒度 #

  • 按年索引:适合数据量较小、查询跨度较长的场景
  • 按月索引:适合中等数据量、查询跨度中等的场景
  • 按天索引:适合数据量很大、查询主要集中在最近几天的场景

2. 合理设置分片数 #

  • 根据单索引的数据量设置分片数
  • 避免过多的小分片,也避免过少的大分片
  • 考虑查询的并行度和写入的吞吐量

3. 使用索引模板 #

使用索引模板可以自动创建时间序列索引:

PUT /_template/logs_template
{
  "index_patterns": ["logs_*"],
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "message": {
        "type": "text"
      }
    }
  }
}

4. 定期清理旧数据 #

  • 根据业务需求设置数据保留策略
  • 定期删除超过保留期的索引
  • 可以使用索引生命周期管理(如果支持)自动化这个过程

查询时间序列数据 #

查询时间序列数据时,通常需要:

  1. 指定时间范围:使用 range 查询过滤时间
  2. 使用别名:通过别名查询多个时间段的索引
  3. 时间聚合:使用 date_histogram 聚合分析趋势

示例:

GET /last_3_months/_search
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-7d/d"
      }
    }
  },
  "aggs": {
    "per_hour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "hour"
      }
    }
  }
}

小结 #

  • 时间序列数据适合按时间范围组织索引,而不是使用单个大索引
  • 使用别名可以透明地管理时间序列索引的切换
  • 根据数据量和查询模式选择合适的索引粒度(年/月/天)
  • 定期清理旧数据,避免存储成本过高
  • 使用索引模板自动化索引创建过程

下一步可以继续阅读: