初探 Elasticsearch Index Template(索引模板)

索引模板,简而言之,是一种复用机制,就像一些项目的开发框架如 Laravel 一样,省去了大量的重复,体力劳动。当新建一个 Elasticsearch 索引时,自动匹配模板,完成索引的基础部分搭建。

典型的如下所示:


{
  "order": 0,
  "template": "sample_info*",
  "settings": {
    "index": {
      "number_of_shards": "64",
      "number_of_replicas": "1"
    }
  },
  "mappings": {
    "info": {
      "dynamic_templates": [
        {
          "string_fields": {
            "mapping": {
              "analyzer": "only_words_analyzer",
              "index": "analyzed",
              "type": "string",
              "fields": {
                "raw": {
                  "ignore_above": 512,
                  "index": "not_analyzed",
                  "type": "string"
                }
              }
            },
            "match_mapping_type": "string",
            "match": "*"
          }
        }
      ],
    "properties": {
        "user_province": {
          "analyzer": "lowercase_analyzer",
          "index": "analyzed",
          "type": "string",
          "fields": {
            "raw": {
              "ignore_above": 512,
              "index": "not_analyzed",
              "type": "string"
            }
          }
        }
      }
    }
  },
  "aliases": {}
}

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


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

1、模板优先级

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

如下为定义所有以 te 开头的索引的模板:


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

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


{
    "order" : 1,
    "template" : "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 }
        }
    }
}

两个模板叠加了,项目的字段,优先级高的覆盖了优先级低的,如分片数。

2、索引模板的匹配

索引模板中的 "template" 字段定义的是该索引模板所应用的索引情况。如 "template": "tete*" 所表示的含义是,当新建索引时,所有以 tete 开头的索引都会自动匹配到该索引模板。利用该模板进行相应的设置和字段添加等。

3、setting 部分

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


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

建立的索引,不会立马查到,这是为什么 Elasticsearch 为 near-real-time(接近实时)的原因,需要配置刷新时间,默认的是 1s。setting 的设置中,重点是自定义分析器的设置。
分析器是三个顺序执行的组件的结合。他们分别是字符过滤器、分词器、标记过滤器。

  • 字符过滤器是让字符串在被分词前变得更加整洁。一个分析器可能包含零到多个字符过滤器(character_filter)。
  • 分词器将字符串分割成单独的词(terms)或标记(tokens)。一个分析器必须包含一个分词器。
  • 分词器分词的结果的标记流会根据各自的情况,传递给特定的标记过滤器。标记过滤器可能修改、添加或删除标记。

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


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

1)字符过滤器

目前字符过滤器共分为三种:映射字符过滤器(mapping char filter)、HTML过滤器(HTML Strip char filter)、格式替换过滤器(Pattern Replace char filter)。html_strip 字符过滤器去除所有的 HTML 标签。

如下定义一个 mapping 字符过去器。将 & 替换成 and。


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

mapping 字符过滤器的格式如上,其中 type 字段定义该字符过滤器的类型是 mapping 。其中 mappings 对应的数组为索要替换的字段。

如下在定义一个格式替换过滤器(Pattern Replace Char Filter),将点 "." 替换成空格。


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

格式替换过滤器的格式如上、必须包含 "pattern"、"type"、"replacement" 三个字段。其中 pattern 定义的是满足替换的格式,type 则定义为格式替换类型。replacement 则是对于满足格式的字符串,要替换成的字符串。

2)分词器

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

standard 分词器将字符串分割成单独的字词,删除大部分标点符号。keyword 分词器输出和它接收到的相同的字符串,不做任何分词处理。whitespace 分词器只通过空格俩分割文本。pattern 分词器可以通过正则表达式来分割文本。最常用的一般为 standard 分词器。

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

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/

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" ]
                }
            }
      },
      ...
    }
  }

4、索引类型的字段映射

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


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

"my_type" 是索引下的一个类型,如果采用上述定义的索引模板,则索引下仅仅 my_type 类型应用了该索引模板。如果想要索引下的所有模板应用定义的映射,则可以将 "my_type" 替换成 "default" 字段。

1)动态映射

动态映射 "dynamic_templates" 字段对应的是一个数组,数组中的元素是一个个字段的映射模板。每个字段的映射模板都有一个名字用户描述这个模板的用途,一个 mapping 字段由于指明这个映射如何使用,和至少一个参数(例如 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 等六个字段,下面分别解释这六个字段。

对于基本的 Elasticsearch 做个了解,一般的 es 用户搜索字段,其反应数据非常快,接近实时,这个效果得益于其倒排索引的数据结构,然而倒排索引这个数据结构在做聚合分析时,却不是那么容易。因而在做传统分析时,需要一种我们常见的行式存储结构,这一结构在排序和巨额和分析时,具有明显的优势。这一部分详见 Elasticsearch 搜索与聚合在数据存储结构方面的理解

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,便于排序与聚合分析
        }
    }
}

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
         }
         ...
      }
    }
}

复杂一些的是 string 类型的字段,对于其他类型的字段,定义方式类似于上述的 money 字段定义方式。

5、别名

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

定义方式如下:


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

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


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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 获取索引的mapping 实例: 测试分析器 创建一个索引 到目前为止, 我们已经通过索引一篇文档创建了一个新的索...
    M醉逍遥阅读 1,047评论 0 1
  • Solr&ElasticSearch原理及应用 一、综述 搜索 http://baike.baidu.com/it...
    楼外楼V阅读 7,247评论 1 17
  • 方正将手中的烟头扔到地上狠狠地踩了两脚说道 :“我方正对天发誓,抓到那个畜生以后,我要不亲手枪崩了他,我就不叫方正...
    长白居士阅读 193评论 0 0
  • 看到你朋友圈里转发的这篇文章。心里有种,不知道啥样的感觉。或许,你心中有那个他让你有这种感触,又或许,我猜错了吧。...
    Mr丨Zoul阅读 676评论 0 0