34 · 索引模板与 data stream(自动套 mapping / 时序数据)
阶段:第四阶段 / 写入与索引管理
ES:index template / component template / data stream | PostgreSQL:CREATE TABLE ... LIKE模板 + 分区表
1. 概念
前面第 33 篇是「手动建一个索引」。但真实场景里索引常常是动态创建的
(按天/按月滚动,如logs-2026.08.01),你不可能每次手写 mapping。
- index template(索引模板):预先定义「索引名匹配某模式时,自动套用哪套 settings/mappings」。
新索引一创建就自动带上正确结构。 - component template(组件模板):可复用的 mapping/settings 片段,多个索引模板拼装它,避免重复。
- data stream(数据流):面向只追加的时序数据(日志、指标、事件)的高级抽象——
你只对一个名字写入,底层自动滚动出一串隐藏索引,配合 ILM 自动管理生命周期。
2. PostgreSQL 对照
| ES | PostgreSQL |
|---|---|
| component template | 可复用的列定义 /LIKE table INCLUDING ALL |
| index template | 「新表自动套用某结构」的约定 |
| data stream | 分区表(按时间自动路由到子分区,只追加) |
| rollover | 分区滚动(新月份自动进新分区) |
3. ES DSL
3.1 组件模板(可复用片段)
PUT _component_template/base_settings { "template": { "settings": { "number_of_shards": 1, "number_of_replicas": 1 } } } PUT _component_template/sales_mappings { "template": { "mappings": { "properties": { "record_id": { "type": "keyword" }, "amount": { "type": "double" }, "@timestamp": { "type": "date" } } } } }3.2 索引模板(匹配索引名,拼装组件)
PUT _index_template/sales_template { "index_patterns": ["sales-*"], // 索引名匹配 sales-* 就套用 "composed_of": ["base_settings", "sales_mappings"], "priority": 200, // 多模板命中时取优先级高的 "template": { "aliases": { "sales": {} } // 顺便挂别名 } }之后任何sales-2026.08之类的新索引,一写入就自动带上上面的结构和别名。
3.3 data stream(时序,只追加)
# 模板里声明这是 data stream 模板 PUT _index_template/logs_template { "index_patterns": ["logs-*"], "data_stream": {}, "composed_of": ["base_settings"], "priority": 200 } # 直接往 data stream 写(必须带 @timestamp) POST logs-app/_doc { "@timestamp": "2026-08-01T10:00:00Z", "level": "INFO", "msg": "started" } # 手动滚动(通常交给 ILM 自动做) POST logs-app/_rolloverdata stream 只支持
create(追加),不能像普通索引那样对历史文档随意update/delete
(要改走_update_by_query,第 32 篇)。
4. Spring Boot 实现
@ComponentpublicclassDoc34Template{@AutowiredprivateElasticsearchClientelasticsearchClient;/** 建/更新索引模板:mapping 外置成 JSON,用 withJson 直灌(对照第 09 篇) */publicvoidputIndexTemplate(Stringname,StringtemplateJson)throwsIOException{PutIndexTemplateRequestreq=PutIndexTemplateRequest.of(b->b.name(name).withJson(newStringReader(templateJson)));// 整段模板 DSL 直灌booleanok=elasticsearchClient.indices().putIndexTemplate(req).acknowledged();if(!ok){thrownewIllegalStateException("put index template 失败: "+name);}}/** 对 data stream 手动触发一次 rollover(一般由 ILM 自动完成) */publicvoidrollover(StringdataStream)throwsIOException{elasticsearchClient.indices().rollover(r->r.alias(dataStream));}}import:
co.elastic.clients.elasticsearch.indices.PutIndexTemplateRequest。
模板 JSON 外置到resources/es-template/*.json,改结构不必改 Java(同第 09 篇思路)。
5. 坑与最佳实践
- 模板只对“之后创建的索引”生效:改了模板不会回改已存在的索引。
priority决多模板冲突:索引名同时命中多个模板时,取priority最高的那个(不叠加)。- data stream 必须有
@timestamp:这是它排序/滚动的依据。 - data stream 配 ILM 才完整:滚动、降副本、迁冷、删除交给 ILM 自动做(见运维篇/官方 ILM)。
- 优先用 component template 拆公共片段:多索引共享 settings/mapping,避免复制粘贴漂移。
- 别用旧的
_template(legacy):8.x 用_index_template+_component_template。