Elasticsearch 索引
# 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)索引零停机(无需重启)无缝平滑切换的方法 (opens new window)。
# 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 刷新间隔设置 (opens new window)。
# 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
,不需要对某字段进行打分排序时,可禁用它,节省空间;type 为 text 时,默认为 true;而 type 为 keyword 时,默认为 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 | 排序方式,可以是fieldName、fieldName: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_fetch或query_then_fetch。默认为query_then_fetch |
allow_partial_search_results | 是否可以返回部分结果。如设置为 false,表示如果请求产生部分结果,则设置为返回整体故障;默认为 true,表示允许请求在超时或部分失败的情况下获得部分结果 |
# 查询流程
在 Elasticsearch 中,查询是一个比较复杂的执行模式,因为我们不知道那些 document 会被匹配到,任何一个 shard 上都有可能,所以一个 search 请求必须查询一个索引或多个索引里面的所有 shard 才能完整的查询到我们想要的结果。
找到所有匹配的结果是查询的第一步,来自多个 shard 上的数据集在分页返回到客户端之前会被合并到一个排序后的 list 列表,由于需要经过一步取 top N 的操作,所以 search 需要进过两个阶段才能完成,分别是 query 和 fetch。