8、es---深入聚合数据分析

一、bucket与metric

1、bucket相当于mysql的group by。

2、metric:对一个数据分组执行的统计,比如说求平均值,求最大值,求最小值

 

二、实战

1、例1:查询参数及结果说明

GET /tvs/sales/_search
{
    "size" : 0,
    "aggs" : { 
        "popular_colors" : { 
            "terms" : { 
              "field" : "color"
            }
        }
    }
}

size:只获取聚合结果,而不要执行聚合的原始数据
aggs:固定语法,要对一份数据执行分组聚合操作
popular_colors:就是对每个aggs,都要起一个名字,这个名字是随机的,你随便取什么都ok
terms:根据字段的值进行分组
field:根据指定的字段的值进行分组

结果:
hits.hits:我们指定了size是0,所以hits.hits就是空的,否则会把执行聚合的那些原始数据给你返回回来
aggregations:聚合结果
popular_color:我们指定的某个聚合的名称
buckets:根据我们指定的field划分出的buckets
key:每个bucket对应的那个值
doc_count:这个bucket分组内,有多少个数据

2、例2:对bucket执行metric聚合操作

GET /tvs/sales/_search
{
   "size" : 0,
   "aggs": {------------------>先进行bucket,再进行metric操作
      "colors": {
         "terms": {---------->bucket操作
            "field": "color"
         },
         "aggs": { ------------------->先进行bucket,再进行metric操作
            "avg_price": { 
               "avg": {----------------->metric操作
                  "field": "price" 
               }
            }
         }
      }
   }
}

3、例3:多层下钻(分组再分组)

GET /tvs/sales/_search 
{
  "size": 0,
  "aggs": {
    "group_by_color": {
      "terms": {------------------------>分组1
        "field": "color"
      },
      "aggs": {
        "color_avg_price": {
          "avg": {
            "field": "price"
          }
        },
        "group_by_brand": {
          "terms": {------------------------>分组2
            "field": "brand"
          },
          "aggs": {
            "brand_avg_price": {
              "avg": {----------------------->求平均数
                "field": "price"
              }
            }
          }
        }
      }
    }
  }
}

4、例4:常见的数据分析的操作-->count,avg,max,min,sum

GET /tvs/sales/_search
{
   "size" : 0,
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": { "avg": { "field": "price" } },
            "min_price" : { "min": { "field": "price"} }, 
            "max_price" : { "max": { "field": "price"} },
            "sum_price" : { "sum": { "field": "price" } } 
         }
      }
   }
}

5、例5:histogram

histogram:类似于terms,也是进行bucket分组操作

GET /tvs/sales/_search
{
   "size" : 0,
   "aggs":{
      "price":{----------------->聚和名称
         "histogram":{ ----------------->相当于bucke,分组操作
            "field": "price",
            "interval": 2000---------------->按区间进行分组
         },
         "aggs":{
            "revenue": {----------------->聚和名称
               "sum": { 
                 "field" : "price"
               }
             }
         }
      }
   }
}

6、例6:按照日期进行分组:date_histogram

GET /tvs/sales/_search
{
   "size" : 0,
   "aggs": {
      "sales": {---->聚合名称
         "date_histogram": {
            "field": "sold_date",
            "interval": "month", 
            "format": "yyyy-MM-dd",
            "min_doc_count" : 0, ---->即使某个日期interval,2017-01-01~2017-01-31中,一条数据都没有,那么这个区间也是要展现的,不然默认是会过滤掉这个区间的
            "extended_bounds" : { ----->划分bucket的时候,会限定在这个起始日期,和截止日期内
                "min" : "2016-01-01",

                "max" : "2017-12-31"
            }
         }
      }
   }
}

7、例7:搜索+聚合

scope:任何的聚合,都必须在搜索出来的结果数据中之行,搜索结果,就是聚合分析操作的scope

GET /tvs/sales/_search 
{
  "size": 0,
  "query": {
    "term": {
      "brand": {
        "value": "小米"
      }
    }
  },
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color"
      }
    }
  }
}

8、例:出来两个结果,一个结果,是基于query搜索结果来聚合的; 一个结果,是对所有数据执行聚合的

GET /tvs/sales/_search 
{
  "size": 0, 
  "query": {
    "term": {
      "brand": {
        "value": "长虹"
      }
    }
  },
  "aggs": {
    "single_brand_avg_price": {
      "avg": {
        "field": "price"
      }
    },
    "all": {
      "global": {},--------->global:就是global bucket,就是将所有数据纳入聚合的scope,而不管之前的query
      "aggs": {
        "all_brand_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

9、过滤+聚合 (原理同搜索+聚合)

GET /tvs/sales/_search 
{
  "size": 0,
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "price": {
            "gte": 1200
          }
        }
      }
    }
  },
  "aggs": {
    "avg_price": {
      "avg": {
        "field": "price"
      }
    }
  }
}

10、bucket_filter:(局部filter),对不同的bucket下的aggs,进行filter

GET /tvs/sales/_search 
{
  "size": 0,
  "query": {
    "term": {
      "brand": {
        "value": "长虹"
      }
    }
  },
  "aggs": {
    "recent_150d": {
      "filter": {
        "range": {
          "sold_date": {
            "gte": "now-150d"
          }
        }
      },
      "aggs": {
        "recent_150d_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    },
    "recent_140d": {
      "filter": {
        "range": {
          "sold_date": {
            "gte": "now-140d"
          }
        }
      },
      "aggs": {
        "recent_140d_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    },
    "recent_130d": {
      "filter": {
        "range": {
          "sold_date": {
            "gte": "now-130d"
          }
        }
      },
      "aggs": {
        "recent_130d_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

11、排序:默认按bucket的doc_count降序排,指定聚合数排序(metric)

GET /tvs/sales/_search 
{
  "size": 0,
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color",
        "order": {
          "avg_price": "asc"
        }
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

 

三、易并行算法+近似聚合算法+三角选择原则

1、易并行聚合算法:max

很容易就可以并行的

2、近似聚合算法:distinct count

es会采取近似聚合的方式,就是采用在每个node上进行近估计的方式,得到最终的结论,cuont(distcint),100万,1050万/95万 --> 5%左右的错误率
近似估计后的结果,不完全准确,但是速度会很快,一般会达到完全精准的算法的性能的数十倍

3、三角选择原则

精准+实时+大数据 --> 选择2个
(1)精准+实时: 没有大数据,数据量很小,那么一般就是单击跑,随便你则么玩儿就可以
(2)精准+大数据:hadoop,批处理,非实时,可以处理海量数据,保证精准,可能会跑几个小时
(3)大数据+实时:es,不精准,近似估计,可能会有百分之几的错误率

 

四、cartinality metric

1、说明:es,去重,cartinality metric,对每个bucket中的指定的field进行去重,取去重后的count,类似于count(distcint)。5%的错误率,性能在100ms左右

 

2、例:

、GET /tvs/sales/_search
{
  "size" : 0,
  "aggs" : {
      "months" : {
        "date_histogram": {
          "field": "sold_date",
          "interval": "month"
        },
        "aggs": {
          "distinct_colors" : {
              "cardinality" : {
                "field" : "brand"
              }
          }
        }
      }
  }
}

3、precision_threshold优化准确率和内存开销

GET /tvs/sales/_search
{
    "size" : 0,
    "aggs" : {
        "distinct_brand" : {
            "cardinality" : {
              "field" : "brand",
              "precision_threshold" : 100 --->如果field的unique value,在100个以内,就将precision_threshold设置为100。cardinality算法,会占用precision_threshold * 8 byte 内存消耗,100 * 8 = 800个字节
            }
        }
    }
}

precision_threshold:估计有多少个unique field-value就将precision_threshold设置为几,es会占用precision_threshold * 8 byte 内存消耗

4、HyperLogLog++ (HLL)算法性能优化

cardinality底层算法:HLL算法
会对所有的uqniue value取hash值,通过hash值近似去求distcint count
默认情况下,发送一个cardinality请求的时候,会动态地对所有的field value,取hash值; 
优化:将取hash值的操作,前移到建立索引的时候

PUT /tvs/
{
  "mappings": {
    "sales": {
      "properties": {
        "brand": {
          "type": "text",
          "fields": {
            "hash": {
              "type": "murmur3" 
            }
          }
        }
      }
    }
  }
}

GET /tvs/sales/_search
{
    "size" : 0,
    "aggs" : {
        "distinct_brand" : {
            "cardinality" : {
              "field" : "brand.hash",
              "precision_threshold" : 100 
            }
        }
    }
}

 

五、percentiles && percentile_ranks

1、常用于 网站的延时统计(tp50,tp99等)

tp50:50%的请求的耗时最长在多长时间
tp90:90%的请求的耗时最长在多长时间
tp99:99%的请求的耗时最长在多长时间

SLA:就是你提供的服务的标准

网站的提供的访问延时的SLA,大公司内,一般都是要求100%在200ms以内

2、percentiles&percents:统计百分之多少的field最大值

例如:

GET /website/logs/_search 
{
  "size": 0,
  "aggs": {
    "latency_percentiles": {
      "percentiles": {
        "field": "latency",
        "percents": [
          50,
          95,
          99
        ]
      }
    },
    "latency_avg": {
      "avg": {
        "field": "latency"
      }
    }
  }
}

3、percentile_ranks:统计一个field的值在[x,y]范围内的百分比

例如:

GET /website/logs/_search 
{
  "size": 0,
  "aggs": {
    "group_by_province": {
      "terms": {
        "field": "province"
      },
      "aggs": {
        "latency_percentile_ranks": {
          "percentile_ranks": {
            "field": "latency",
            "values": [
              200,
              1000
            ]
          }
        }
      }
    }
  }
}

 

六、doc_value 正排索引

doc_vlaue就是正排索引的数据结构,聚合操作会使用到正排索引

1、doc value原理

a、建立索引的时候默认就会生成正排索引

b、正排索引,也会写入磁盘文件中,然后呢,os cache先进行缓存,以提升访问doc value正排索引的性能。如果os cache内存大小不足够放得下整个正排索引,doc value,就会将doc value的数据写入磁盘文件中

c、性能问题:给jvm更少内存,64g服务器,给jvm最多16g

es官方是建议,es大量是基于os cache来进行缓存和提升性能的,不建议用jvm内存来进行缓存,那样会导致一定的gc开销和oom问题
给jvm更少的内存,给os cache更大的内存
例如:64g服务器,给jvm最多16g,几十个g的内存给os cache

2、column压缩

doc1: 550
doc2: 550
doc3: 500
合并相同值,550,doc1和doc2都保留一个550的标识即可
(1)所有值相同,直接保留单值
(2)少于256个值,使用table encoding模式:一种压缩方式
(3)大于256个值,看有没有最大公约数,有就除以最大公约数,然后保留这个最大公约数
doc1: 36
doc2: 24
12 --> doc1: 3, doc2: 2 --> 保留一个最大公约数12的标识,12也保存起来
(4)如果没有最大公约数,采取offset结合压缩的方式:

3、disable doc value
doc_vlaue就是正排索引的数据结构,聚合操作会使用到正排索引,如果没有聚合操作,可将其禁用。
如果的确不需要doc value,比如聚合等操作,那么可以禁用,减少磁盘空间占用
PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "my_field": {
          "type":       "keyword"
          "doc_values": false 
        }
      }
    }
  }
}

 

七、string field 与fieldData

1、对于string或text等分词类型的field进行聚合操作时,需要设置:fieldData = true, 否则会报错,如下:

"reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [test_field] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory."

2、例如:

POST /test_index/_mapping/test_type 
{
  "properties": {
    "test_field": {
      "type": "text",
      "fielddata": true
    }
  }
}

{
  "test_index": {
    "mappings": {
      "test_type": {
        "properties": {
          "test_field": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            },
            "fielddata": true
          }
        }
      }
    }
  }
}

3、fieldData与doc_value的区别

fieldData: 分词field,是没有doc value。对于分词field,必须打开和使用fielddata=true,且是懒加载(query-time时加载)完全存在于纯内存中。

doc_value:如果field不分词,那么在index-time,就会自动生成doc value --> 针对这些不分词的field执行聚合操作的时候,自动就会用doc value来执行

 

八、fieldData内存

1、lazy load

fielddata加载到内存的过程是lazy加载的,对一个analzyed field执行聚合时,才会加载,query-time时加载。

2、fielddata内存限制

indices.fielddata.cache.size: 20%。fielddata占用的内存超出了这个比例的限制,那么就清除掉内存中已有的fielddata数据
默认无限制。

限制内存使用,但是会导致频繁evict和reload,大量IO性能损耗,以及内存碎片和gc。

3、监控fielddata内存使用

GET /_stats/fielddata?fields=*  ---------->每个分片(share)中的每个索引中的fieldData内存使用情况
GET /_nodes/stats/indices/fielddata?fields=*--------->每个个node中fieldData内存使用情况
GET /_nodes/stats/indices/fielddata?level=indices&fields=*------->每个node中的每个索引中的fieldData内存使用情况

4、circuit breaker

如果一次query load的feilddata超过总内存,就会oom --> 内存溢出

circuit breaker会估算query要加载的fielddata大小,如果超出总内存,就短路,query直接失败,如下:
indices.breaker.fielddata.limit:fielddata的内存限制,默认60%
indices.breaker.request.limit:执行聚合的内存限制,默认40%
indices.breaker.total.limit:综合上面两个,限制在70%以内

 

九、fieldData- filter

1、使用:

POST /test_index/_mapping/my_type
{
  "properties": {
    "my_field": {
      "type": "text",
      "fielddata": { 
        "filter": {
          "frequency": { 
            "min":              0.01, 
            "min_segment_size": 500  
          }
        }
      }
    }
  }
}

2、说明:

min:仅仅加载至少在1%的doc中出现过的term对应的fielddata
比如说某个值,hello,总共有1000个doc,hello必须在10个doc中出现,那么这个hello对应的fielddata才会加载到内存中来

min_segment_size:少于500 doc的segment不加载fielddata
加载fielddata的时候,也是按照segment去进行加载的,某个segment里面的doc数量少于500个,那么这个segment的fielddata就不加载

 

十、fieldData预加载

如果真的要对分词的field执行聚合,那么每次都在query-time现场生产fielddata并加载到内存中来,速度可能会比较慢

1、fielddata预加载

POST /test_index/_mapping/test_type
{
  "properties": {
    "test_field": {
      "type": "string",
      "fielddata": {
        "loading" : "eager" 
      }
    }
  }
}
query-time的fielddata生成和加载到内存,变为index-time,建立倒排索引的时候,会同步生成fielddata并且加载到内存中来,这样的话,对分词field的聚合性能当然会大幅度增强

2、序号标记预加载

global ordinal原理解释
doc1: status1
doc2: status2
doc3: status2
doc4: status1
有很多重复值的情况,会进行global ordinal标记
status1 --> 0
status2 --> 1
doc1: 0
doc2: 1
doc3: 1
doc4: 0

建立的fielddata也会是这个样子的,这样的好处就是减少重复字符串的出现的次数,减少内存的消耗,如下:
POST /test_index/_mapping/test_type
{
  "properties": {
    "test_field": {
      "type": "string",
      "fielddata": {
        "loading" : "eager_global_ordinals" 
      }
    }
  }
}

 

十一、下钻聚合操作的深度优先和广度优先

1、区别

深度优先:默认就是深度优先,树状结构

广度优先:先水平搜索

2、使用:

{
  "aggs" : {
    "actors" : {
      "terms" : {
         "field" :        "actors",
         "size" :         10,
         "collect_mode" : "breadth_first" --------->广度优先
      },
      "aggs" : {
        "costars" : {
          "terms" : {
            "field" : "films",
            "size" :  5
          }
        }
      }
    }
  }
}

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值