ElasticSearch(ES)使用Nested结构存储KV及聚合查询
自建博客地址:https://www.bytelife.net,欢迎访问! 本文为博客同步发表文章,为了更好的阅读体验,建议您移步至我的博客
本文作者: Jeffrey
本文链接: https://www.bytelife.net/articles/51440.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
本文将讨论如何在ElasticSearch中使用nested结构进行数据的存储、查询和聚合,并结合K-V场景讨论ElasticSearch针对field数量限制的解决方案。
为何要使用Nested结构存储KV(键值对)?
ElasticSearch对于field的数量有限制,默认情况下field的数量如果超过1000个,写入时再创建新的fields就会报错:
java.lang.IllegalArgumentException: Limit of total fields [1000] in index [(index_name)] has been exceeded
at org.elasticsearch.index.mapper.MapperService.checkTotalFieldsLimit(MapperService.java:630)
但有些场景的field数量并不是我们能控制的,例如在监控系统中的业务数据所携带的业务标签,其中可能包含了监控系统不能预知的业务字段。
对于这种情景,可能想到的解决方案两个:
- 调整ElasticSearch的配置,增加field的限制数量:这种方案仅仅适用于可以预测出field数量极限的情况,治标不治本,一旦field数量再次抵达限制,又会面临同样的问题。
- 就是使用Pair结构来存储
假设第2种方案的数据结构为:
{
"labels": [{
"key": "ip",
"value: "127.0.0.1"
}]
},
{
"labels": [{
"key": "ip",
"value: "127.0.0.2"
}]
}
那么es查询就会存在一个问题,例如下面的查询:
{
"query":{
"bool":{
"must":[
{
"match":{
"key":"ip"
}
},
{
"match":{
"value":"127.0.0.1"
}
}
]
}
}
}
这个查询会把例子中的的数据全部查询出来,并不符合我们的预期。这是因为es在存储索引时,对于普通object类型的field实际上是打平来存储的,比如这样:
{
"labels.key":[
"ip"
],
"labels.value":[
"127.0.0.1",
"127.0.0.2"
]
}
可以看见,索引打平后,对象的关联关系丢失了。对于这种情况,ElasticSearch提供的nested结构可以帮助我们解决类似的问题。Nested结构保留了子文档数据中的关联性,如果labels的数据格式被定义为nested,那么每一个nested object将会作为一个隐藏的单独文本建立索引。如下:
{
"labels.key":"ip",
"labels.value":"127.0.0.1"
},
{
"labels.key":"ip",
"labels.value":"127.0.0.2"
}
通过分开给每个nested object建索引,object内部的字段间的关系就能保持。当执行查询时,只会匹配’match’同时出现在相同的nested object的结果。
定义mappings
使用nested结构非常简单,指定字段的type为nested即可。下面的例子中定义了一个名为labels的nested结构,其中包含两个字段,分别是key和value。
"mappings": {
"demoType": {
"labels": {
// 字段类型设置为nested
"type": "nested",
"properties": {
"key": {
"type": "keyword"
},
"value": {
"type": "keyword"
}
}
}
}
}
查询
nested结构的数据查询和普通object略有不同,nested object作为一个独立隐藏文档单独建索引,因此,不能直接查询到它们。取而代之,我们必须使用nested查询或者nested filter。例如:
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "labels",
"query": {
"bool": {
"must": [
{
"term": {
"labels.key": "ip"
}
},
{
"term": {
"labels.value": "127.0.0.1"
}
}
]
}
}
}
}
]
}
}
}
这个查询可以返回我们预期的正确结果:
[{
"labels": {
"key": "ip",
"value": "127.0.0.1"
}
}]
分桶聚合
查询的问题解决了,聚合时问题又来了,前面我们说到,nested结构存储在一个隐藏的单独文本索引中,那么普通的聚合查询自然便无法访问到它们。因此,nested结构在聚合时,需要使用特定的nested聚合。
nested聚合
假设es中存储如下数据:
[{
"labels": [{
"key": "ip",
"value": "127.0.0.1"
},{
"key": "os",
"value": "windows"
}]
}, {
"labels": [{
"key": "ip",
"value": "127.0.0.2"
},{
"key": "os",
"value": "linux"
}]
}]
我们要聚合所有对labels.value
进行聚合,可以使用下面的方式:
{
"size": 0,
"aggs": {
"labels_nested": {
"nested": {
"path": "labels"
},
"aggs": {
"nested_value": {
"terms": {
"field": "labels.value"
}
}
}
}
}
}
这个查询将会得到下面类似的结果:
{
"aggregations": {
"labels_nested": {
"doc_count": 2,
"nested_value": {
"buckets": [
{
"doc_count": 1,
"key": "127.0.0.1"
},
{
"doc_count": 1,
"key": "127.0.0.2"
},
{
"doc_count": 1,
"key": "windows"
},
{
"doc_count": 1,
"key": "linux"
}
]
}
}
}
}
过滤属性值
上面的例子可以看到,其只是单纯的将所有的value进行了聚合,并没有针对k-v中的key进行过滤,因此导致labels.key
为ip
和os
的数据均被统计到了其中,这通常不符合我们实际场景中的需求。
现在假设要对所有labels.key
为ip
的labels.value
进行聚合,那么可以使用如下的方式:
{
"size": 0,
"aggs": {
"labels_nested": {
"nested": {
"path": "labels"
},
"aggs": {
"nested_ip": {
"filter": {
"term": {
"labels.key": "ip"
}
},
"aggs": {
"nested_value": {
"terms": {
"field": "labels.value"
}
}
}
}
}
}
}
}
通过这样的方式就可以把labels.key
不是ip
的文档过滤掉,经过这个查询将得到类似如下的结果:
{
"aggregations": {
"labels_nested": {
"doc_count": 2,
"nested_ip": {
"doc_count": 2,
"nested_value": {
"buckets": [
{
"doc_count": 1,
"key": "127.0.0.1"
},
{
"doc_count": 1,
"key": "127.0.0.2"
}
]
}
}
}
}
}
nested多重聚合
如果想在nested聚合下嵌套聚合其它字段,直接嵌套是不行的,这里需要使用到reverse_nested
跳出当前nested聚合后,再进行嵌套聚合。
注意:无论是嵌套其它nested字段还是普通字段,都需要使用reverse_nested跳出当前nested聚合。
例如想对labels.key
为ip
聚合后,再对labels.key
为os
进行聚合:
{
"size": 0,
"aggs": {
"labels_nested": {
"nested": {
"path": "labels"
},
"aggs": {
"nested_ip": {
"filter": {
"term": {
"labels.key": "ip"
}
},
"aggs": {
"nested_ip_value": {
"terms": {
"field": "labels.value"
},
"aggs": {
"reverse_labels": {
"reverse_nested": {}, //注意这里
"aggs": {
"nested_os": {
"nested": {
"path": "labels"
},
"aggs": {
"labels_os": {
"filter": {
"term": {
"labels.key": "os"
}
},
"aggs": {
"labels_os_value": {
"terms": {
"field": "labels.value"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
如此,将得到类似下面的结果:
{
"aggregations": {
"labels_nested": {
"doc_count": 2,
"nested_ip": {
"nested_ip_value": {
"buckets": [
{
"doc_count": 1,
"reverse_labels": {
"doc_count": 1,
"nested_os": {
"labels_os": {
"doc_count": 1,
"labels_os_value": {
"buckets": [
{
"doc_count": 1,
"key": "windows"
}
]
}
},
"doc_count": 1
}
},
"key": "127.0.0.1"
},
{
"doc_count": 1,
"reverse_labels": {
"doc_count": 1,
"nested_os": {
"labels_os": {
"doc_count": 1,
"labels_os_value": {
"buckets": [
{
"doc_count": 1,
"key": "linux"
}
]
}
},
"doc_count": 1
}
},
"key": "127.0.0.2"
}
]
},
"doc_count": 2
}
}
}
}
结语
至此,关于nested结构存储K-V的用法就介绍完啦!使用nested结构可以帮助我们保持object内部的关联性,借此解决elasticsearch对field数量的限制。nested结构不仅可以应用在K-V结构的场景,还可以应用于其它任何需要保持object内部关联性的场景。
注意:使用nested结构也会存在一些问题:
- 增加,改变或者删除一个nested文本,整个文本必须重新建索引。nested文本越多,代价越大。
- 检索请求会返回整个文本,而不仅是匹配的nested文本。尽管有计划正在执行以能够支持返回根文本的同时返回最匹配的nested文本,但目前还未实现。
ElasticSearch(ES)使用Nested结构存储KV及聚合查询的更多相关文章
- ES[7.6.x]学习笔记(十)聚合查询
聚合查询,它是在搜索的结果上,提供的一些聚合数据信息的方法.比如:求和.最大值.平均数等.聚合查询的类型有很多种,每一种类型都有它自己的目的和输出.在ES中,也有很多种聚合查询,下面我们看看聚合查询的 ...
- JAVA中调用LevelDB用于Linux和Window环境下快速存储KV结构
一.简介 JAVA中调用LevelDB用于Linux和Window环境下快速存储KV结构 二.依赖 <!-- https://mvnrepository.com/artifact/org.fus ...
- Elasticsearch之重要核心概念(cluster(集群)、shards(分配)、replicas(索引副本)、recovery(据恢复或叫数据重新分布)、gateway(es索引的持久化存储方式)、discovery.zen(es的自动发现节点机制机制)、Transport(内部节点或集群与客户端的交互方式)、settings(修改索引库默认配置)和mappings)
Elasticsearch之重要核心概念如下: 1.cluster 代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的.es的一个概念就是 ...
- 解析如何利用ElasticSearch和Redis检索和存储十亿信息
如果从企业应用的生存率来看,选择企业团队信息作为主要业务,HipChat的起点绝非主流:但是如果从赚钱的角度上看,企业市场的高收益确实值得任何公司追逐,这也正是像JIRA和Confluence这样的智 ...
- Elasticsearch ES索引
ES是一个基于RESTful web接口并且构建在Apache Lucene之上的开源分布式搜索引擎. 同时ES还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向 ...
- 为什么Elasticsearch不适合做数据存储?(转学习使用)
一.问题描述 公司想尝试使用Elasticsearch来存一部分数据,以此缓解数据增长带来的对数据库的压力.在研究了一段时间后,发现Elasticsearch不适合作为数据存储使用. 二.理由如下 1 ...
- Elasticsearch(es)介绍与安装
### RabbitMQ从入门到集群架构: https://zhuanlan.zhihu.com/p/375157411 可靠性高 ### Kafka从入门到精通: https://zhuanlan. ...
- elasticsearch(es) 集群恢复触发配置(Local Gateway参数)
elasticsearch(es) 集群恢复触发配置(Local Gateway) 当你集群重启时,几个配置项影响你的分片恢复的表现. 首先,我们需要明白如果什么也没配置将会发生什么. 想象一下假设你 ...
- Kubernetes 搭建 ES 集群(存储使用 cephfs)
一.集群规划 使用 cephfs 实现分布式存储和数据持久化 ES 集群的 master 节点至少需要三个,防止脑裂. 由于 master 在配置过程中需要保证主机名固定和唯一,所以搭建 master ...
随机推荐
- ACM#学习心得0
加入实验室也有些日子了,这是第一个近来的小小学习心得 1.在之前的训练题和考核题以及平时刷过的题中,我发现自己对字符串这一块的基础知识掌握还是比较差的,总是不能正确的接收的字符或字符串. 这两个星期, ...
- 洛谷P2241-统计方形-矩形内计算长方形和正方形的数量
洛谷P2241-统计方形 题目描述: 有一个 \(n \times m\) 方格的棋盘,求其方格包含多少正方形.长方形(不包含正方形). 思路: 所有方形的个数=正方形的个数+长方形的个数.对于任意一 ...
- Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本
Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本 前言 序言 再高大上的框架,也需要扎实的基础才能玩转,高频面试问题更是基础中的高频实战要点. 适合阅读人群 J ...
- prefetch & preload & prerender & dns-prefetch & preconnect
prefetch & preload & prerender & dns-prefetch & preconnect performance optimization ...
- Upcoming Browser Behavior Changes & Chrome & SameSite
Upcoming Browser Behavior Changes & Chrome & SameSite Chrome 80 https://auth0.com/blog/brows ...
- 微信小程序-生命周期图解
微信小程序-生命周期图解 小程序生命周期 App 生命周期 https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.htm ...
- Big O Complexity Graph
Big O Complexity Graph Big O === O() 算法复杂度速查表 数据结构 数组排序算法 Quicksort O(n log(n)) O(n log(n)) O(n^2) O ...
- vue & child component & props
vue & child component & props vue pass data to child component https://vuejs.org/v2/guide/co ...
- iframe 父子互传消息,父页面滚动,子页面触发父页面高度
https://blog.csdn.net/qq_38366657/article/details/81538145 // 父页面的js<iframe id='TopHeader' src=&q ...
- Flutter:发布包
[package] 生成包含模块化Dart代码的可共享Flutter项目 [plugin] 生成一个可共享的Flutter项目, 在Dart代码中包含带有API的API, 针对Android的平台特定 ...