在现代企业的 IT 架构中,日志数据往往占据了数据存储的"半壁江山"。从应用程序日志、网关日志到基础设施监控指标,这些时序数据(Time Series Data)具有写入量大、只有追加操作(Append-only)、随时间推移价值递减的特点。
传统的管理方式通常是按天生成索引(如 logs-2023-10-01),但这带来了管理上的复杂性:如何自动化清理旧数据?如何处理某天流量暴增导致单个索引过大的问题?查询时如何避免编写复杂的通配符?
INFINI Easysearch 支持原生的 Data Stream(数据流) 功能,结合其独有的 ZSTD 压缩技术,为海量日志管理提供了一套既简单又高效的解决方案。本文将带你深入了解并实战 Easysearch 的数据流管理。
什么是 Data Stream? #
Data Stream 是 Elasticsearch(Easysearch 完全兼容)引入的一种抽象概念,专门用于简化时序数据的管理。
即使用户看到的是一个统一的名称(例如 logs-prod),但在后台,Easysearch 自动维护了一个隐藏的索引列表(Backing Indices)。
- 写入时:数据总是写入最新的那个"写索引"。
- 查询时:请求会自动路由到所有包含相关数据的索引上。
- 滚动(Rollover):配合索引生命周期管理(ILM),当"写索引"达到一定大小或时间(如 50GB 或 7天)后,会自动创建一个新的写索引,旧索引转为只读。
简单来说,Data Stream 让你不再需要关心具体的索引名称和切分逻辑,只需对着一个名字读写即可。
为什么在 Easysearch 中使用 Data Stream? #
除了简化运维,Easysearch 的核心优势能让 Data Stream 方案发挥更大价值:
- 极致压缩(降本):日志数据通常重复率高。Easysearch 支持 ZSTD 压缩算法(
ZSTD),结合 Data Stream 的自动滚动机制,可以将旧数据的存储成本降低 40%-50%。 - 写入稳定性:Easysearch 对写入进行了深度优化,能够承受 Data Stream 场景下高并发的日志吞吐,不易出现节点 Crash 或 OOM。
- 生命周期自动化:通过 ILM 策略,自动完成从 热(Hot)-> 温(Warm)-> 冷(Cold)-> 删除 的全生命周期流转。
实战:配置 Easysearch Data Stream #
下面我们通过一个完整的示例,演示如何创建一个用于存储 Nginx 日志的数据流,并应用生命周期策略。
第一步:创建索引生命周期策略 (ILM Policy) #
首先,我们需要定义数据的"生老病死"。假设我们的策略如下:
- Hot 阶段:索引大小达到 30GB 或创建时间超过 1 天,则滚动生成新索引。
- Warm 阶段:滚动后立即进入 Warm 阶段,设为只读。
- Delete 阶段:数据保留 30 天后自动删除。
PUT _ilm/policy/logs_policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_size": "30gb",
"max_age": "1d"
}
}
},
"warm": {
"min_age": "0ms",
"actions": {
"readonly": {}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
第二步:创建组件模板 (Component Templates) #
为了复用配置,我们建议使用组件模板。这里我们将配置映射(Mappings)**和**设置(Settings)。
关键点:在这里我们开启 Easysearch 的 ZSTD 压缩,这是降本的关键配置。
# 1. 配置 Settings,启用 ZSTD 压缩
PUT _component_template/logs_settings
{
"template": {
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"index.lifecycle.name": "logs_policy",
"index.codec": "ZSTD"
}
}
}
# 2. 配置 Mappings,定义字段结构
PUT _component_template/logs_mappings
{
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"host": {
"type": "keyword"
},
"message": {
"type": "text"
},
"status_code": {
"type": "integer"
}
}
}
}
}
第三步:创建索引模板 (Index Template) #
这是最重要的一步。我们需要创建一个索引模板,将上面的组件组合起来,并显式启用 data_stream 模式。
PUT _index_template/logs_template
{
"index_patterns": [
"logs-nginx-*"
],
"data_stream": {},
"composed_of": [
"logs_settings",
"logs_mappings"
],
"priority": 100
}
第四步:创建 Data Stream 并写入数据 #
配置完成后,我们不需要显式调用 “Create Data Stream” 的 API。当我们第一次向匹配 index_patterns 的名称写入数据时,Data Stream 会自动创建。
注意:Data Stream 强制要求文档必须包含 @timestamp 字段。
# 写入第一条数据,Easysearch 会自动创建名为 logs-nginx-prod 的 Data Stream
POST logs-nginx-prod/_doc
{
"@timestamp": "2023-10-27T10:00:00Z",
"host": "web-server-01",
"message": "GET /index.html 200",
"status_code": 200
}
第五步:验证与管理 #
写入成功后,我们可以查看 Data Stream 的状态:
GET _data_stream/logs-nginx-prod
返回结果将显示该流背后的具体索引(如 .ds-logs-nginx-prod-000001)。
如果需要查询数据,直接使用 Data Stream 的名称,就像操作普通索引一样:
GET logs-nginx-prod/_search
{
"query": {
"match": {
"status_code": 200
}
}
}
Data Stream 的限制与注意事项 #
在使用 Easysearch Data Stream 时,有几点需要注意:
- 仅限追加(Append-Only):Data Stream 设计初衷是处理时序数据,因此不支持直接更新(Update)已存在的文档。如果确实需要修改旧数据,必须通过
_update_by_query或者直接对背后的具体索引进行操作(不推荐)。 - 必须包含时间戳:写入的文档必须包含
@timestamp字段(或者在映射中定义好的自定义时间字段)。 - 删除操作:你不能直接删除 Data Stream 中的某条文档,但可以使用
delete_by_query。通常建议依赖 ILM 策略自动删除过期的整个索引,效率最高。
总结 #
通过在 INFINI Easysearch 中使用 Data Stream,我们实现了日志管理的"自动驾驶":
- 运维侧:无需编写脚本每天创建索引,无需手动删除旧数据,ILM 策略全自动接管。
- 开发侧:读写入口统一,不再受限于
logs-YYYY-MM-DD这种日期命名的困扰。 - 成本侧:利用 Easysearch 的 ZSTD 压缩,在不改变业务逻辑的前提下,大幅节省存储空间(通常可达 50%)。
对于任何需要处理大量时序日志、监控指标的企业,Easysearch Data Stream + ILM + ZSTD 都是目前的最佳实践组合。





