醉鱼

V1

2022/10/13阅读:27主题:自定义主题1

Elasticsearch创建一个索引怎么也这么复杂

ES(8.1)认证考题大纲参考如下

https://mp.weixin.qq.com/s/x7lMmMAo2563JysMJ8vgRQ

今天是我们学习ES8.1官方搜索工程师的第一课,首先第一个问题就是根据给定的需求创建一个索引,本篇文章将采用如下(总分总)的形式分析该题目,第一部分先进行考题的分析,猜测要考的知识点大概有什么,然后第二部分对该题目涉及的知识点进行分析讲解,每一个考点后都跟着简单的解读,最后模拟汇总该考题,最终实现熟练掌握该题目。后续本类型文章如不特殊说明均使用该种方式进行讲解,如果有好的建议欢迎留在评论区讨论

本文结构如下:

1、题目分析(总)

2、题目拆解知识点(分)

3、总结(模拟出题考试,总)

下面进入第一部分,题目分析阶段

一、题目分析

题目:根据给定的需求创建一个索引

解读:首先我在看到这个题目的第一感觉就是这个题目很简单,不就是创建个索引吗,最多就是设置几个主分片,几个副本分片,稍微再深点就是字段类型、自定义分词器、索引别名,看着好像很简单,其实实际操作起来还是有一定难度的,不过考试过程中能够查阅官网,所以我们只要熟记考点官网位置即可,下面我就该题目涉及到的(索引设置,字段映射类型,文本分析)这三点进行分析

通过阅读本文,你可以获得什么?

1、定义一个索引常用的配置项有哪些

2、自定义字段映射与动态字段映射

3、如何设置字段的数据类型

4、文本分析中 TokenizerToken filterCharacter filters各自发挥什么作用以及如何设置

5、定义一个指定分片数量的索引,包含自定义分词器,自定义字段映射

二、题目拆解

2.1、索引设置

2.1.1、静态索引设置

静态索引设置是只能在索引创建时或者索引关闭时使用的

  • index.number_of_shards

    索引主分片数量设置,默认1,只能在索引创建时设置,索引关闭时不能修改此选项

  • index.number_of_routing_shards

    index.number_of_shards一起使用的整数值,文档路由到主分片的分片数

  • index.codec

    压缩存储数据策略选择,默认LZ4

  • index.routing_partition_size

    路由可以到达的分片数,默认1,只能在索引创建时设置,这个值必须小于index.number_of_shards,除非index.number_of_shards的值也是1

  • index.soft_deletes.enabled

    索引软删除开关,只能在创建索引时进行配置,默认true

  • index.soft_deletes.retention_lease.period

    软删除记录保留最长期限策略,默认值12h

  • index.load_fixed_bitset_filters_eagerly

    嵌套查询预加载缓存过滤器,默认true

  • index.shard.check_on_startup

    分片打开时是否对分片进行检查的开关。默认false,还有true,checksum,除非特别精通各配置项含义,否则不建议更改

2.1.2、动态索引设置

动态索引设置是可以使用update-index-settings API在激活的索引上来动态设置

更改关闭掉的静态或者动态索引设置可能导致不正确的设置,如果不删除或者重建索引,则无法更正这些错误设置

  • index.number_of_replicas

    副本分片数量设置,默认为1

  • index.auto_expand_replicas

    根据集群中数据节点的数量自动展开的副本数量,默认false关闭

  • index.search.idle.after

    分片多久没有请求或搜索会被认为是空闲的分片,默认30s

  • index.refresh_interval

    执行refresh的间隔,默认1s,可以设置-1来禁用

  • index.max_result_window

    es搜索结果返回的最大记录数量(from+size),默认10000

  • index.max_inner_result_window

    es搜索结果中聚合桶返回的最大数量,默认100

  • index.max_rescore_window

    Rescore 请求的返回记录最大值,默认10000

  • index.max_docvalue_fields_search

    查询请求中对docvalue_fields数量的限制,默认100

  • Index.max_script_fields

    查询请求中允许的script_fields的数量最大值,默认32

  • Index.max_ngram_diff

    NGramTokenizerNGramTokenFilter 之间min_grammax_gram之间的最大差异值,默认1

  • index.max_shingle_diff

    shingle token filtermax_shingle_sizemin_shingle_size最大差值,默认3

  • index.max_refresh_listeners

    索引分片上最大的监听器数量,实现了refresh=wait_for的监听器

  • index.analyze.max_token_count

    使用analyze api 获取最大的token数量,默认10000

  • index.highlight.max_analyzed_offset

    高亮显示时设置的最大字符数,默认1000000

  • index.max_terms_count

    Term 查询中term的最大的数量,默认65536

  • index.max_regex_length

    正则查询中表达式的最大长度,默认1000

  • index.query.default_field

    查询语句中查询的字段设置,默认*,查询除了元数据之外的所有字段

  • index.routing.allocation.enable

    控制索引分片分配

    • all 允许所有分片分配(默认)
    • primaries 只允许主分片分配
    • new_primaries 只允许新创建的主分片分配
    • none 不允许分片分配
  • index.routing.rebalance.enable

    控制索引分片重新平衡

    • all 允许所有分片重新平衡(默认)
    • primaries 允许主分片重新平衡
    • replicas 运行副本分片重新平衡
    • none 不允许分片重新平衡
  • index.gc_deletes

    已删除文档的历史版本号可被用于版本化操作的间隔,默认60s

  • index.default_pipeline

    设置索引默认的ingest pipeline

  • index.final_pipeline

    索引最终的final ingest pipeline ,所有的请求都会走,如果该管道不存在,请求将会失败

    不能使用final ingest pipeline 修改_index字段,如果使用final ingest pipeline 修改_index字段,该请求将失败

  • index.hidden

    索引是否隐藏,默认 不返回隐藏的索引,请求时也可以使用参数expand_wildcards 控制,默认false

2.2、映射

映射在我们的使用中有动态映射、动态映射模版、显式设置映射三块。动态映射也就是我们不需要使用显式的设置字段类型,由Elasticsearch来进行推测类型生成映射;动态映射模版就是介于中间的一种,意思是我们提前设置好映射关系,并定一个模版名称、匹配规则,在进行索引插入数据的时候,根据匹配规则找到符合条件的动态模版,根据模版中的显式设置来生成索引;显式设置映射关系就是对索引中的每个字段都固定类型,无需进行类型推测。下面我们就以这三个方面深入的看下字段映射关系如何设置,在看字段映射如何设置之前先来了解一下Elasticsearch中的数据类型都支持哪些?

2.2.1、支持的数据类型

本文数据类型只介绍下工作中经常使用的,个别的数据类型参考《根据给定需求创建索引进阶篇》,后续推出都会有的,全都会有的

本文数据类型只介绍下工作中经常使用的,个别的数据类型参考《根据给定需求创建索引进阶篇》,后续推出都会有的,全都会有的

本文数据类型只介绍下工作中经常使用的,个别的数据类型参考《根据给定需求创建索引进阶篇》,后续推出都会有的,全都会有的

  • 聚合字段类型

    类型设置为aggregate_metric_double作为一个对象,子字段可以有min,max,sum,value_count,当我们对字段设置为aggregate_metric_double字段做聚合时,它能够直接使用子字段的值进行聚合,比如最大值就可以直接去子对象中max的值比较,详情可参考如下文章

    https://mp.weixin.qq.com/s/erLmbgMA9mMrytSAQ9Bt3Q

  • 字段别名类型

    举例一个实用的例子,我们在做es中数据汇总的时候有这么一个统计,统计log.level为info级别的日志,但是因为数据源来自各个渠道,无法都满足ECS(Elastic Common Schema),所以我们可以使用字段别名,比如索引1中对应的统计字段是log.level.keyword,索引2对应的是level.keyword,但是mapping又无法进行修改来完成,所以我们创建一个字段别名log.level对应log.level.keyword,索引二中对应level.keyword,我们统计时只需要对lov.level统计即可了

    我们知道索引可以起一个别名,字段也可以起一个别名,做搜索、聚合、排序的时候直接使用字段别名进行查询、聚合和排序,下面是本人对字段别名的一个深度学习,参考如下:

    https://mp.weixin.qq.com/s/YMm-CedHtPnY6MIvGE1X9g

  • 数组字段

    数组字段没有固定的类型,但是数组中的所有元素类型必须一致,数组里可以包含零个或多个元素,如果为空,则是代表这个数组是没有值的字段

  • binary

    二进制类型,默认该类型字段不可以被搜索,也不存储

  • Boolean

    接收truefalse或者可以解析为truefalse的字符串”true“"false"和空字符串""

  • Date

    时间类型,可以是2022-08-01或者2022-08-01 12:00:00,也可以是一个秒数或者毫秒数,时间格式可以自定义格式,如果不添加使用默认格式 "strict_date_optional_time||epoch_millis",使用时间类型字段排序时会自动转换为时间戳毫秒数进行排序

  • Date nano

    Date类型的补充字段,纳秒存储

  • Geo

    描述地理位置信息的字段数据类型,因为知识点较多,详细信息可参考另一篇文章,通俗解读了geo_pointgeo_shape两种类型的数据,如果查询,聚合,排序等

    https://mp.weixin.qq.com/s/nw2_M8icS3v94w4sfrbqaw

  • Ip

    保存网络地址信息,支持ipv4ipv6类型的存储与检索,详细可查看官网

  • Join

    对于Join类型,不是很常用,但是也有使用,常用的我单独摘出来写了一篇文章,不常用或者比较简单常见的就简单一句话概括了,这个Join就在下面大概说说吧,首先还是先创建一个索引

    PUT my-index-000001
    {
      "mappings": {
        "properties": {
          "my_id": {
            "type": "keyword"
          },
          "my_join_field": { 
            "type": "join",
            "relations": {
              "user": "hobby" 
            }
          }
        }
      }
    }
    

    如上索引,父子文档,指定一个人的爱好,一个人可以有多个爱好,所以创建索引时指定父子关系字段类型,指定user的子类为hobby,每条文档都有一个名称(父文档或子文档),以上图创建的索引为例,每个文档都要指定这是个user还是个hobby,如下,文档1是父文档张三用户,文档2是父文档李四用户,文档3,4张三的爱好,文档5,6李四的爱好

    PUT my-index-000001/_doc/1?refresh
    {
      "my_id": "1",
      "text": "张三",
      "my_join_field": {
        "name": "user" 
      }
    }
    
    PUT my-index-000001/_doc/2?refresh
    {
      "my_id": "2",
      "text": "李四",
      "my_join_field": {
        "name": "user"
      }
    }
    
    PUT my-index-000001/_doc/3?routing=1&refresh 
    {
      "my_id": "3",
      "text": "乒乓球",
      "my_join_field": {
        "name": "hobby", 
        "parent": "1" 
      }
    }
    
    PUT my-index-000001/_doc/4?routing=1&refresh
    {
      "my_id": "4",
      "text": "篮球",
      "my_join_field": {
        "name": "hobby",
        "parent": "1"
      }
    }
    PUT my-index-000001/_doc/5?routing=1&refresh 
    {
      "my_id": "5",
      "text": "读书",
      "my_join_field": {
        "name": "hobby", 
        "parent": "2" 
      }
    }
    
    PUT my-index-000001/_doc/6?routing=1&refresh
    {
      "my_id": "6",
      "text": "下棋",
      "my_join_field": {
        "name": "hobby",
        "parent": "2"
      }
    }
    
    

    需要注意的是 写入数据时必须指定路由值,因为父子文档必须保证数据存储在同一个分片

    一个父文档可以有多个子文档,如下,userhobbygirlfriend的父类,grielfriendfans的父类



PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "user": ["hobby", "girlfriend"],  
          "girlfriend": "fans" 
        }
      }
    }
  }
}
  • Keyword

    • 关键字类型,单一的内容,比如身份证,姓名,Ip 等这种数据使用
    • 常量类型字段,一个索引中该字段的值是固定不变的,创建索引时指定,如果与指定的值不同,文档拒绝写入
    • 模糊查询类型,模糊查询可在keyword类型时使用,通配符类型在文本内容基数较大时候的做了优化

    关键字类型数据可以用于聚合,排序,查询(term)

  • Nested

    嵌套对象类型,作为一个单独的lucene文档索引,支持查询,排序,聚合,聚合父文档等功能,详细可参考博主的另一篇Nested详解,点击标签即可访问

  • Numeric

    数字类型,支持longintegershortbytedoublefloathalf_floatscaled_floatunsigned_long

    对于整数类型,使用longintegershortbyte就可以,浮点数可以使用scaled_float,如果scaled_float不能满足,那可以选取精读更高的doublefloathalf_float

  • Object

    在学习Nested嵌套对象类型的时候我们已经提到了一点,如果我们不知道对象类型,默认的就是Object类型的数据,在Elasticsearch内部,这个文档会被索引成一个简单扁平化的键值对形式

  • Range

    表示一个范围内的数据,使用gt或者gte定义下界,使用ltlte定义上界,不过支持的聚合的类型有限,支持histogram或者cardinality

    支持的类型有integer_rangefloat_rangelong_rangedouble_rangedate_rangeip_range

  • Text

    文本类型、为了textmatch_only_texttext传统的全文检索字段类型;match_only_text一种优化占用空间的文本类型,关闭打分,对需要执行查看进行较慢的操作,适合用作日志索引

    一般情况下使用text文件类型的字段还会使用一个keyword的字段,进行聚合、排序、关键字匹配等操作

2.2.2、字段动态映射

何为Dynamic mapping,我们知道在使用关系型数据库mysql的时候,我们创建一个表之后需要创建字段,比如字符串ID字符串NAME,时间类型CREATE_TIME等字段来使用,而Elasticsearch强大的就在于我们不在像关系型数据库那样提前设置好字段以及每个字段的类型,我们只需要保存一个文档,ES会自动的推断出数据类型并创建好索引,类型,字段映射关系

Elasticsearch检测到新字段时,默认情况下Elasticsearch会自动的识别字段数据类型,并将字段添加到mapping映射中,但是我们可以通过参数dynamic来指定是否自动添加字段映射,可选值有trueruntime,如果为true默认使用以下规则生成字段映射关系

JSON data type "dynamic":"true" "dynamic":"runtime"
null
true or false boolean boolean
double float double
long long long
object object
array 根据数组中第一个非空的值判断 根据数组中第一个非空的值判断
日期类型的字符串 date date
数字类型的字符串 float or long double or long
不是日期也不是数字的字符串 text类型以及.keyword的字类型 keyword

也可以设置dynamicfalse,这样在遇到新的字段时会抛出异常

2.2.3、字段动态映射模版

上面我们看到Elasticsearch会自动的推断数据类型,生成字段映射,那么Dynamic template动态模版就是自定义映射,提前内置好映射关系,在使用时通过一定的规则匹配上即可

如下:如果字段名称是ip开头的字符串,映射为ip类型的运行时字段

PUT my-index-000001/
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_ip": {
          "match_mapping_type": "string",
          "match": "ip*",
          "runtime": {
            "type": "ip"
          }
        }
      }
    ]
  }
}

如果想加到mapping中只需替换runtimemapping即可

PUT my-index-000001/
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_ip": {
          "match_mapping_type": "string",
          "match": "ip*",
          "mapping": {
            "type": "ip"
          }
        }
      }
    ]
  }
}

2.2.4、自定义字段映射

自定义字段映射,其实就是关系数据库中创建表时的字段设置,顾名思义就是提前设置好索引需要的字段以及字段类型,在添加文档时如遇到新字段报错(可配置),一般在规范索引字段时使用,比如日志索引,根据日期每天定时生成一个新的索引,这个索引我们就可以提前设置好模版,生成索引时直接使用模版生成,而模版中,直接定义好字段的类型,也就是说,自定义字段映射关系可以在映射模版的基础上使用的(关于索引模版的使用查看后文)如下是自定义字段映射的一个小例子

我们创建一个索引my-index-000001,其中ageinteger类型,emailkeyword类型,nametext类型

PUT /my-index-000001
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },  
      "email":  { "type": "keyword"  }, 
      "name":   { "type": "text"  }     
    }
  }
}

后续如果我们想修改这个已经创建的索引,比如增加个字段,那可以使用如下语句

PUT /my-index-000001/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

查看索引mapping语句如下

# 查看所有的字段映射
GET my-index-000001/_mapping
# 查看某一个字段的映射关系,此处以age举例
GET my-index-000001/_mapping/field/age

2.3、文本分析

2.3.1、什么是文本分析

文本分析是Elasticsearch实现全文检索的前提,通过全文检索可以实现返回结果不仅包含精确完全匹配的结果,还包含相近的结果。比如我们有个数据中华人民共和国,其中我们搜索中华或者人民或者共和国或者其他组合词语的时候都可以搜索出来,不仅限于中华人民共和国

  • Tokenization: 当我们进行全文检索时,分词可以让我们把一个词语拆分成更小的词语,默认情况下,这些小的词语也是有单独的词语语义

  • Normalization: 其中Tokenization可以解决单个关键词的匹配查询, 但是还是精确匹配,不能解决以下这种情况的查询

    • Quickquick的大小写
    • foxfoxes的词根
    • jumpsleaps的近义词

    为了解决这些问题,文本分析可以将这些token分词规范化为标准格式,这样就可以实现大小写,词根匹配,近义词匹配。

    为了确保搜索时搜索词与预期的词匹配,尽量要使用相同的tokenizationnormalization规则

2.3.2、配置文本分析器

默认情况下,Elasticsearch使用standard analyzer,如果不能满足我们的需求,我们可以使用其他的内置分析器,或者自定义分词器,分析器的组成有三部分,character filtertokenizertoken filter其中这三部分都可以根据我们的需求来进行选择设置

  • Character filter

    翻译一下就是字符过滤器,用在分词之前,常用的有html符号移除,正则替换、映射字符替换

  • Tokenizer

    核心分词器,将一组字符流拆成单个词语,具体的分词策略在此实现,默认的标准分词器,常用的中文分词器英文分词器拼音分词器

  • Token filter

    分词之后的词语过滤,比如英文中的停用词移除(a,an,the)、转换大小写

2.3.4、自定义分词器

需求

  1. 实现中文分词
  2. 实现拼音分词(词语全拼,词语首字母简拼)
  3. 实现:) => _happy_,:( => _sad_
  4. 使用pattern_replace替换机智如我

解决方案

  • 使用中文IK分词器拼音分词器组合实现上述需求1,2
  • 使用char_filter实现上述需求3
  • 使用filter实现上述需求4

过程分析

首先自定义分词器ik_smart_pinyinik_max_pinyin

  • ik_smart_pinyin:简单分词,分的比较粗,词语少
  • ik_max_pinyin:最大粒度分词,分的比较细,词语多

其中ik_smart_pinyin加入pinyin_first_letter_and_full_pinyin_filter实现拼音分词

ik_max_pinyin也加入pinyin_first_letter_and_full_pinyin_filter实现拼音分词,但是为了对比,此处ik_max_pinyin分词器再加入自定义的pattern_replace_filter实现上述的需求4,加入my_mappings_char_filter实现上述需求3

settings全部内容

完整的创建索引settings内容如下

PUT ik_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ik_smart_pinyin": {
          "tokenizer": "ik_smart",
          "filter": [
            "pinyin_first_letter_and_full_pinyin_filter"
          ]
        },
        "ik_max_pinyin": {
          "tokenizer": "ik_max_word",
          "filter": [
            "pinyin_first_letter_and_full_pinyin_filter",
            "pattern_replace_filter"
          ],
          "char_filter": "my_mappings_char_filter"
        }
      },
      "filter": {
        "pinyin_first_letter_and_full_pinyin_filter": {
          "type": "pinyin",
          "keep_separate_first_letter": false,
          "keep_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "lowercase": true,
          "remove_duplicated_term": true
        },
        "pattern_replace_filter": {
          "type": "pattern_replace",
          "pattern": "(我)",
          "replacement": "机智如$1"
        }
      },
      "char_filter": {
        "my_mappings_char_filter": {
          "type": "mapping",
          "mappings": [
            ":) => _happy_",
            ":( => _sad_"
          ]
        }
      }
    }
  }
}

2.4、小结

在上面创建的自定义分词器的基础上,我们在创建mapping字段关系映射时就可以直接指定ik_smart_pinyin或者ik_max_pinyin分词器即可实现分词啦,如下示例所示:

PUT ik_index/_mapping/
{
  "properties": {
    "content": {
      "type": "text",
      "analyzer": "ik_smart_pinyin"
    }
  }
}

三、总结

假总结,真心理总结

通过上面的学习,我们知道了设置索引时的常用参数、常见的字段类型以及建立字段映射关系时的参数设置,可以看得出来,创建索引就这三块,只要这三块使用熟练了,哪怕不查看官网也可以直接创建出一个满足自己需求的索引,鉴于考试时也都是可以参考官网的,所以上面的内容也不用死记硬背,只需要理解,多操作,记住常见的配置项即可了。好了该篇文章已经进入尾声了,一直想早点结束,但是写着写着就发现,知识点太多, 假设都写到这篇文章里,那就长的过分了,所以文中就分了几篇小的,比如字段类型那块,Geo、Nested等单独输出了一篇文章,想参考学习的可以关注公众号【醉鱼JAVA】直接去获取。后面的话,也是尽快吧,尽量赶点进度,把工作中常用的知识点整理起来,结合考点输出汇总,如果大家有好的意见或者建议欢迎评论区留言,下篇文章见喽!!!

分类:

后端

标签:

后端

作者介绍

醉鱼
V1