Skip to content

Latest commit

 

History

History
472 lines (379 loc) · 22 KB

File metadata and controls

472 lines (379 loc) · 22 KB
title date categories tags permalink
Elasticsearch 索引
2022-02-22 13:01:01 -0800
数据库
搜索引擎数据库
Elasticsearch
数据库
搜索引擎数据库
Elasticsearch
索引
/pages/293175/

Elasticsearch 索引

索引管理操作

Elasticsearch 索引管理主要包括如何进行索引的创建、索引的删除、副本的更新、索引读写权限、索引别名的配置等等内容。

索引删除

ES 索引删除操作向 ES 集群的 http 接口发送指定索引的 delete http 请求即可,可以通过 curl 命令,具体如下:

curl -X DELETE http://{es_host}:{es_http_port}/{index}

如果删除成功,它会返回如下信息,具体示例如下:

curl -X DELETE http://10.10.10.66:9200/my_index?pretty

为了返回的信息便于读取,增加了 pretty 参数:

{
  "acknowledged" : true
}

索引别名

ES 的索引别名就是给一个索引或者多个索引起的另一个名字,典型的应用场景是针对索引使用的平滑切换。

首先,创建索引 my_index,然后将别名 my_alias 指向它,示例如下:

PUT /my_index
PUT /my_index/_alias/my_alias

也可以通过如下形式:

POST /_aliases
{
  "actions": [
    { "add": { "index": "my_index", "alias": "my_alias" }}
  ]
}

也可以在一次请求中增加别名和移除别名混合使用:

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

需要注意的是,如果别名与索引是一对一的,使用别名索引文档或者查询文档是可以的,但是如果别名和索引是一对多的,使用别名会发生错误,因为 ES 不知道把文档写入哪个索引中去或者从哪个索引中读取文档。

ES 索引别名有个典型的应用场景是平滑切换,更多细节可以查看 Elasticsearch(ES)索引零停机(无需重启)无缝平滑切换的方法

Settings 详解

Elasticsearch 索引的配置项主要分为静态配置属性动态配置属性,静态配置属性是索引创建后不能修改,而动态配置属性则可以随时修改。

ES 索引设置的 api 为 _settings,完整的示例如下:

PUT /my_index
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "1",
      "refresh_interval": "60s",
      "analysis": {
        "filter": {
          "tsconvert": {
            "type": "stconvert",
            "convert_type": "t2s",
            "delimiter": ","
          },
          "synonym": {
            "type": "synonym",
            "synonyms_path": "analysis/synonyms.txt"
          }
        },
        "analyzer": {
          "ik_max_word_synonym": {
            "filter": [
              "synonym",
              "tsconvert",
              "standard",
              "lowercase",
              "stop"
            ],
            "tokenizer": "ik_max_word"
          },
          "ik_smart_synonym": {
            "filter": [
              "synonym",
              "standard",
              "lowercase",
              "stop"
            ],
            "tokenizer": "ik_smart"
          }
        },
			"mapping": {
				"coerce": "false",
				"ignore_malformed": "false"
			},
			"indexing": {
				"slowlog": {
					"threshold": {
						"index": {
							"warn": "2s",
							"info": "1s"
						}
					}
				}
			},
			"provided_name": "hospital_202101070533",
			"query": {
				"default_field": "timestamp",
				"parse": {
					"allow_unmapped_fields": "false"
				}
			},
			"requests": {
				"cache": {
					"enable": "true"
				}
			},
			"search": {
				"slowlog": {
					"threshold": {
						"fetch": {
							"warn": "1s",
							"info": "200ms"
						},
						"query": {
							"warn": "1s",
							"info": "500ms"
						}
					}
				}
			}
		}
	}
}

固定属性

  • index.creation_date:顾名思义索引的创建时间戳。
  • index.uuid:索引的 uuid 信息。
  • index.version.created:索引的版本号。

索引静态配置

  • index.number_of_shards:索引的主分片数,默认值是 5。这个配置在索引创建后不能修改;在 es 层面,可以通过 es.index.max_number_of_shards 属性设置索引最大的分片数,默认为 1024
  • index.codec:数据存储的压缩算法,默认值为 LZ4,可选择值还有 best_compression,它比 LZ4 可以获得更好的压缩比(即占据较小的磁盘空间,但存储性能比 LZ4 低)。
  • index.routing_partition_size:路由分区数,如果设置了该参数,其路由算法为:( hash(_routing) + hash(_id) % index.routing_parttion_size ) % number_of_shards。如果该值不设置,则路由算法为 hash(_routing) % number_of_shardings_routing 默认值为 _id

静态配置里,有重要的部分是配置分析器(config analyzers)。

  • index.analysis

    :分析器最外层的配置项,内部主要分为 char_filter、tokenizer、filter 和 analyzer。

    • char_filter:定义新的字符过滤器件。
    • tokenizer:定义新的分词器。
    • filter:定义新的 token filter,如同义词 filter。
    • analyzer:配置新的分析器,一般是 char_filter、tokenizer 和一些 token filter 的组合。

索引动态配置

  • index.number_of_replicas:索引主分片的副本数,默认值是 1,该值必须大于等于 0,这个配置可以随时修改。
  • index.refresh_interval:执行新索引数据的刷新操作频率,该操作使对索引的最新更改对搜索可见,默认为 1s。也可以设置为 -1 以禁用刷新。更详细信息参考 Elasticsearch 动态修改 refresh_interval 刷新间隔设置

Mapping 详解

在 Elasticsearch 中,Mapping(映射),用来定义一个文档以及其所包含的字段如何被存储和索引,可以在映射中事先定义字段的数据类型、字段的权重、分词器等属性,就如同在关系型数据库中创建数据表时会设置字段的类型。

Mapping 会把 json 文档映射成 Lucene 所需要的扁平格式

一个 Mapping 属于一个索引的 Type

  • 每个文档都属于一个 Type
  • 一个 Type 有一个 Mapping 定义
  • 7.0 开始,不需要在 Mapping 定义中指定 type 信息

映射分类

在 Elasticsearch 中,映射可分为静态映射和动态映射。在关系型数据库中写入数据之前首先要建表,在建表语句中声明字段的属性,在 Elasticsearch 中,则不必如此,Elasticsearch 最重要的功能之一就是让你尽可能快地开始探索数据,文档写入 Elasticsearch 中,它会根据字段的类型自动识别,这种机制称为动态映射,而静态映射则是写入数据之前对字段的属性进行手工设置。

静态映射

静态映射是在创建索引时手工指定索引映射。静态映射和 SQL 中在建表语句中指定字段属性类似。相比动态映射,通过静态映射可以添加更详细、更精准的配置信息。

如何定义一个 Mapping

PUT /books
{
    "mappings": {
        "type_one": { ... any mappings ... },
        "type_two": { ... any mappings ... },
        ...
    }
}

动态映射

动态映射是一种偷懒的方式,可直接创建索引并写入文档,文档中字段的类型是 Elasticsearch 自动识别的,不需要在创建索引的时候设置字段的类型。在实际项目中,如果遇到的业务在导入数据之前不确定有哪些字段,也不清楚字段的类型是什么,使用动态映射非常合适。当 Elasticsearch 在文档中碰到一个以前没见过的字段时,它会利用动态映射来决定该字段的类型,并自动把该字段添加到映射中,根据字段的取值自动推测字段类型的规则见下表:

JSON 格式的数据 自动推测的字段类型
null 没有字段被添加
true or false boolean 类型
浮点类型数字 float 类型
数字 long 类型
JSON 对象 object 类型
数组 由数组中第一个非空值决定
string 有可能是 date 类型(若开启日期检测)、double 或 long 类型、text 类型、keyword 类型

下面举一个例子认识动态 mapping,在 Elasticsearch 中创建一个新的索引并查看它的 mapping,命令如下:

PUT books
GET books/_mapping

此时 books 索引的 mapping 是空的,返回结果如下:

{
  "books": {
    "mappings": {}
  }
}

再往 books 索引中写入一条文档,命令如下:

PUT books/it/1
{
	"id": 1,
	"publish_date": "2019-11-10",
	"name": "master Elasticsearch"
}

文档写入完成之后,再次查看 mapping,返回结果如下:

{
  "books": {
    "mappings": {
      "properties": {
        "id": {
          "type": "long"
        },
        "name": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "publish_date": {
          "type": "date"
        }
      }
    }
  }
}

使用动态 mapping 要结合实际业务需求来综合考虑,如果将 Elasticsearch 当作主要的数据存储使用,并且希望出现未知字段时抛出异常来提醒你注意这一问题,那么开启动态 mapping 并不适用。在 mapping 中可以通过 dynamic 设置来控制是否自动新增字段,接受以下参数:

  • true:默认值为 true,自动添加字段。
  • false:忽略新的字段。
  • strict:严格模式,发现新的字段抛出异常。

基础类型

类型 关键字
字符串类型 string、text、keyword
数字类型 long、integer、short、byte、double、float、half_float、scaled_float
日期类型 date
布尔类型 boolean
二进制类型 binary
范围类型 range

复杂类型

类型 关键字
数组类型 array
对象类型 object
嵌套类型 nested

特殊类型

类型 关键字
地理类型 geo_point
地理图形类型 geo_shape
IP 类型 ip
范围类型 completion
令牌计数类型 token_count
附件类型 attachment
抽取类型 percolator

Mapping 属性

Elasticsearch 的 mapping 中的字段属性非常多,具体如下表格:

| 属性名 | 描述 | | :- | :- | | | type | 字段类型,常用的有 text、integer 等等。 | | index | 当前字段是否被作为索引。可选值为 true,默认为 true。 | | store | 是否存储指定字段,可选值为 true | false,设置 true 意味着需要开辟单独的存储空间为这个字段做存储,而且这个存储是独立于 _source 的存储的。 | | norms | 是否使用归一化因子,可选值为 true | false,不需要对某字段进行打分排序时,可禁用它,节省空间;typetext 时,默认为 true;而 typekeyword 时,默认为 false。 | | index_options | 索引选项控制添加到倒排索引(Inverted Index)的信息,这些信息用于搜索(Search)和高亮显示:docs:只索引文档编号(Doc Number);freqs:索引文档编号和词频率(term frequency);positions:索引文档编号,词频率和词位置(序号);offsets:索引文档编号,词频率,词偏移量(开始和结束位置)和词位置(序号)。默认情况下,被分析的字符串(analyzed string)字段使用 positions,其他字段默认使用 docs。此外,需要注意的是 index_option 是 elasticsearch 特有的设置属性;临近搜索和短语查询时,index_option 必须设置为 offsets,同时高亮也可使用 postings highlighter。 | | term_vector | 索引选项控制词向量相关信息:no:默认值,表示不存储词向量相关信息;yes:只存储词向量信息;with_positions:存储词项和词项位置;with_offsets:存储词项和字符偏移位置;with_positions_offsets:存储词项、词项位置、字符偏移位置。term_vector 是 lucene 层面的索引设置。 | | similarity | 指定文档相似度算法(也可以叫评分模型):BM25:ES 5 之后的默认设置。 | | copy_to | 复制到自定义 _all 字段,值是数组形式,即表明可以指定多个自定义的字段。 | | analyzer | 指定索引和搜索时的分析器,如果同时指定 search_analyzer 则搜索时会优先使用 search_analyzer。 | | search_analyzer | 指定搜索时的分析器,搜索时的优先级最高。 | | null_value | 用于需要对 Null 值实现搜索的场景,只有 Keyword 类型支持此配置。 |

索引查询

多个 index、多个 type 查询

Elasticsearch 的搜索 api 支持一个索引(index)的多个类型(type)查询以及**多个索引(index)**的查询。

例如,我们可以搜索 twitter 索引下面所有匹配条件的所有类型中文档,如下:

GET /twitter/_search?q=user:shay

我们也可以搜索一个索引下面指定多个 type 下匹配条件的文档,如下:

GET /twitter/tweet,user/_search?q=user:banon

我们也可以搜索多个索引下匹配条件的文档,如下:

GET /twitter,elasticsearch/_search?q=tag:wow

此外我们也可以搜索所有索引下匹配条件的文档,用_all 表示所有索引,如下:

GET /_all/_search?q=tag:wow

甚至我们可以搜索所有索引及所有 type 下匹配条件的文档,如下:

GET /_search?q=tag:wow

URI 搜索

Elasticsearch 支持用 uri 搜索,可用 get 请求里面拼接相关的参数,并用 curl 相关的命令就可以进行测试。

如下有一个示例:

GET twitter/_search?q=user:kimchy

如下是上一个请求的相应实体:

{
  "timed_out": false,
  "took": 62,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1.3862944,
    "hits": [
      {
        "_index": "twitter",
        "_type": "_doc",
        "_id": "0",
        "_score": 1.3862944,
        "_source": {
          "user": "kimchy",
          "date": "2009-11-15T14:12:12",
          "message": "trying out Elasticsearch",
          "likes": 0
        }
      }
    ]
  }
}

URI 中允许的参数:

名称 描述
q 查询字符串,映射到 query_string 查询
df 在查询中未定义字段前缀时使用的默认字段
analyzer 查询字符串时指定的分词器
analyze_wildcard 是否允许通配符和前缀查询,默认设置为 false
batched_reduce_size 应在协调节点上一次减少的分片结果数。如果请求中潜在的分片数量很大,则应将此值用作保护机制,以减少每个搜索请求的内存开销
default_operator 默认使用的匹配运算符,可以是AND或者OR,默认是OR
lenient 如果设置为 true,将会忽略由于格式化引起的问题(如向数据字段提供文本),默认为 false
explain 对于每个 hit,包含了具体如何计算得分的解释
_source 请求文档内容的参数,默认 true;设置 false 的话,不返回_source 字段,可以使用**_source_include_source_exclude**参数分别指定返回字段和不返回的字段
stored_fields 指定每个匹配返回的文档中的存储字段,多个用逗号分隔。不指定任何值将导致没有字段返回
sort 排序方式,可以是fieldNamefieldName:asc或者fieldName:desc的形式。fieldName 可以是文档中的实际字段,也可以是诸如_score 字段,其表示基于分数的排序。此外可以指定多个 sort 参数(顺序很重要)
track_scores 当排序时,若设置 true,返回每个命中文档的分数
track_total_hits 是否返回匹配条件命中的总文档数,默认为 true
timeout 设置搜索的超时时间,默认无超时时间
terminate_after 在达到查询终止条件之前,指定每个分片收集的最大文档数。如果设置,则在响应中多了一个 terminated_early 的布尔字段,以指示查询执行是否实际上已终止。默认为 no terminate_after
from 从第几条(索引以 0 开始)结果开始返回,默认为 0
size 返回命中的文档数,默认为 10
search_type 搜索的方式,可以是dfs_query_then_fetchquery_then_fetch。默认为query_then_fetch
allow_partial_search_results 是否可以返回部分结果。如设置为 false,表示如果请求产生部分结果,则设置为返回整体故障;默认为 true,表示允许请求在超时或部分失败的情况下获得部分结果

查询流程

在 Elasticsearch 中,查询是一个比较复杂的执行模式,因为我们不知道那些 document 会被匹配到,任何一个 shard 上都有可能,所以一个 search 请求必须查询一个索引或多个索引里面的所有 shard 才能完整的查询到我们想要的结果。

找到所有匹配的结果是查询的第一步,来自多个 shard 上的数据集在分页返回到客户端之前会被合并到一个排序后的 list 列表,由于需要经过一步取 top N 的操作,所以 search 需要进过两个阶段才能完成,分别是 query 和 fetch。

参考资料