目录

一、概况

二、索引拆分规则

三、创建索引模板

3.1 索引模板的优先级

3.2 索引模板匹配的名称

3.3 索引模板的settings部分

3.3.1 字符过滤器

3.3.2 分词器

3.3.3 标记过滤器

3.3.4 分析器组合

3.4 索引模板的mapping字段映射

3.4.1 动态字段映射

3.4.2 自定义字段映射

3.5 索引别名

3.6 索引模板管理

3.6.1 创建索引模板

3.6.2 查看索引模板详情

3.6.3 判断模板是否存在

3.6.4 删除索引模板

四、同步文档时,自动创建索引

五、别名搜索

六、可能存在的问题点

七、附录demo演示

7.1 查询索引模板列表

7.2 创建索引模板

7.3 查看索引模板详情

7.4 使用索引名称查询数据

7.5 创建文档时自动创建索引

7.6 有无索引模板的索引生成对比

八、参考


一、概况

在生产项目中,由于ElasticSearch单个索引数据量大,索引中部分数据不常用,在搜索和写入文档时,效率较低。为了减小单个索引的数据量,提升搜索和文档写入效率,将大索引根据一定的规则拆分为小的索引。拆分索引的关键点在于:建立索引、文档同步、多索引搜索。

建立索引的关键是:索引的设置,以及字段的属性设置。最常见的问题是:对于某个字段,我们希望ElasticSearch按照我们的想法进行分词。采用自动生成索引(默认模板),索引字段的类型就会根据第一条文档的数据进行字段转换,无法实现指定某个字段使用我们想要的分词方式。另外,无法使用自定义分词器;索引的默认分片数为5,无法根据我们制定的分片数进行分配。

为了实现按照指定条件自动创建索引,Elasticsearch也提供了索引模板API。

索引模板,就是创建索引的模板。其中包含公共的配置(Settings)和映射(Mappings),并包含一个简单触发条件(满足条件时,使用该模板创建一个新的索引)

模板只在创建索引时应用。更改模板不会对现有索引产生影响。当使用create index API时,“作为create index调用的一部分定义的设置/映射”将优先于“模板中定义的任何匹配设置/映射”。

文档同步和搜索,我们都采用了索引别名的形式。索引别名,就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。索引别名不能与索引具有相同的名称。索引别名带给我们极大的灵活性:

其一,对于es集群,可以在运行的集群中无缝地从一个索引切换到另一个索引;

其二,对于应用,可以在零停机的情况下从旧索引迁移到新索引;

其三,可以给多个索引分组 ,给索引的一个子集分组。

文档同步,必须指定一个唯一的索引。在原来单索引时,我们的索引名称采取了 “索引名称_v1”的形式;为方便在零停机的情况下重建索引,文档更新也新建了一个专门的索引别名。拆分索引后,索引名称规范为“索引名称_YYMM”按月拆分(包括但不限于此种方式),就会出现多个索引;此时,使用索引名字直接进行文档更新,就会更加方便、直接和准确,不需要新增专门的索引别名用于文档更新。

文档同步使用索引名称,文档搜索使用索引别名。索引拆分后,文档分属不同的索引,但因为有相同的别名,使用别名搜索,依然可以将数据搜索出来。

通过建立索引、文档同步、多索引搜索,实现了单索引到多索引的拆分。数据还是那些数据,依然能搜索出来。在索引拆分后,索引数变多,每个索引的数据减少,同步文档的速度就可以提高。根据业务需求,搜索也可以只查询部分索引,提升了查询速度;也可以查询所有数据,根据实际场景可自由变换。

二、索引拆分规则

索引拆分,可以根据创建时间拆分,如:”索引名称_yyyyMM“、”索引名称_yyyy“;也可以根据主键ID求余的方式进行拆分,如:”索引名称_0“、”索引名称_1“。

具体的拆分规则,根据业务需要来定。需要注意的是:无论是根据创建时间,还是根据主键ID求余,都要求“拆分规则使用的值必须是文档中不变的值”。只有拆分规则使用的值是不变的,才能唯一确定一个索引,进行文档的存储,如:主键ID、创建时间。变化的值,或有可能变化的值,无法唯一确定一个索引进行文档存储,如:状态。如果拆分规则使用状态进行拆分,那么就会出现“文档当前在这个索引,状态改变后在另外的索引”,每个索引都包含“状态不同”的同一条数据,搜索结果就会不准确。

本文将根据创建时间进行索引拆分。

索引拆分思路:

  • 创建索引模板。
  • 同步文档时,选用的索引名称以"索引名称_yyyyMM"命名,自动创建带别名的索引。
  • 如果文档同步到新索引,那么原索引中的文档需删除。

三、创建索引模板

以商品评论索引为例,将单索引拆分为多索引。根据以下规则,在同步文档时,如果索引不存在,那么会根据模板自动生成索引

  • 索引名称的规则为 goods_comment_202010。
  • 索引别名为 goods_comment。
  • number_of_shards 分片数为3。
  • 配置Settings。
  • 定义Mappings字段及其类型。

具体模板如下所示:

{
    "order" : 0,
    "index_patterns" : [
      "goods_comment*"
    ],
    "settings" : {
      "index" : {
        "max_result_window" : "100000",
        "analysis" : {
          "filter" : {
            "by_stop_filter" : {
              "type" : "stop",
              "stopwords" : [
                " "
              ]
            },
            "pinyin_first_letter_and_full_pinyin_filter" : {
              "keep_none_chinese_in_first_letter" : "true",
              "lowercase" : "true",
              "keep_original" : "true",
              "keep_first_letter" : "true",
              "trim_whitespace" : "true",
              "type" : "pinyin",
              "keep_none_chinese" : "true",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            },
            "by_synonym_filter" : {
              "type" : "synonym",
              "synonyms_path" : "analysis/synonym.txt"
            },
            "full_pinyin_filter" : {
              "keep_none_chinese_in_first_letter" : "true",
              "lowercase" : "true",
              "keep_original" : "true",
              "keep_first_letter" : "false",
              "trim_whitespace" : "true",
              "type" : "pinyin",
              "keep_none_chinese" : "true",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            }
          },
          "char_filter" : {
            "by_char_filter" : {
              "type" : "mapping",
              "mappings" : [
                "| => |"
              ]
            }
          },
          "analyzer" : {
            "by_max_word" : {
              "filter" : [
                "by_synonym_filter",
                "lowercase"
              ],
              "char_filter" : [
                "html_strip"
              ],
              "type" : "custom",
              "tokenizer" : "ik_max_word"
            }
          },
          "tokenizer" : {
            "my_pinyin" : {
              "lowercase" : "true",
              "keep_original" : "true",
              "remove_duplicated_term" : "true",
              "keep_separate_first_letter" : "false",
              "type" : "pinyin",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            }
          }
        },
        "number_of_shards" : "3",
        "number_of_replicas" : "1"
      }
    },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "is_img" : {
            "type" : "integer"
          },
          "gid" : {
            "type" : "integer"
          },
          "pubtime" : {
            "type" : "integer"
          }
            ....
        }
      }
    },
    "aliases" : {
      "goods_comment" : { }
    }
  }

上述模板定义,看似复杂,拆分来看,主要为如下几个部分:

{
  "order": 0,                               // 模板优先级
  "index_patterns": ["goods_comment*"],     // 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {...}                          // 索引的别名
}

3.1 索引模板的优先级

有时候,一个模板可能绝大部分符合新建索引的需求,但是局部需要微调。此时,复制旧的模板,生成一个新的索引模板,修改该新模板,即可达到我们的需求,但是这操作略显重复。此时,可以采用模板叠加与覆盖来操作模板的优先级是通过模板中的 order 字段定义的,数字越大,优先级越高 如下为定义“所有以 te 开头的索引”的模板:

{
    "order": 0,
    "index_patterns": "te*",
    "settings": {
        "number_of_shards": 1
    },
    "mappings": {
        "type1": {
            "_source": {
                "enabled": false
            }
        }
    }
}

索引模板是有序合并的。如果想单独修改某一小类索引的一两处设置,可以在累加一层模板。

{
    "order": 1,
    "index_patterns": "tete*",
    "settings": {
        "number_of_shards": 2
    },
    "mappings": {
        "type1": {
            "_all": {
                "enabled": false
            }
        }
    }
}

上述第一个模板的 order 为0,第二个模板的 order 为1,第二个模板的优先级高于第一个模板,其会覆盖第一个模板中的相同项。所以,对于所有以 tete 开头的索引模板,效果如下:

{
    "settings": {
        "number_of_shards": 2
    },
    "mappings": {
        "type1": {
            "_source": {
                "enabled": false
            },
            "_all": {
                "enabled": false
            }
        }
    }
}

两个模板叠加后,优先级高的模板设置会覆盖会优先级低的,如分片数。

3.2 索引模板匹配的名称

索引模板中的 "index_patterns" 字段定义的内容该索引模板所匹配的索引名称的正则表达式。如 "index_patterns": "tete*" 所表示的含义为:当新建索引时,所有以 tete 开头的索引,都会自动匹配到该索引模板,利用该模板进行相应的设置。

3.3 索引模板的settings部分

索引模板中的 settings 部分一般定义的内容是:索引的主分片、拷贝分片、刷新时间、自定义分析器等。常见的 settings 部分结构如下:


"settings": {
    "index": {
      "analysis": {...},                // 自定义的分析器
      "number_of_shards": "32",         // 主分片的个数
      "number_of_replicas": "1",        // 主分片的拷贝分片个数
      "refresh_interval": "5s"          // 刷新时间
    }
  }

建立的索引,不会立马查到。ElasticSearch 为 near-real-time(接近实时)检索,需要配置刷新时间,默认的是 1s。settings 的设置中,重点是自定义分析器的设置。

  • 分析器是三个顺序执行的组件的结合。它们分别是字符过滤器、分词器、标记过滤器

  • 字符过滤器:让字符串在被分词前变得更加整洁。一个分析器可能包含零到多个字符过滤器(character_filter)。

  • 分词器:将字符串分割成单独的词(terms)或标记(tokens)。一个分析器必须包含一个分词器。

  • 标记过滤器分词器分词结果的标记流会传递给特定的标记过滤器标记过滤器可能修改、添加或删除标记。

创建的自定义分析器的结构如下:

"settings": {
    "index": {
      "analysis": {
            "char_filter": { ... },             // 用户自定义字符过滤器
            "tokenizer":   { ... },             // 用户自定义分词器
            "filter":      { ... },             // 用户自定义标记过滤器
            "analyzer":    { ... }              // 用户自定义分析器
      },
      ...
    }
  }

3.3.1 字符过滤器

目前,字符过滤器共分为三种:映射字符过滤器(mapping char filter)、HTML过滤器(HTML Strip char filter)、格式替换过滤器(Pattern Replace char filter)。

html_strip 字符过滤器去除所有的 HTML 标签。

mapping 字符过滤器对字符进行映射转换。在映射字符过滤器中,type 字段定义该字符过滤器的类型是 mapping , mappings 对应的数组为索要替换的字段。如下定义一个 mapping 字符过滤器,将 & 替换成 and。

"char_filter": {
    "&_to_and": {
        "type":       "mapping",
        "mappings": [ "&=> and "]
    }
}

格式替换过滤器对满足正则匹配的字符进行替换。格式替换过滤器必须包含 "pattern"、"type"、"replacement" 三个字段,其中 pattern 定义满足替换的格式,type 定义格式替换类型,replacement 则是对于满足格式的字符串,要替换成的字符串。如下在定义一个格式替换过滤器(Pattern Replace Char Filter),将点 "." 替换成空格。


"char_filter": {
    "replace_dot": {
        "pattern": "\\.",
        "type": "pattern_replace",
        "replacement": " "
    }
}

3.3.2 分词器

常用的分词器有 standard、keyword、whitespace、pattern等。

standard 分词器将字符串分割成单独的字词,删除大部分标点符号。最常用的一般为 standard 分词器。

keyword 分词器输出和它接收到的相同的字符串,不做任何分词处理。

whitespace 分词器只通过空格俩分割文本。

pattern 分词器可以通过正则表达式来分割文本。

更多的分词器详见官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/2.4/analysis-standard-tokenizer.html

3.3.3 标记过滤器

常用的标记过滤器有 lowercase 和 stop 。

lowercase 标记过滤器:将词转换为小写。

stop 标记过滤器:去除一些自定义停用词或者语言内定义的停用词。

stop 标记过滤器的常用结构如下:

"filter": {
    "my_stopwords": {
        "type":        "stop",
        "stopwords": [ "the", "a" ]
    }
}

上述标记过滤器功能为自定义过滤掉 "the" 、"a" 两词。除了自定义,还有特定的语言停用词过滤,相应的有:spanishenglish等。具体用法如下:

"filter": {
    "my_stop": {
        "type": "stop",
        "stopwords": _spanish_
    }
}

更多的语言停用词,参考官方网址:https://www.elastic.co/guide/en/elasticsearch/reference/2.4/analysis-stop-tokenfilter.html/

3.3.4 分析器组合

在定义了字符过滤器、分词器、标记过滤器之后,就可以组合为用户自定义分析器。分析器是由三者按顺序组合而成。

将上述组件组合起来便是:


"analyzer": {
    "my_analyzer": {
        "type":           "custom",
        "char_filter":  [ "html_strip", "&_to_and", "replace_dot" ],
        "tokenizer":      "standard",
        "filter":       [ "lowercase", "my_stopwords", "my_stop" ]
    }
}

将自定义分析器各部分组合起来,完整表示如下:

"settings": {
    "index": {
      "analysis": {
           "char_filter": {
                "&_to_and": {
                    "type":       "mapping",
                    "mappings": [ "&=> and "]
                },
                "replace_dot": {
                    "pattern": "\\.",
                    "type": "pattern_replace",
                    "replacement": " "
                }
            },
            "filter":      {
                "my_stop": {
                    "type":        "stop",
                    "stopwords": _spanish_
                },
                "my_stopwords": {
                    "type":        "stop",
                    "stopwords": [ "the", "a" ]
                }
            },
            "analyzer":    {
                "my_analyzer": {
                    "type":           "custom",
                    "char_filter":  [ "html_strip", "&_to_and", "replace_dot" ],
                    "tokenizer":      "standard",
                    "filter":       [ "lowercase", "my_stopwords", "my_stop" ]
                }
            }
      },
      ...
    }
  }

3.4 索引模板的mapping字段映射

索引模板中,字段映射所对应的常用结构是:

"mappings": {
    "_doc": {                               // 索引下的类型 _doc 应用该映射
      "dynamic_templates": [ ... ],         // 动态映射部分,用于未定义的 my_type 下字段
      "properties": { ... }                 // 自定义字段的响应映射
    }
}

"_doc" 是索引下的一个类型,Elasticsearch 7.x仅支持"_doc"作为索引类型,Elasticsearch 6.x推荐使用"_doc"为索引类型。

3.4.1 动态字段映射

动态映射 "dynamic_templates" 一个数组,数组中的元素是字段映射模板。每个字段映射模板包括:

(1)一个名字,用于描述这个模板的用途;

(2)一个 mapping 字段,用于指明这个映射如何使用;

(3)至少一个字段名匹配规则(例如 match),用来定义这个模板适用于哪个字段。

dynamic_templates 字段对应的模板结构如下:

{
    "string_fields": {                                  // 字段映射模板的名称,一般为"类型_fields"的命名方式
        "match": "*",                                   // 匹配的字段名为所有
        "match_mapping_type": "string",                 // 限制匹配的字段类型,只能是 string 类型
        "mapping": { ... }                              // 字段的处理方式
 }

如下为一个定义实例:


"mappings": {
    "my_type": {
      "dynamic_templates": [
         {
            "string_fields": {                                  // 字段映射模板的名称,一般为"类型_fields"的命名方式
                "match": "*",                                   // 匹配的字段名为所有
                "match_mapping_type": "string",                 // 限制匹配的字段类型,只能是 string 类型
                "mapping": {
                    "fielddata": { "format": "disabled" },      // fielddata 不可用,对于分析字段,其默认值是可用
                    "analyzer": "only_words_analyzer",          // 字段采用的分析器名,默认值为 standard 分析器
                    "index": "analyzed",                        // 索引方式定义为索引,默认值是分析
                    "omit_norms": true,                         // omit_norms 为真表示考虑字段的加权,可分析字段默认值 false
                    "type": "string",                           // 字段类型限定为 string
                    "fields": {                                 // 定义一个嵌套字段,将该字段应用于不分析的场景
                        "raw": {
                            "ignore_above": 256,                // 忽略字段对应的值长度大于256的字段
                            "index": "not_analyzed",            // 索引方式为不分析
                            "type": "string",                   // 字段的类型为 string
                            "doc_values": true                  // 对于不分析字段,doc_values 对应的是一种列式存储结构,默认false
                        }
                    }
                }
            }
        },
        ...
      ],
      "properties": { ... }
    }
}

可以看到,动态字段模板的 mapping 字段下包含 fielddata、analyzer、index、omit_norms、type、fields 等六个字段。下面分别解释这六个字段。

一般情况下,使用 es 进行检索,查询速度非常快,接近实时,这个效果得益于其倒排索引的数据结构。但是,倒排索引这个数据结构在做聚合分析时,却不是那么容易。因此,在做传统分析时,需要一种常见的行式存储结构,这一结构在排序、聚合和分析时,具有明显的优势。

fielddata:分析字段是否需要排序或聚合分析。对于分析字段,除了倒排索引的数据存储方式,仍定义一种形式上看着是行式结构的存储样式,数据存储在内存中,用于对字段的排序和聚合分析。对于分析字段, fielddata 的默认值是可用的。如果确定一个分析字段不需要排序或者聚合分析,则该字段就设置为不可用,这样可以节省内存。

analyzer:该字段的分析器,默认的分析器是 standard 标准分析器,这个地方可定义为自定义的分析器。

index:字段的索引方式。默认情况下 ,Elasticsearch 对字段采取的是分析索引,即会对字段进行倒排索引。

omit_norms:字段在分析过程中,是否考虑加权。例如如果权重为2,字段出现的频率在计算时会翻倍,以提高计算的匹配度。对于分析字段,默认的是不考虑加权。

type:字段的数据类型。

fields:字段对应的嵌入字段。以 title 字段为例,Elasticsearch 会自动生成两个字段,分别是 title 和 title.raw 。这样,在可能同时需要分词和不分词结果的环境,就可以灵活使用不同的索引字段。比如,查看标题中最常用的单词,应该是使用 title 字段,查看阅读数最多的文章标题,应该是使用 title.raw 字段。

ignore_above 字段是索引时忽略长度超过定义值的字段。

doc_values 字段与 fielddata 相对应,都是为了方便字段排序和聚合分析而重新定义的一种数据存储方式。不同的是,fielddata 应用于分析字段,存储在内存中;doc_values 应用于不分析字段,存储于磁盘中。

上述是针对字段数据类型为 string 的动态模板定义,对于其他数据类型,其动态模板的定义方式一般如下:


{
    "float_fields": {                               // 命名方式 "类型_fields"
        "match": "*",
        "match_mapping_type": "float",
        "mapping": {
            "type": "float",
            "doc_values": true                      // doc_values 定义为 true,便于排序与聚合分析
        }
    }
}

3.4.2 自定义字段映射

索引类型中存在的字段,除了可以采用动态模板的方式,还可以采用自定义的方式,常见的自定义结构如下:

"mappings": {
    "my_type": {
      "dynamic_templates": [ ... ],
      "properties": {
          "user_city": {                                // 字段名
             "analyzer": "lowercase_analyzer",          // 字段分析器
             "index": "analyzed",                       // 字段索引方式定义索引
             "type": "string",                          // 字段数据类型定义为 string
             "fields": {                                // 定义一个名为 user_city.raw 的嵌入的不分析字段
                "raw": {
                    "ignore_above": 512,
                    "index": "not_analyzed",
                    "type": "string"
                }
            }
         },
         "money":{
            "type": "double",
            "doc_values": true
         }
         ...
      }
    }
}

3.5 索引别名

即使你认为现在的索引设计已经是完美的了,当你的应用在生产环境使用时,还是有可能在今后有一些改变的。所以请做好准备:在应用中使用别名,而不是索引。然后,你就可以在任何时候重建索引。别名的开销很小,应当广泛使用。利用索引别名,可以实现零停机时间重新索引。 定义方式如下:

{
  "order": 0,                               // 模板优先级
  "index_patterns": "goods_comment*",       // 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {                              // 索引别名
     "goods_comment":{}
  }
}

零停机时间实现重新索引方式:

POST /_aliases
{
    "actions": [
        { "remove": { "index": "my_index_v1", "alias": "my_index" }},
        { "add":    { "index": "my_index_v2", "alias": "my_index" }}
    ]
}

以上只是简单介绍了索引模板及其组成部分,详情请见Elasticsearch官方文档。Elastic Stack and Product Documentation | Elastic

有了以上的知识,我们就可以利用索引模板的API,对索引模板进行创建、查询、删除操作。

3.6 索引模板管理

3.6.1 创建索引模板

PUT _template/goods_comment_template
{
  "order": 0,                               // 模板优先级
  "index_patterns": "goods_comment*",       // 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {                              // 索引别名
     "goods_comment":{}
  }
}

3.6.2 查看索引模板详情

GET _template                // 查看所有模板
GET _template/temp*          // 查看与通配符相匹配的模板
GET _template/temp1,temp2    // 查看多个模板
GET _template/shop_template  // 查看指定模板

3.6.3 判断模板是否存在

HEAD _template/shop_tem

结果: a) 如果存在, 响应结果是: 200 - OK b) 如果不存在, 响应结果是: 404 - Not Found

3.6.4 删除索引模板

DELETE _template/shop_template    // 删除上述创建的模板

如果模板不存在, 将抛出404 错误。

四、同步文档时,自动创建索引

我们在前面已创建了商品评论的索引模板(goods_comment_template)。同步文档时,指定索引名称为“goods_comment_202010”。如果该索引不存在,那么ES会自动创建名为“goods_comment_202010”的索引,同时创建好“goods_comment”别名。索引的settings和mappings都会根据模板定义的规则生成。索引创建成功后,便能正常使用。

在商品评论业务中,同步文档是在代码中实现,需要根据商品评论的创建时间,以“goods_comment_yyyyMM”的形式获取完整的索引名称(如:goods_comment_202010)。同步文档时,指定索引名称为goods_comment_202010,即可将数据同步到该索引。

五、别名搜索

对于多个商品评论索引,每个索引都有“goods_comment“别名。使用别名进行搜索,便能从这多个索引中获取数据。

同理,其他业务索引实现搜索,都要求使用别名形式。

六、可能存在的问题点

索引在创建后,并不是一成不变的。随着业务的发展,新增字段也是较常见的。原来单索引时,若新增一个字段,只需要在mappings中新增字段、重建索引、迁移数据、切换别名即可若拆分后的多个索引新增字段,工作量便会成倍增加。

修改索引模板,只会对后续生成的索引有作用之前生成的索引如需调整,需要手动或者使用脚本的形式,进行索引重建迁移数据

七、附录demo演示

demo演示,体验索引拆分的一个实现过程。

7.1 查询索引模板列表

查看ES中的所有索引模板列表,命令为:

GET _cat/templates?v

结果为:

name                                  index_patterns             order       version
kibana_index_template:.kibana         [.kibana]                  0           
.monitoring-kibana                    [.monitoring-kibana-6-*]   0           6050399
.management-beats                     [.management-beats]        0           67000

7.2 创建索引模板

命令为:

PUT _template/demo_template
{
  "order": 0,
  "index_patterns": [
    "demo*"
  ],
  "settings": {
    "index": {
      "number_of_shards": 2,
      "number_of_replicas": 0,
      "max_result_window": 100000
    }
  },
  "aliases": {
    "demo": {}
  }
}

结果为:

{
  "acknowledged" : true
}

7.3 查看索引模板详情

命令为:

GET _template/demo_template

结果为:

{
  "demo_template" : {
    "order" : 0,
    "index_patterns" : [
      "demo*"
    ],
    "settings" : {
      "index" : {
        "max_result_window" : "100000",
        "number_of_shards" : "2",
        "number_of_replicas" : "0"
      }
    },
    "mappings" : { },
    "aliases" : {
      "demo" : { }
    }
  }
}

7.4 使用索引名称查询数据

命令为:

GET demo_v1/_search

结果为:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index",
        "resource.type" : "index_or_alias",
        "resource.id" : "demo_v1",
        "index_uuid" : "_na_",
        "index" : "demo_v1"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index",
    "resource.type" : "index_or_alias",
    "resource.id" : "demo_v1",
    "index_uuid" : "_na_",
    "index" : "demo_v1"
  },
  "status" : 404
}

可以看到,demo_v1索引不存在。

7.5 创建文档时自动创建索引

在此之前,demo_v1索引不存在。通过创建文档,ES会根据demo_template索引模板,自动生成demo_v1索引。

命令为:

POST demo_v1/_doc
{
  "id": 1,
  "title": "这是一条数据"
}

结果为:

{
  "_index" : "demo_v1",
  "_type" : "_doc",
  "_id" : "20upIHUBO6Fj2CIJUFPr",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

查看数据,命令为:

GET demo_v1/_search 用索引名称进行查询
GET demo/_search 用索引别名进行查询

结果为:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "demo_v1",
        "_type" : "_doc",
        "_id" : "20upIHUBO6Fj2CIJUFPr",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "title" : "这是一条数据"
        }
      }
    ]
  }
}

使用索引名称和索引别名,都能按照预期搜索出数据。但是,我们并未单独创建索引别名。我们来查看一下demo_v1索引的结构,命令为:

GET demo_v1

结果为:

{
  "demo_v1" : {
    "aliases" : {
      "demo" : { }
    },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "id" : {
            "type" : "long"
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "number_of_shards" : "2",
        "provided_name" : "demo_v1",
        "max_result_window" : "100000",
        "creation_date" : "1602570768526",
        "number_of_replicas" : "0",
        "uuid" : "WrXtDB5eRzmU-xX1vAUCrA",
        "version" : {
          "created" : "6070099"
        }
      }
    }
  }
}

我们可以看到,demo_v1 索引中配置项的值为:

  • 分片数(number_of_shards): 2

  • 副本(number_of_replicas): 0

  • 别名(aliases):demo

  • 最大结果窗口(max_result_window):100000

这些都是我们在demo_template模板中设置的。在自动创建索引时,根据索引模板的index_patterns值,只要我们的索引名称是以“demo”为前缀,都会根据该模板生成索引。因此,无论是demo_v1,还是demo_v2,只要索引名称是以“demo”为前缀,在创建文档时,如果不存在索引,ES会自动创建以“demo_template”为模板的索引。

7.6 有无索引模板的索引生成对比

实现索引拆分的最关键点,就在于索引模板。

下面,我们通过创建文档,来生成一个没有索引模板的索引进行对比。

(1)查询demo,命令为:

GET demo/_search

结果为:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index",
        "resource.type" : "index_or_alias",
        "resource.id" : "demo",
        "index_uuid" : "_na_",
        "index" : "demo"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index",
    "resource.type" : "index_or_alias",
    "resource.id" : "demo",
    "index_uuid" : "_na_",
    "index" : "demo"
  },
  "status" : 404
}

观察结果,可以确定demo索引不存在。

(2)创建一条文档,命令为:

POST demo/_doc
{
  "id": 1,
  "title": "这是一条数据"
}

结果为:

{
  "_index" : "demo",
  "_type" : "_doc",
  "_id" : "PmXEIHUBwM4PCvJbG7Xw",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

观察结果,确认demo索引创建成功。

(3)查看数据,命令为:

GET demo/_search

结果为:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "demo",
        "_type" : "_doc",
        "_id" : "PmXEIHUBwM4PCvJbG7Xw",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "title" : "这是一条数据"
        }
      }
    ]
  }
}

数据同步成功,索引也创建完成。我们来看看这个索引结构,命令为:

GET demo

结果为:

{
  "demo" : {
    "aliases" : { },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "id" : {
            "type" : "long"
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "number_of_shards" : "2",
        "provided_name" : "demo",
        "creation_date" : "1602572524390",
        "number_of_replicas" : "1",
        "uuid" : "p8kNddGzQzWOaz5xLcSWhA",
        "version" : {
          "created" : "6070099"
        }
      }
    }
  }
}

可以看到,demo索引中配置项的值为:

  • 分片数(number_of_shards): 2

  • 副本(number_of_replicas): 1

  • 别名(aliases):无

  • 最大结果窗口(max_result_window):无

为了直观比较,请看下表:

有索引模板(demo_v1)

无索引模板(demo)

number_of_shards

2

2

number_of_replicas

0

1

aliases

demo

max_result_window

10w

无,默认是1w

通过上表可知,通过索引模板自动创建索引,有利于我们更好地掌控索引的结构。

通过demo演示,我们可以进一步理解索引拆分的过程及其实现原理,重点为索引模板

八、参考

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐