栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

ElasticSearch相关知识点

ElasticSearch相关知识点

简介

基于Apache Lucene(TM)的开源搜索引擎,由于Lucene使用的复杂性,ElasticSearch的目的就是通过RESTful API 让全文搜索变得简单。

基础概念

1、接近实时NRT

全文搜索一般做不到真实时,一般会有一个延迟,不同的搜索引擎都有一个核心的搜索的延迟时间,ES一般的延迟时间在1s。

2、分布式集群

通过管理多节点来提高扩容性,多个节点使用一个名称,就是ES管理的一个集群。

一个运行中的ES实例被称为一个节点,集群是由有相同名字(即cluster.name相同)的节点组成,共同进行数据的处理和负载平衡。

3、文档

具象化来说文档是对象的序列话的JSON表示。对象被序列化成JSON后可以被称为文档。很多条文档组成了一个索引。

是指最顶层或者根对象, 这个根对象被序列化成 JSON 并存储到 Elasticsearch 中,指定了唯一 ID。

ES是分布式的文档存储,他实际上是将复杂的对象序列化成JSON,然后将JSON文档存储并实现实时的搜索。

4、节点

存储数据(对应一个服务器),每个节点用一个名称标识,不指定在节点启动的时候回赋予一个随机名字,节点通过配置集群名称cluster.name的方式加入集群,如果没有集群,节点在启动的时候会创建一个名为“elasticsearch”集群,并加入该默认集群。

5、主节点

负责管理集群内变更,增加删除索引或增加删除节点,主节点不进行文档级别的变更和搜索等操作。

使用方可以将请求加入到集群中的任意节点,节点会将请求转发到实际需要请求的节点,最终将数据放回给使用方。

6、索引

索引是相似特征文档的集合,不要求一个索引下文档结构相同,但是相同时搜索的效率会提升。比如运单索引,必须通过索引对运单信息进行搜索、更新和删除。一个集群中可以定义任意多的索引。

索引指向一个或多个物理分片的逻辑命名空间。索引是对分片的抽象,实际使用时,程序直接与索引进行交互。

索引也可以代表存储文档使其属于某一个索引(名词意义上)的过程。

7、类型

一个索引可以定义一个或多种类型,类型是一种索引的逻辑分类,可以用于过滤。不同结构的数据不能用type来区分,应该使用不同的索引,类似于mysql的table的概念。Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type。

两个类型的同命字段不能定义成两个不同的属性,参考Lucene处理文档

8、分片

分片是一个底层的工作单元,保存了全部数据中的一部分,一个分片对应一个Lucene实例,且一个分片本身就是一个完整的搜索引擎。

数据被存储和索引到分表内,分片被分配到集群内的各个节点(服务器),扩删集群时,ES自动在个节点中迁移分片,进行负载均衡

主分片:索引内的数据都属于某一个主分片,主分片的数目决定索引能保存的最大数据量

副分片:主分片的复本,为保障硬件故障数据不丢失,可以为读操作提供服务

索引建立的时候确定主分片的片数,副本片数可以随时修改。

增加副本分片可以提高吞吐量,但是副分片和主分片不能在一个节点

详解 根对象

映射的最高一层被称作跟对象,主要包括以下几个

1、_source 元数据

只是在相同节点数目的集群上增加更多的副本分片并不能提高性能,因为每个分片从节点上获得的资源会变少。 你需要增加更多的硬件资源来提升吞吐量。

9、故障恢复

当一个节点挂掉了,如果是主节点会重新选举主节点,如果挂掉的节点包括主分片,对应的副负片在的节点正常,会将副分片升级为主分片。ES自动管理。

10、映射mappings

Mapping is the process of defining how a document, and the fields it contains, are stored and indexed.

映射定义了一个文档和其所包含的字段如何被存储和被索引(被查询)。

映射描述了文档可能具有的字段或属性、每个字段的数据类型以及Lucene是如何索引和存储这些字段的。

一个 properties 节点,列出了文档中可能包含的每个字段的映射

元数据字段以下划线开头的字段,文档包含数据外还有指定其属性的元数据,有三个必不可少的元数据

_index 文档存放索引,名称必须小写不能以下划线开头,不能包含逗号

_type 文档对象类别,对索引中的数据进行逻辑区分,不同类别可以有不同的字段。

可以包含大写字符,不能有逗号且不能以下划线或句号开头,限制256个字符

_id 文档唯一id

设置项,控制如何动态处理新的字段,例如 analyzer 、 dynamic_date_formats 和 dynamic_templates

其他设置,可以同时应用在根对象和其他 object 类型的字段上,例如 enabled 、 dynamic 和 include_in_all

在 _source 字段存储代表文档体的JSON字符串,即最原始的数据。和所有被存储的字段一样, _source 字段在被写入磁盘之前先会被压缩。

使用_source的优点

PUT /my_index
{
    "mappings": {
        "my_type": {
            "_source": {
                "enabled":  false
            }
        }
    }
}

GET /_search
{
    "query":   { "match_all": {}},
    "_source": [ "title", "created" ]
}

搜索结果包括了整个可用的文档——不需要额外的从另一个的数据仓库来取文档。

如果没有 _source 字段,部分 update 请求不会生效。

当你的映射改变时,需要重新索引你的数据,有了_source字段则可以直接重新索引而不用从另一个(通常是速度更慢的)数据仓库取回你的所有文档。

当你不需要看到整个文档时,单个字段可以从 _source 字段提取和通过 get 或者 search 请求返回。

利于调试,可以直接看文档包含的内容。

如果不需要原始的数据,可以禁用source节省空间。

指定需要返回的字段

java api

SearchSourceBuilder  builder = new SearchSourceBuilder();
List fieldNameList = Lists.newArrayList("billCode");
String[] includes = fieldNameList.toArray();
builder.fetchSource(new FetchSourceContext(true, includes, null));

2、_all元数据

_all字段将所有其他字段集合形成一个大文本字段,query_string 查询子句(搜索 ?q=john )在没有指定字段时默认使用 _all 字段。

举例

索引文档内容:

{
    "tweet":    "However did I manage before Elasticsearch?",
    "date":     "2014-09-14",
    "name":     "Mary Jones",
    "user_id":  1
}

_all 字段内容

{
  "_all" : "However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1"
}

禁用_all

禁用_all

PUT /my_index/_mapping/my_type
{
    "my_type": {
        "_all": { "enabled": false }
    }
}

字段设置是否进入_all

include_in_all 设置来逐个控制字段是否要包含在 _all 字段中,默认值是 true。

3、文档标识

文档标识与四个元数据字段相关:

_id

默认情况下, _uid 字段是被存储(可取回)和索引(可搜索)的。 _type 字段被索引但是没有存储, _id 和 _index 字段则既没有被索引也没有被存储,这意味着它们并不是真实存在的。

尽管如此,你仍然可以像真实字段一样查询 _id 字段。Elasticsearch 使用 _uid 字段来派生出 _id 。 虽然你可以修改这些字段的 index 和 store 设置,但是基本上不需要这么做。

Lucene处理文档

Lucene 没有文档类型的概念,每个文档的类型名被存储在一个叫 _type 的元数据字段上。 当我们要检索某个类型的文档时, Elasticsearch 通过在 _type 字段上使用过滤器限制只返回这个类型的文档。

如果有两个不同的类型,每个类型都有同名的字段,但映射不同(例如:一个是字符串一个是数字),将会出现什么情况?

答:会出错,因为底层上ES所有类型最终都共享相同的映射,对于整个索引,映射在本质上被 扁平化 成一个单一的、全局的模式。这就是为什么两个类型不能定义冲突的字段:当映射被扁平化时,Lucene 不知道如何去处理。 类型和映射

类型映射

每个文档由一系列的字段组成,每个字段有各自的数据类型。映射定义包含一系列与文档相关的字段(feild) 和其类型等的相关定义。映射定义也包括元数据字段(metadate filed),比如 _source 等。元数据字段定义了一个文档相关的元数据的处理方式。

1、动态映射

为了让用户快速使用 es,用户可以可以直接向 es 新增文档,文档对应的字段、映射和类型由动态映射自动生成。使用动态映射确定字段的数据类型并自动把新的字段添加到类型映射。

动态映射是当用户新增一些文档时,默认增加的字段和类型之间的映射。通过设置参数 dynamic 值来设置不同的默认映射。

以下是默认的动态类型映射:

把 dynamic 设置为 false 一点儿也不会改变 _source 的字段内容。 _source 仍然包含被索引的整个JSON文档。只是新的字段不会被加到映射中也不可搜索

文档的 ID 字符串

_type

文档的类型名

_index

文档所在的索引

_uid

_type 和 _id 连接在一起构造成 type#id

一个文档由一组简单的键值对组成,每一个字段可以有 1 到多个值。

一个字符串可以通过分析过程转化成多个值,Lucene不关心值的类型是什么,对他来说都被当做不透明字节。

在Lucene中索引一个文档时,每个字段的值都被添加到相关字段的倒排索引中。

true动态添加新的字段—缺省

false忽略新的字段

strict如果遇到新字段抛出异常

JSON 数据类型

"dynamic":"true" 转换

"dynamic":"runtime"转换

null

No field added

No field added

true or false

boolean

boolean

double

float

double

integer

long

long

object1

object

object

array

Depends on the first non-null value in the array

Depends on the first non-null value in the array

符合日期格式的字符串

date

date

符合数字的字符串

float or long

double or long

其余字符串不支持

其中对象Object会再重新映射成 properties 字段。

动态映射主要包括两部分

PUT /my_index
{
    "mappings": {
        "my_type": {
            // 动态模板定义
            "dynamic_templates": [
              	// 模板名称
                { "es": {
                  		// 匹配规则
                      "match":              "*_es", 
                  		// 匹配的类型,应用模板到特定类型的字段上
                      "match_mapping_type": "string",
                  		// 匹配类型和定义分词器
                      "mapping": {
                          "type":           "string",
                          "analyzer":       "spanish"
                      }
                }},
                { "en": {
                      "match":              "*", 
                      "match_mapping_type": "string",
                      "mapping": {
                          "type":           "string",
                          "analyzer":       "english"
                      }
                }}
            ]
}}}

动态属性映射

动态字段匹配规则

动态模板 

动态字段属性映射的自定义规则

3、自定义动态映射规则

日期

默认规则值 dynamic_date_formats:

[ "strict_date_optional_time","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"]

更改默认值

es :以 _es 结尾的字段名需要使用 spanish 分词器。

en :所有其他字段使用 english 分词器。

PUT my_index
{
  "mappings": {
    "my_type": {
      "dynamic_date_formats": ["MM/dd/yyyy"]
    }
  }
}

4、动态模板

使用 dynamic_templates动态模板 ,控制新检测生成字段的映射,也可以通过字段名称或数据类型来应用不同的映射。

每个模板都有一个名称,一个 mapping 来指定映射应该怎样使用,以及至少一个参数 (如 match) 来定义这个模板适用于哪个字段。

模板按照顺序来检测;第一个匹配的模板会被启用。例如,我们给 string 类型字段定义两个模板:

es :以 _es 结尾的字段名需要使用 spanish 分词器。

en :所有其他字段使用 english 分词器。

match 参数只匹配字段名称,

path_match 参数匹配字段在对象上的完整路径, address.*.name 匹配对象路径

unmatch 和 path_unmatch将被用于未被匹配的字段。

5、 缺省映射

通常,一个索引中的所有类型共享相同的字段和设置。 _default_ 映射更加方便地指定通用设置,而不是每次创建新类型时都要重复设置。 _default_ 映射是新类型的模板。在设置 _default_ 映射之后创建的所有类型都将应用这些缺省的设置,除非类型在自己的映射中明确覆盖这些设置。

例如,我们可以使用 _default_ 映射为所有的类型禁用 _all 字段, 而只在 blog 类型启用:

PUT /my_index
{
    "mappings": {
        "_default_": {
            "_all": { "enabled":  false }
        },
        "blog": {
            "_all": { "enabled":  true  }
        }
    }
}
ES语法使用 1、创建索引

PUT 地址/索引名称

PUT http://127.0.0.1:9200/commodity

默认情况下,创建的索引分片数量是 5 个,副本数量是 1 个,没有指定集群则默认新建一个名为 elasticjob 的集群

可以在创建索引的时候同时指定分片和映射带上如下参数进行自定义配置:

{
	"settings": {
		"number_of_shards": 3,
		"number_of_replicas": 2
	},
	"mapping": {
		"_doc": // 类型
    {
			"properties": {
				"commodity_id": {
					"type": "long"
				},
				"commodity_name": {
					"type": "text"
				},
				"picture_url": {
					"type": "keyword"
				},
				"price": {
					"type": "double"
				}
			}
		}
	}
}

field字段解释

mappings映射字段field

搜索要解决的问题是“包含查询关键词的文档有哪些?”,聚合恰恰相反,聚合要解决的问题是“文档包含哪些词项”,大多数字段再索引时生成doc_values,但是text字段不支持doc_values。取而代之,text字段在查询时会生成一个fielddata的数据结构,fielddata在字段首次被聚合、排序、或者使用脚本的时候生成。ELasticsearch通过读取磁盘上的倒排记录表重新生成文档词项关系,最后在Java堆内存中排序。

text字段的fielddata属性默认是关闭的,开启fielddata非常消耗内存。在你开启text字段以前,想清楚为什么要在text类型的字段上做聚合、排序操作。大多数情况下这么做是没有意义的。

“New York”会被分析成“new”和“york”,在text类型上聚合会分成“new”和“york”2个桶,也许你需要的是一个“New York”。这是可以加一个不分析的keyword字段。

ignore_above字段

指定字段索引和存储的长度最大值,超过最大值的会被忽略,索引被忽略。

2、索引新增

原始用法

操作 /索引名称/类型/自定义id

响应体

PUT /{index}/{type}/{id}
{
  "field": "value",
  ...
}
PUT /website/blog/123
{
  "title": "My first blog entry",
  "text":  "Just trying this out...",
  "date":  "2014/01/01"
}

 

响应体

{
   "_index":    "website", // 索引
   "_type":     "blog", // 类型
   "_id":       "123", // id
   "_version":  1, // 版本号
   "created":   true // 是否是新增
}

不指定 ID 则使用系统生成默认 ID

POST /website/blog/
{
  "title": "My second blog entry",
  "text":  "Still trying this out...",
  "date":  "2014/01/01"
}

响应

{
   "_index":    "website",
   "_type":     "blog",
   "_id":       "AVFgSgVHUP18jI2wRx0w",// 系统生成的默认ID
   "_version":  1,
   "created":   true
}

在 Elasticsearch 中每个文档都有一个版本号。当每次对文档进行修改时(包括删除), _version 的值会递增。 在 处理冲突 中,讨论了怎样使用 _version 号码确保你的应用程序中的一部分修改不会覆盖另一部分所做的修改。

3、简单查询
GET /{index}/{type}/{id}

参数

pretty

可读方式返回

_source=title,text

返回部分数据,返回数据中的title和text字段

_source

只返回_source数据字段不需要元数据

获得博客中id为123的博文

GET /website/blog/123?pretty
{
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 1,
  "found" :    true,
  "_source" :  {
      "title": "My first blog entry",
      "text":  "Just trying this out...",
      "date":  "2014/01/01"
  }
}

query 查询

match :匹配指定条件,执行查询前会用正确的分析器去分析查询字符串

{ "match": { "age":    26 }}

match_all : 匹配所有文档

multi_match : 多个字段执行相同的查询

{
    "multi_match": {
        "query":    "full text search",
        "fields":   [ "title", "body" ]
    }
}

term :精确查询,输入文本不进行分析,基于给定的值进行精确匹配

{ "term": { "age":    26 }}

terms:多值匹配,只要文档包含指定列表中任意一个值即可被召回

{ "terms": { "tag": [ "search", "full_text", "nosql" ] }}

exists :指定字段有值

missing:指定字段无值

bool :布尔查询用于合并查询语句

must : 文档必须包含的条件

must not : 文档必须不包含的条件

should :满足条件增加文档分(_score)否则没有影响,修正每个文档的相关性得分,当must或fileter与should联合使用时需要注意should条件失效的问题。

filter : 过滤,需要过滤的条件,过滤器执行速度快,因为他不会计算文档的相关度,按照满足和不满足进行筛选,且结果很容易被缓存。

range : 范围

gt、gte:大于、大于等于

lt、lte:小于、小于等于

{
    "range": {
        "age": {
            "gte":  20,
            "lt":   30
        }
    }
}

minimum_should_match: should条件应最低满足的条件个数,当must或fliter与should连用时,should条件可能会退化成加减分,所以使用minimum_should_match可是强制让should条件最低满足多少,支持百分比或者指定值

{
    "bool": {
        "must": { "match":   { "email": "business opportunity" }},
        "should": [
            { "match":       { "starred": true }},
            { "bool": {
                "must":      { "match": { "folder": "inbox" }},
                "must_not":  { "match": { "spam": true }}
            }}
        ],
        "minimum_should_match": 1// match 和bool至少满足一个 或的关系
    }
}

搜索是多个关键字时,默认认为是OR关系

GET //{index}/{type}/_search
{
	"query":{ 
    "match":{
      "desc":"条件 第二个条件"}},// 用空格分隔的多个条件
	"from":2, // 指定位移
	"size":10 // 指定返回的size
}

执行多个关键字的and搜索 使用布尔查询

{
  "query": {
    "bool": {
      // 即满足 软件又满足系统
      "must": [
        { "match": { "desc": "软件" } },
        { "match": { "desc": "系统" } }
      ]
    }
  }
}'

查询和过滤的区别

filter 过滤是一个 选择题 只有是和否

query 查询是一个 简答题 不同的答案分数不一

考验文档与此查询的 相关程度,同时将这个相关程度分配给表示相关性的字段 _score,并且按照相关性对匹配到的文档进行排序

4、更新

文档是不可变的,需要修改,重建索引或进行替换,重建索引进行put时,返回的version会增加

PUT /{index}/{type}/{id}
{
  "title": "My first blog entry",
  "text":  "I am starting to get the hang of this...",
  "date":  "2014/01/02"
}
返回
{
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 2, // 版本号
  "created":   false // 已存在
}
5、创建新文档

指定op_type参数为create,只接受新建,冲突返回409

PUT /{index}/{type}/{id}?op_type=create
6、删除

删除会增加version的值,仅是逻辑删除,即使不存在version也会增加

DELETe /{index}/{type}/{id}
7、游标查询scroll

深度分页慢的原因是当页数越大时每个分片返回的数据集就越多,最后排序的结果集就越大,排序代价越大。游标查询是取某个时间点的快照数据,游标查询初始化之后索引上的任何变化会被忽略,游标查询可以看做取某一个点的所有数据,并给这个数据设置一个有效时间,在这个有效时间内,一批一批的返回分页的结果,而且下一批分页的结果需要使用上一批结果返回的scrolle_id查询。

GET /your_index/_search?scroll=1m
{
	"query":{"match_all":{}}, // 匹配所有文档 筛选条件
	"sort":["_doc"],
	"size":1000
}

游标窗口有效时间 1 分钟

排序方式:_doc 

size:指定的是每个分片的数量大小,所以取回的文档数可能最大为 size * 你的主分片数量

返回:

GET /_search/scroll
{
    "scroll": "1m", 
    "scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
}
搜索 结构化搜索 term查询-单个精确值查找

使用范围:数字、布尔值、日期、文本

如果索引数据的方式是分析时,通过term进行精确查找会不能召回,所以要将字段设置成not_analyzed才能进行精确值查找

使用示例:

GET /claim_form/_search
{
  
  "query": {
    "term": {"id": "20395536"}
  }
}
terms-多个精确值查找

召回文档id字段包含List中内容的字段,只要文档的id字段在List中即可被召回

语法如下:

GET /claim_form/_search
{
  
  "query": {
    "terms": {"id": ["1","2"]}
  }
}
//结果
// 召回id值为 1 和2 的两个文档

当召回文档的字段值包含多值时,比如字段【tags】的文档值是【["tag1","tag2"]】当使用term(terms相同){ "term" : { "tags" : "tag1" } } ,该文档是可以被召回的。是一个包含而不是等值的概念。

组合过滤器-bool过滤器

接收多个其他过滤器作为参数并将这些过滤器组合成布尔逻辑组合

结构

{
   "bool" : {
      "must" :     [],// 所有语句必须匹配 and 
      "should" :   [], // 至少有一个语句匹配 OR 
      "must_not" : [],// 所有语句不匹配 NOT AND 
   }
}

布尔过滤器内部还可以进行嵌套,复杂使用情况如想表达如下sql语句:

select * from claim_form where claimSource=3 or ( complaintOrgCode=1 &&toComplaintOrgCode1=2);

GET /claim_form/_search
{
  "query": {
    "bool": {
      "filter": {
        "bool": {
          "should": [
            {
              "term": {
                "claimSource": "3"
              }
            },
            {
              "bool": {
                "must": [
                  {
                    "term": {
                      "complaintOrgCode": "1"
                    }
                  },
                  {
                    "term": {
                      "toComplaintOrgCode1": "2"
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
  }
}

案例-分享一个在使用es中过程遇到的一个坑

项目历史原因,使用es用的是最基础的方法,没有将es的查询条件进行封装而是每次查询将查询参数塞进一个bool查询中。项目需要查询在9月1号被投诉方省区或投诉方省区是000195省区的单子,每一个单子被投诉方最多有三个最少有一个。当时使用的查询语句如下:

POST /form/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "complaintZoneCode": "000195"
          }
        },{
          "term": {
            "toComplaintZoneCode1": "000195"
          }
        },
        {
          "term": {
            "toComplaintZoneCode2": "000195"
          }
        },
        {
          "term": {
            "toComplaintZoneCode3": "000195"
          }
        }
      ],
      "filter": {
        "range": {
          "gmtCreate": {
            "gte": "2021-09-01 00:00:00",
            "lte": "2021-09-01 23:59:59"
          }
        }
      }
    }
  }
}

该语句是 bool 由 filter 和 should 组成,即理想效果是查找到时间范围是9月1号的,投诉方省区或被投诉方省区是000195的单子。该语句查询到的结果总共有 135924 条。 分页查询(加入能直接跳到深页)时会发现后面几页的数据不是符合要求的数据,原因是当 should 语句同时和 filter 或 must 等语句如上使用时,should 语句变成一种评分项,即当文档满足 should 中的条件那么文档的相关度评分增加在召回中排在更前面,但是不满足 should 的文档也会被召回,即在召回排序的后面。解决方式就是使用  minimum_should_match ,该参数会强制召回的文档必须满足 should 条件中的一条。

改后的语句如下:

POST /form/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "complaintZoneCode": "000195"
          }
        },{
          "term": {
            "toComplaintZoneCode1": "000195"
          }
        },
        {
          "term": {
            "toComplaintZoneCode2": "000195"
          }
        },
        {
          "term": {
            "toComplaintZoneCode3": "000195"
          }
        }
      ],
      "minimum_should_match": 1, // 强制 should 至少满足一项
      "filter": {
        "range": {
          "gmtCreate": {
            "gte": "2021-09-01 00:00:00",
            "lte": "2021-09-01 23:59:59"
          }
        }
      }
    }
  }
}

执行后召回结果为40599,都是满足条件的。当然如果你的 bool 语句中只有 should 语句时,召回的文档一定是必须满足其中一项才会被召回。

为了理解 should 时效的条件我又尝试了以下语法进行查询,即将 filter 语句和 should 语句放在 bool 中 又将其处于 filter 的上下文中,

查出的结果也是40599

POST /form/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "filter": {
            "range": {
              "gmtCreate": {
                "gte": "2021-09-01 00:00:00",
                "lte": "2021-09-01 23:59:59"
              }
            }
          },
          "should": [
            {
              "term": {
                "complaintZoneCode": "000195"
              }
            },
            {
              "term": {
                "toComplaintZoneCode1": "000195"
              }
            },
            {
              "term": {
                "toComplaintZoneCode2": "000195"
              }
            },
            {
              "term": {
                "toComplaintZoneCode3": "000195"
              }
            }
          ]
        }
      }
    }
  }
}

结果是正确的,但是有以下提示:

#! Deprecation: Should clauses in the filter context will no longer automatically set the minimum should match to 1 in the next major version. You should group them in a [filter] clause or explicitly set [minimum_should_match] to 1 to restore this behavior in the next major version.

   实际上这么使用的时候,es自动帮我们加入的 minimum_should_match 条件。底层源码:

final String minimumShouldMatch;
  if (context.isFilter() && this.minimumShouldMatch == null && shouldClauses.size() > 0) {
    if (mustClauses.size() > 0 || mustNotClauses.size() > 0 || filterClauses.size() > 0) {
      deprecationLogger.deprecatedAndMaybeLog("filter_context_min_should_match",
        "Should clauses in the filter context will no longer automatically set the minimum should " +
          "match to 1 in the next major version. You should group them in a [filter] clause or explicitly set " +
          "[minimum_should_match] to 1 to restore this behavior in the next major version." );
    }
    minimumShouldMatch = "1";
  } else {
    minimumShouldMatch = this.minimumShouldMatch;
  }

更详细的分析可以参考这篇,

更清楚一写

范围查询

这部分比较简单,举例说明

查询状态大于等于1小于等于2的数据

POST /claim_form/_search
{
  
  "query": {
    "bool": {
      "filter": {
        "range": {
          "status": {
            "gte": 1,
            "lte": 2
          }
        }
      }
    }
  }
}

查询创建日期大于等于2021年9月01号的数据

POST /claim_form/_search
{
  
  "query": {
    "bool": {
      "filter": {
        "range": {
          "gmtCreate": {
            "gte": "2021-09-01 00:00:00"
          }
        }
      }
    }
  }
}

 

ES存储结构

 

es 的搜索引擎严重依赖于底层的 filesystem cache ,你如果给 filesystem cache 更多的内存,尽量让内存可以容纳所有的 idx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。

数据先写入内存 buffer,然后每隔 1s,将数据刷新到系统缓存中,系统缓存中的数据就能被搜索到即es 从写入到能被搜索到,中间有 1s 的延迟。每隔 5s ,将数据写入 translog 文件(这样如果机器宕机,内存数据全没,最多会有 5s 的数据丢失),translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都刷新到 segment file 磁盘文件中。

优化

数据预热

定时搜索热数据保证数据在文件系统缓存中,可以让用户更快的搜索到

问题:如果已经是热数据正常应该一直会在缓存中 为什么需要不断搜索保持其一直在缓存中?

实际使用场景中,大v的一条微博查看的用户比较多,但是用户不是在同一时间去查询的所以间隔比如1分钟时,可能改热点数据已经被非热点数据挤走,所以保持其一直在缓存中,能让更多的用户更快的搜索。

冷热分离

大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在 filesystem os cache 里,别让冷数据给冲刷掉。

文档的设计

数据的一些关联操作在写入文档之前利用代码将相关关联数据写入,不要依赖本身es的关联等复杂操作进行查询,这些操作的效率低

分页优化

es 的分页越往后越慢,原因是翻页越往后每个分片上需要查询的数据就越多,总体数据量越多,需要排序的数据量越多

优化

不支持深分页

利用翻页模式,scroll 会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标scroll_id移动,获取下一页,性能基本是毫秒级的。但是不支持跳页。

search_after 来做,search_after的思想是使用前一页的结果来帮助检索下一页的数据,不允许你随意翻页。初始化时,需要使用一个唯一值的字段作为 sort 字段。

 

引用:

ES Mapping、字段类型Field type详解

ES JAVA API

ES  官网

ES优化

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/712942.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号