作者:来自 Elastic Musab Dogan
概述
Elasticsearch 提供两种类型的索引模板:旧(legacy)索引模板和可组合 (composable) 索引模板。 Elasticsearch 7.8 中引入的可组合模板旨在替换旧模板,两者仍然可以在 Elasticsearch 8 中使用。
本文探讨了这些模板之间的差异以及它们的交互方式。 特别是,我们将重点关注如何在创建索引时检测将使用哪个模板。 让我们首先了解如何创建不同类型的索引模板。
Elasticsearch 中的索引模板
可以使用以下 API 创建旧模板:
PUT _template/t1
{
"order": 1,
"index_patterns": [...],
"mappings": {...},
"settings": {...},
"alias": {...}
}
可以使用此 API 创建可组合模板:
PUT _index_template/ct1
{
"priority": 1,
"index_patterns": [...],
"template": {
"mappings": {...},
"settings": {...},
"alias": {...}
}
}
组件模板是第三种类型,通常用于管理具有相似结构的多个模板。 例如,如果你需要创建数百个具有相似结构的模板,则可以创建具有通用设置、映射和别名的组件模板,然后将其包含在索引模板中。 可以使用此 API 创建组件模板:
PUT _component_template/template_1
{
"template": {
"mappings": {...},
"settings": {...},
"alias": {...}
}
}
确定创建索引时将使用哪个模板
要确定索引在创建时将使用哪个模板,你可以使用 _simulate_index API。 此 API 将返回将使用的模板以及任何重叠的模板。 但是,如果不存在可组合模板,则 API 将返回空正文。 在这种情况下,你可以创建一个虚拟索引并检查所选主节点的日志以确定将使用哪个模板。
如果你同时拥有旧模板和可组合模板,会发生什么情况?
如上所述,如果你同时拥有旧模板和可组合模板,则旧模板将被忽略,就好像它不存在一样。
PUT _template/t1
{
"index_patterns": [
"test_index-*"
],
"mappings": {
"properties": {
"field_1": {
"type": "integer"
},
"field_2": {
"type": "integer"
}
}
}
}
在这种情况下,当你运行命令时,你会收到如下警告消息:
PUT _index_template/ct1
{
"index_patterns": [
"test_index-*"
],
"template": {
"mappings": {
"properties": {
"field_1": {
"type": "integer"
}
}
},
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
}
}
}
如果新创建的可组合模板与现有旧模板匹配或包含索引模式,你将收到如下警告消息:
POST _index_template/_simulate_index/test_index-1
#response:
{
"template": {
"settings": {
"index": {
"number_of_shards": "1",
"number_of_replicas": "0",
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
}
}
},
"mappings": {
"properties": {
"field_1": {
"type": "integer"
}
}
},
"aliases": {}
},
"overlapping": [
{
"name": "t1",
"index_patterns": [
"test_index-*"
]
}
]
}
如果你想测试它,请使用此命令:
PUT test_index-1
GET test_index-1
更多阅读 “Elasticsearch:Simulate index API”。
现实生活场景的笔记
冲突可能很烦人,并且可能导致应用程序崩溃。 想象一下,你有 logstash-dev-*、logstash-prd-*、logstash-stg-* 旧模板都可以正常工作。 如果有人添加一个包含索引模式(如 logstash-*)的单个可组合模板,所有旧模板都将被忽略,字段类型可能会被更改,最终可能会破坏应用程序。 因此,如果你使用 Elasticsearch 7 及更高版本,建议从旧版模板切换到可组合模板。
另一个需要记住的好点是,如果你在 Elasticsearch 8 或更高版本中运行 Logstash,Logstash 默认情况下会将其模板添加为可组合模板。 因为对于 Elasticsearch 8 及更高版本,manage_template 默认设置为 true,并且 Logstash template_api 设置为可组合。 如果可组合模板不存在,它将创建一个具有 logstash-* 索引模式的 Logstash 可组合模板。 是的,它将忽略所有覆盖 logstash-* 的旧模板并重叠它们。
模板重叠
1. 如果你有两个指向相同索引模式的可组合模板,会发生什么情况?
如前所述,如果你有两个指向相同索引模式的可组合模板,则具有最高优先级的可组合模板将优先。
PUT _index_template/ct1
{
"priority": 0,
"index_patterns": [
"test_index-*"
],
"template": {
"mappings": {
"properties": {
"field_1": {
"type": "integer"
}
}
},
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
}
}
}
PUT _index_template/ct2
{
"priority": 1,
"index_patterns": [
"test_index-*"
],
"template": {
"mappings": {
"properties": {
"field_1": {
"type": "keyword"
},
"field_2": {
"type": "integer"
}
}
},
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
}
}
}
POST _index_template/_simulate_index/test_index-1
#response:
{
"template": {
"settings": {
"index": {
"number_of_shards": "2",
"number_of_replicas": "0",
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
}
}
},
"mappings": {
"properties": {
"field_1": {
"type": "keyword"
},
"field_2": {
"type": "integer"
}
}
},
"aliases": {}
},
"overlapping": [
{
"name": "ct1",
"index_patterns": [
"test_index-*"
]
}
]
}
在此示例中,你有两个模板 - ct1 和 ct2 - 都针对相同的索引模式 test_index-。 但是,ct2 的优先级 (1) 高于 ct1 (0)。 因此,当你创建与模式 test_index- 匹配的索引时,ct2 中定义的设置和映射将在 ct1 之前应用。 如果 ct1 和 ct2 模板中有相同的设置,则 ct2 模板将被覆盖。
2. 如果你有两个指向相同索引模式的旧模板,会发生什么情况?
如上所述,如果你有多个指向相同索引模式的模板,则首先合并具有较低阶值的模板。 具有较高阶值的模板稍后会合并,覆盖具有较低值的模板。
如果两个旧模板具有相同的顺序值,它们将按名称排序。 例如,在 [t2, t1] 的情况下,t1 将首先合并,t2 将稍后合并,如果存在任何相同的映射/设置/别名,则 t2 将覆盖 t1。
PUT _template/t1
{
"index_patterns": ["test_index-*"],
"mappings": {
"properties": {
"field_1": {
"type": "integer"
}
}
},
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
}
}
PUT _template/t2
{
"index_patterns": [
"test_index-*"
],
"mappings": {
"properties": {
"field_1": {
"type": "geo_point"
},
"field_2": {
"type": "long"
}
}
},
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
}
}
POST _index_template/_simulate_index/test_index-1
#response
{}
不幸的是,如果你没有可组合模板,则此 API 调用将返回空正文。 那么如何检查呢?
答案是创建一个虚拟索引并检查 Elasticsearch elected-master 日志。
PUT test_index-test
2023-11-14 14:14:27 {"@timestamp":"2023-11-14T11:14:27.535Z", "log.level": "WARN", "data_stream.dataset":"deprecation.elasticsearch","data_stream.namespace":"default","data_stream.type":"logs","elasticsearch.event.category":"templates","event.code":"index_template_multiple_match","message":"index [test_index-1] matches multiple legacy templates [t1, t2], composable templates will only match a single template" , "ecs.version": "1.2.0","service.name":"ES_ECS","event.dataset":"deprecation.elasticsearch","process.thread.name":"elasticsearch[elasticsearch][masterService#updateTask][T#3]","log.logger":"org.elasticsearch.deprecation.cluster.metadata.MetadataCreateIndexService","trace.id":"85e0a432ec11e2f2d3c7883f510376ac","elasticsearch.cluster.uuid":"Jc-a46VUSjOwuxWmbnSDZQ","elasticsearch.node.id":"MTX1x5-OTlWhiGa9lwUJPw","elasticsearch.node.name":"elasticsearch","elasticsearch.cluster.name":"elasticsearch-cluster1"}
2023-11-14 14:14:27 {"@timestamp":"2023-11-14T11:14:27.605Z", "log.level": "INFO", "message":"[test_index-1] creating index, cause [api], templates [t2, t1], shards [2]/[0]", "ecs.version": "1.2.0","service.name":"ES_ECS","event.dataset":"elasticsearch.server","process.thread.name":"elasticsearch[elasticsearch][masterService#updateTask][T#3]","log.logger":"org.elasticsearch.cluster.metadata.MetadataCreateIndexService","trace.id":"85e0a432ec11e2f2d3c7883f510376ac","elasticsearch.cluster.uuid":"Jc-a46VUSjOwuxWmbnSDZQ","elasticsearch.node.id":"MTX1x5-OTlWhiGa9lwUJPw","elasticsearch.node.name":"elasticsearch","elasticsearch.cluster.name":"elasticsearch-cluster1"}
从日志中,我们可以看到 “[test_index-1] creating index, cause [api], templates [t2, t1]”。
GET _cat/templates/t*?v
name index_patterns order version composed_of
t2 [test_index-*] 0
t1 [test_index-*] 0
如你所见,旧版模板 t1 和 t2 具有相同的顺序; 那么,哪一个会覆盖另一个呢?
在这种情况下,Elasticsearch 将根据名称对旧索引模板进行排序并应用它们。 将应用两个模板,并且列表中的第一个模板(本例中为 t2)将覆盖该模板。
奖励:如果你有两个旧模板指向具有相同字段名称但类型不合适的相同索引模式,会发生什么情况?
尝试在旧模板中合并属性,无论顺序如何,都可能会失败,因为字段定义应保持原子性。 这个问题是引入新的可组合模板的主要动机。 请参阅下面的示例。 我们感谢 Philipp Krenn 在本文中添加这些评论。
PUT _template/test1
{
"order": 3,
"index_patterns": [
"test-*"
],
"mappings": {
"properties": {
"my_field": {
"type": "integer",
"ignore_malformed": true
}
}
}
}
PUT _template/test2
{
"order": 2,
"index_patterns": [
"test-*"
],
"mappings": {
"properties": {
"my_field": {
"type": "keyword",
"ignore_above": 1024
}
}
}
}
PUT test-1/_doc/1
{
"my_field": "a string..."
}
#response:
{
"error": {
"root_cause": [
{
"type": "mapper_parsing_exception",
"reason": "unknown parameter [ignore_above] on mapper [my_field] of type [integer]"
}
],
"type": "mapper_parsing_exception",
"reason": "Failed to parse mapping: unknown parameter [ignore_above] on mapper [my_field] of type [integer]",
"caused_by": {
"type": "mapper_parsing_exception",
"reason": "unknown parameter [ignore_above] on mapper [my_field] of type [integer]"
}
},
"status": 400
}
注意和最好需要知道的事情
- 以相同的顺序使用旧模板可能会导致很多混乱。 这就是为什么建议向模板添加 order 的原因。
- 具有较低 order 的模板首先被合并。 具有较高顺序值的模板稍后会被合并,覆盖具有较低 order 的模板。
- 你无法创建两个具有相同优先级的可组合模板。
{
"type": "illegal_argument_exception",
"reason": "index template [ct2] has index patterns [test_index-*] matching patterns from existing templates [ct1] with patterns (ct1 => [test_index-*]) that have the same priority [0], multiple index templates may not match during index creation, please use a different priority"
}
结论
总之,了解 Elasticsearch 索引模板的工作原理对于有效的索引管理至关重要。 通过了解如何确定索引在创建时将使用哪个模板,你可以确保使用正确的设置、映射和别名创建索引。