ElasticSearch学习记录
聚合
高阶概念
概念:
- 桶(Buckets)
满足特定条件的文档的集合。类比SQL 的分组(GROUP BY) - 指标(Metrics)
对桶内的文档进行统计计算。类比SQL中聚合函数 - 每个聚合都是一个或者多个桶和零个或者多个指标的组合
桶
指标
桶和指标的组合
每个 <国家, 性别, 年龄> 组合的平均薪酬。所有的这些都在一个请求内完成并且只遍历一次数据!
尝试聚合
自定义名字;为每个词项动态创建新的桶;popular_colors 聚合是作为 aggregations 字段的一部分被返回的;实时执行
GET /cars/transactions/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color.keywor"
}
}
}
}
添加度量指标
添加新的聚合
每种颜色汽车的平均价格是多少?
GET /cars/transactions/_search
{
"size" : 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
嵌套桶
度量与之间的独立性,可一次查询相关却不同的度量
我们想知道每个颜色的汽车制造商的分布
GET /cars/transactions/_search
{
"size" : 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
},
"make": {
"terms": {
"field": "make"
}
}
}
}
}
}
最后的修改,再嵌套
为每个汽车生成商计算最低和最高的价格
GET /cars/transactions/_search
{
"size" : 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": { "avg": { "field": "price" }
},
"make" : {
"terms" : {
"field" : "make"
},
"aggs" : {
"min_price" : { "min": { "field": "price"} },
"max_price" : { "max": { "field": "price"} }
}
}
}
}
}
}
条形图
聚合还有一个令人激动的特性就是能够十分容易地将它们转换成图表和图形
histogram直方图,在数值范围指定区间构建buckets
按时间统计
如果搜索是在 Elasticsearch 中使用频率最高的,那么构建按时间统计的 date_histogram 紧随其后。
date_histogram在时间范围指定区间构建buckets。相比较histogram自动识别日期,合理处理时区
返回空Buckets
默认返回非空buckets
设置参数返回空buckets。
"min_doc_count" : 0,
"extended_bounds" : {
"min" : "2014-01-01",
"max" : "2014-12-31"
}
限定范围的聚合
- 没有指定查询的聚合,默认match_all,即全文档查询;
增加query部分,可以限定查询范围再聚合。 - 全局桶 包含 所有 的文档,它无视查询的范围。因为它还是一个桶,我们可以像平常一样将聚合嵌套在内
global 全局桶没有参数
GET /cars/transactions/_search
{
"size" : 0,
"query" : {
"match" : {
"make" : "ford"
}
},
"aggs" : {
"single_avg_price": {
"avg" : { "field" : "price" }
},
"all": {
"global" : {},
"aggs" : {
"avg_price": {
"avg" : { "field" : "price" }
###聚合操作针对所有文档,忽略汽车品牌。
}
}
}
}
}
过滤和聚合
聚合自然的拓展就是使用过滤,应该尽可能的使用过滤
过滤
对搜索结果进行过滤:不计算得分的 filter 查询
- 从根本上讲,使用 non-scoring 查询和使用 match 查询没有任何区别。
- 查询(包括了一个过滤器)返回一组文档的子集,聚合正是操作这些文档。
- 使用 filtering query 会忽略评分,并有可能会缓存结果数据等等。
过滤桶
对聚合结果进行过滤:filter 桶
filter 桶和其他桶的操作方式一样,所以可以随意将其他桶和度量嵌入其中。所有嵌套的组件都会 “继承” 这个过滤,这使我们可以按需针对聚合过滤出选择部分
后过滤器
对搜索结果进行过滤,不过滤聚合结果
post_filter,这个过滤器在查询之后执行,所以对查询范围没有任何影响,对聚合也不会有任何影响。
失去过滤带来对好处。
小结
选择合适类型的过滤(如:搜索命中、聚合或两者兼有)通常和我们期望如何表现用户交互有关。选择合适的过滤器(或组合)取决于我们期望如何将结果呈现给用户。
- 在 filter 过滤中的 non-scoring 查询,同时影响搜索结果和聚合结果。
- filter 桶影响聚合。
- post_filter 只影响搜索结果。
多桶排序
默认根据doc_count 降序排列
内置排序
操作桶生成的数据
引入一个order内置对象
允许我们可以根据以下几个值中的一个值进行排序:
- _count
按文档数排序。对 terms 、 histogram 、 date_histogram 有效。 - _term
按词项的字符串值的字母顺序排序。只在 terms 内使用。 - _key
按每个桶的键值数值排序(理论上与 _term 类似)。 只在 histogram 和 date_histogram 内使用。
按度量排序
order对象中使用度量名字,多值度量中使用点式路径
基于“深度”度量排序
-
可以定义更深的路径,将度量用尖括号( > )嵌套起来,像这样: my_bucket>another_bucket>metric 。
-
嵌套路径上的每个桶都必须是 单值 的,filter 桶生成 一个单值桶。
近似聚合
max算法可以单词请求获得精确对结果,是高度并行对,可在多机上并行执行。可以横向扩展,无须任何协调操作,内存消耗小
更加复杂的操作则需要在算法的性能和内存使用上做出权衡。对于这个问题,我们有个三角因子模型:大数据、精确性和实时性。
我们需要选择其中两项:
- 精确 + 实时
数据可以存入单台机器的内存之中,我们可以随心所欲,使用任何想用的算法。结果会 100% 精确,响应会相对快速。 - 大数据 + 精确
传统的 Hadoop。可以处理 PB 级的数据并且为我们提供精确的答案,但它可能需要几周的时间才能为我们提供这个答案。 - 大数据 + 实时
近似算法为我们提供准确但不精确的结果。
ES支持两种近似算法,它们会提供准确但不是 100% 精确的结果。 以牺牲一点小小的估算错误为代价,这些算法可以为我们换来高速的执行效率和极小的内存消耗
- cardinality
- percentiles
统计去重后对数量
Elasticsearch 提供的首个近似聚合是 cardinality (注:基数)度量。 它提供一个字段的基数,即该字段的 distinct 或者 unique 值的数目
cardinality 度量是一个近似算法。 它是基于 HyperLogLog++ (HLL)算法的。 HLL 会先对我们的输入作哈希运算,然后根据哈希运算的结果中的 bits 做概率估算从而得到基数。算法特性:
- 可配置精度,控制内存对使用
- 精度与数据量成反比
- 可配置最大内存使用2量
参数precision_threshold 接受 0–40,000 之间的数字,这个阈值定义了在何种基数水平下我们希望得到一个近乎精确的结果
速度优化
…
百分位计算
Elasticsearch 提供的另外一个近似度量就是 percentiles 百分位数度量。 百分位数展现某以具体百分比下观察到的数值。
…
通过聚合发现异常指标
significant_terms (SigTerms)聚合 与其他聚合都不相同。 目前为止我们看到的所有聚合在本质上都是简单的数学计算。将不同这些构造块相互组合在一起,我们可以创建复杂的聚合以及数据报表。
significant_terms 有着不同的工作机制。对有些人来说,它甚至看起来有点像机器学习。 significant_terms 聚合可以在你数据集中找到一些 异常 的指标。它分析统计你的数据并通过对比正常数据找到可能有异常频次的指标。
演示
…
Doc Values and Fileddata
Doc Values
聚合使用doc values的数据结构。
doc values 的存在是因为倒排索引只对某些操作是高效的。倒排索引的优势在于查找包含某个项的文档,反向操作并不高效。
doc values 通过转置doc 与 terms的关系将文档映射到它们包含的词项中
Doc values 不仅可以用于聚合。 任何需要查找某个文档包含的值的操作都必须使用它。 除了聚合,还包括排序,访问字段值的脚本,父子关系处理
深入理解Doc Values
与倒排索引一样基于Segment生成且不可变,序列化到磁盘,使用操作系统的内存,不由JVM管理
- 列式存储的压缩
Doc Values 在压缩数值过程中使用如下技巧。它会按依次检测以下压缩模式:- 如果所有的数值各不相同(或缺失),设置一个标记并记录这些值
- 如果这些值小于 256,将使用一个简单的编码表
- 如果这些值大于 256,检测是否存在一个最大公约数
- 如果没有存在最大公约数,从最小的数值开始,统一计算偏移量进行编码
- 禁用
不分析的类型都会默认开启 ,关闭节省空间,提升索引速度- 在字段的映射(mapping)设置 doc_values: false 即可
- 也可以反过来,开启doc values,禁用索引
聚合与分析
使用分析字符串聚合得到的结果和要的并不相同,因为统计的是每个字符项。应该使用multifield并且设置为not_analyzed
-
分析字符串和Fielddata
Doc values不支持分析字符串,不能很有效的表示多值字符串,使用Fielddata数据结构支持聚合。Fileddata构建和管理100%在内存中,常驻于JVM内存堆,本质上不可扩展的。
-
高基数内存的影响
n-gram 的过程生成大量的唯一token,占用内存。聚合字符串字段前,评估:
- 这是一个 not_analyzed 字段吗?如果是,可以通过 doc values 节省内存 。
- 否则,这是一个 analyzed 字段,它将使用 fielddata 并加载到内存中。这个字段因为 ngrams 有一个非常大的基数?如果是,这对于内存来说极度不友好。
限制内存使用
- fielddata特点:
- 一旦分析字符串被加载到 fielddata ,他们会一直在那里,直到被驱逐(或者节点崩溃)。
- Fielddata是延迟加载,会加载索引中所有的特定文档,而不管查询的特异性。
- 运行时动态填充
- 限制 fielddata 对堆使用的影响有多套机制。选择堆大小:
- 不要超过可用RAM堆50%
- 不要超过32GB
- Fielddata堆大小
- indices.fielddata.cache.size 控制为 fielddata 分配的堆空间大小。加载时空间不足会回收其他值
- 默认unbounded,不回收。因为它堆构建很高昂
- 可以通过在 config/elasticsearch.yml 文件中增加配置为 fielddata 设置一个上限。有了这个设置,最久未使用**(LRU)**的 fielddata 会被回收为新数据腾出空间。
indices.fielddata.cache.size: 20%
- 监控 fielddata。高的回收数可以预示严重的资源问题以及性能不佳的原因
- 断路器
Elasticsearch 包括一个 fielddata 断熔器 ,这个设计就是为了处理上述情况。 断熔器通过内部检查(字段的类型、基数、大小等等)来估算一个查询需要的内存。它然后检查要求加载的 fielddata 是否会导致 fielddata 的总量超过堆的配置比例。
如果估算查询的大小超出限制,就会 触发 断路器,查询会被中止并返回异常。这都发生在数据加载 之前 ,也就意味着不会引起 OutOfMemoryException
分类:- indices.breaker.fielddata.limit
- indices.breaker.request.limit
- indices.breaker.total.limit
断路器的限制可以在文件 config/elasticsearch.yml 中指定
Fielddata的过滤
fielddata的过滤可以节省内存。
- fielddata关键字
- fielddata 关键字允许我们配置 fielddata 处理该字段的方式。
- frequency 过滤器允许我们基于项频率过滤加载 fielddata。
- 只加载那些至少在本段文档中出现 1% 的项。
- 忽略任何文档个数小于 500 的段。
PUT /music/_mapping/song
{
"properties": {
"tag": {
"type": "string",
"fielddata": {
"filter": {
"frequency": {
"min": 0.01,
"min_segment_size": 500
}
}
}
}
}
}
预加载fielddata
有三种方式可以解决延时高峰:
- 预加载 fielddata
预加载是按字段启用的
将载入 fielddata 的代价转移到索引刷新的时候,而不是查询时,从而大大提高了搜索体验
PUT /music/_mapping/_song
{
"tags": {
"type": "string",
"fielddata": {
"loading" : "eager"
}
}
}
- 预加载全局序号
… - 缓存预热,索引预热器
…
优化聚合查询
terms 桶基于我们的数据动态构建桶。聚合多字段时,可能产生大量分组,造成OOM
- 深度优先(默认)和广度优先
Elasticsearch 允许我们改变聚合的 集合模式,通过参数 collect 选择