ELK: Getting Start In Logstash

架构简介

Input(数据采集)->Filter(数据解析/转换)->Output(数据输出)

Pipeline

1.input-filter-output的3阶段处理流程
2.队列管理
3.插件生命周期管理

Logstash Event

1.内部流转的数据表现形式
2.原始数据在input被装换为Event, 在output event被转换为目标格式数据
3.在配置文件中可以对Event中的属性进行增删改查

Life Of An Event

1.Input从日志文件读取数据
2.被数据的数据经过JSON Codec将数据变成Logstash Event 3.Logstash Event通过Queue流入到某个Worker Threads中
4.数据在某个Worker Threads中的Batcher, 当Batcher达到某个条件后, 将数据发送给Filter
5.Filter将数据发送给Output, Output进行输出
6.最后发送ACK发送给Queue, 告知处理了哪些数据

Queue简介

1.In Memory  
<1>无法处理进程Crash、机器宕机等情况, 会导致数据丢失 2.Persistent Queue In Disk  
<1>可处理进程Crash等情况, 保证数据不丢失  
<2>保证数据至少消费一次  
<3>充当缓冲区, 可以代替Kafka等消息队列的作用

Persistent Queue简单流程

1.数据从Input进入到Persistent Queue
2.Persistent Queue在磁盘上备份数据, Persistent Queue返回结果给Input(需Input Plugins支持Event Response)
3.数据从Persistent Queue发送给Filter/Output
4.Filter/Output收到数据处理完后返回ACK给Persistent Queue 5.Persistent Queue收到ACK后, 把磁盘上的数据清理掉, 保证数据不丢失

Persistent Queue的基本配置

1.queue.type:persisted:默认是memory
2.queue.max_bytes:4gb:队列存储最大数据量

线程

1.Input Thread 数据读取线程
2.Pipeline Worker Thread 数据处理线程

线程相关配置

1.pipeline.workers | -w  
<1>pipeline线程数, 即filter_output的处理线程数, 默认是CPU核数 2.pipeline.batch.size | -b  
<1>Batcher一次批量获取的待处理文档数, 默认125, 可以根据输出进行调整, 越大会占用越多的heap空间, 可以通过jvm.options进行调整 3.pipeline.batch.delay | -u  
<1>Batcher等待的时长, 单位为ms

Logstash配置文件

1.logstash设置相关的配置文件(在conf文件夹中, setting file)  <1>logstash.yml logstash相关配置, 比如node.name、path.data、pipeline.workers、queue.type等, 这其中的配置可以被命令行参数中的相关参数覆盖  
<2>jvm.options修改jvm的相关参数, 比如修改heap size等
2.pipeline配置文件  
<1>定义数据处理流程的文件, 以.conf结尾

logstash.yml

支持如下两种形式:

logstash.yml配置项

1.node.name:节点名, 便于识别
2.path.data: 持久化存储数据的文件夹, 默认是logstash home目录下的data
3.path.config:设定pipeline配置文件的目录
4.path.log:设定pipeline日志文件的目录
5.pipeline.workers:设定pipeline的线程数(filter + output), 优化的常用项
6.pipeline.batch.size/delay:设定批量处理数据的数目和延迟 7.queue.type:设定队列类型, 默认是memory
8.queue.max_bytes:队列总容量, 默认是1g

logstash命令行配置项

1.–node.name
2.-f –path.config pipeline路径, 可以是文件或者文件夹
3.-path.settings logstash配置文件夹路径, 其中包含logstash.yml
4.-e –config.string 指明pipeline内容, 多用于测试使用
5.-w –pipeline.batch.size
6.–path.data
7.–debug
8.-t –config.test_and_exit 测试

logstash配置方式建议

1.线上环境推荐采用配置文件的方式来设定logstash的相关配置, 这样可以减少犯错的机会, 而且文件便于进行版本化管理
2.命令行形式多采用来进行快速的配置测试、验证、检查等

logstash多实例运行方式

1.bin/logstash –path.settings instance1
2.bin/logstash –path.settings instance2
3.不同instance中修改logstash.yml, 自定义path.data, 确保其不同即可

pipeline配置

用于配置input、filter和output插件, 结构如下:

pipeline配置语法

1.主要有如下的数值类型:
<1>布尔类型Boolean: isFailed=>true
<2>数值类型Number: port=>33
<3>字符串类型String: name=>”Hello World”
<4>数组Array/List
users => [{id=>1, name=>bob}, {id=>2, name=>tim}]
path =>  [“/var/log/messages”, “/var/log/*.log”]
<5>哈希类型Hash
match => {   
“field1” => “value1”,
    “field2” => “value2”
}
<6>注释: #
<7>在配置中可以引用Logstash Event的属性(字段), 主要有如下两种方式:
(1)直接引用字段值;
(2)在字符串中以springf方式引用

直接引用字段值Field Reference

使用[]即可, 嵌套字段写多层[]即可

字符串中引用字段值sprintf

使用%{}来实现

pipeline判断语句

1.支持条件判断语法, 主要格式如下

2.表达式主要包含如下操作符:
<1>比较: ==、 !=、 <、>、<=、>=
<2>正则是否匹配: =~、!~
<3>包含(字符串或者数组): in、not in
<4>布尔操作符: and、or、nand、xor、!
<5>分组操作符:()

ELK: Production Environment Deployment Recommendations

系统设置

1.按照官方建议设置所有的系统参数
2.可以看官方文档Setup Elasticsearch -> Important System Configuration

关闭swap

如果内存不够运用到swap的话,会导致elasticsearch性能下降.

修改ulimit

增加虚拟内存

线程数设置

ES设置尽量简洁

1.elasticsearch.yml中尽量只写必备的参数,其他可以通过api动态设置的参数通过api来设定
2.可以参考文档Setup Elasticsearch -> Important Elasticsearch Configuration
3.不要在网络上随便复制别人的配置,因为版本不一样可能会导致不一样的结果

elasticsearch.yml中建议设定的基本参数

1.cluster.name
2.node.name
3.node.master/node.data/node.ingest
4.network.host建议显示指定为内网ip, 不要偷懒直接设为0.0.0.0 5.discover.zen.ping.unicast.hosts 设定集群其他节点地址 6.discovere.zen.minimum_master_nodes一般设定为2 7.path.data/path.log
8.除上述参数外再根据需要增加其他的静态配置参数
9.动态设定的参数有transient和persistent两种设置,前者在集群重启后会丢失,后者不会,但两种设定都会覆盖elasticsearch

关于JVM内存设定

1.不要超过31GB
2.预留一般内存给操作系统,用来做文件缓存
3.具体大小根据该node要存储的数据量来估算,为了保证性能,在内存和数据量间有一个建议的比例:  
<1>搜索类项目的比例建议在1:16以内  
<2>日志类项目的比例建议在1:48~1:96
4.假设数据总量大小为1TB, 3个node, 1个副本, 那么每个node要存储的数据量为2TB(因为有一个副本)/3=666GB, 即700GB左右, 做20%的预留空间, 每个node要存储大约850GB的数据  
<1>如果是搜索类项目,每个node内存大小为850GB/16=53GB, 大于31GB. 31 * 16 = 496GB, 即每个node最多存储496GB, 所以至少需要5个node  
<2>如果是日志类型的项目, 每个node内存大小为850GB / 48 = 18GB, 因此3个节点足够

ES写数据过程
1.refresh
2.translog
3.flush

refresh

1.segment写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性,先将segment在缓存中创建并开放查询来进一步提升实时性,该过程在es中被称为refresh
2.在refresh之前文档会先存储在一个buffer中,refresh时将buffer中的所有文档清空并生成segment
3.es默认每1秒执行一次refresh,因此文档的实时性被提高到1秒,这也是es被称为近实时(Near Real Time)的原因

translog

如果在内存中的segment还没有写入磁盘前发生了宕机,那么其中的文档就无法恢复了,如何解决这个问题?
1.es引入translog机制.写入文档到buffer时,同时将该操作写入translog. 2.translog文件会即时写入磁盘(fsync), 6.x默认每个请求都会落盘, 可以修改文每5秒写一次, 这样的风险便是丢失5秒内得数, 相关配置为index.translog.*
3.es启动时会检查translog文件, 并从中恢复

flush

flush负责将内存中的segment写入磁盘,主要做如下工作:
<1>将translog写入磁盘
<2>将index buffer清空, 其中的文档生成一个新的segment, 相当于一个refresh操作
<3>更新commit point并写入磁盘
<4>执行fsync操作, 将内存中的segment写入磁盘
<5>删除旧的translog文件

写性能优化

1.目标是增大吞吐量-EPS(Events Per Second)越高越好
2.优化方案:
<1>客户端:多线程写, 批量写
<2>ES:在高质量数据建模的前提下, 主要是在refresh、translog和flush之间做文章

写性能优化-refresh

目标为降低refresh频率
<1>增大refresh_interval, 降低实时性, 以增大一次refresh处理的文档数, 默认是1s, 设置为-1直接禁止自动refresh
<2>增大index buffer size, 参数为indices.memory.index_buffer_size(静态参数, 需要设定在elasticsearch.yml中), 默认10%

写性能优化-translog

目标是降低translog写磁盘的频率, 从而提高写效率, 但会降低容灾能力 <1>index.translog.durability设置为async, index.translog.sync_interval设置需要的大小, 比如120s, 那么translog会改成120s写一次磁盘 <2>index.translog.flush_threshold_size默认为512mb, 即translog超过该大小时会触发一次flush, 那么调大该大小可以避免flush的发生

写性能优化-flush

目标为降低flush的次数

写性能优化-其他

1.副本设置为0, 写入完毕再增加
2.合理地设计shard书, 并保证shard均匀地分配在所有的node上, 充分利用所有node的资源
<1>index.routing.allocation.total_shards_per_node限定每个索引在每个node上可分配的总主副本分片数
<2>5个node, 某索引有10个主分片, 1个副本, 上述只应该设置为多少? (10 + 10)/5 = 4, 时机要设置5个, 防止在某个node下线时1, 分片迁移失败的问题

index级别的优化

主要为index级别的设置优化, 以日志场景举例, 一般会有如下的索引设定:

读性能优化

读性能优化主要受以下几个方面影响:
<1>数据模型是否符合业务模型
<2>数据规模是否过大
<3>索引配置是否优化
<4>查询语句是否优化

读性能优化-数据建模

高质量的数据建模是优化的基础
<1>将需要通过script脚本动态计算的值提前计算好作为字段存到文档中 <2>尽量使得数据模型贴近业务模型

读性能优化-数据规模

根据不同的数据规模设定不同的SLA
<1>不同的数据规模性能肯定存在差异

读性能优化-索引配置调优

索引配置优化主要包括如下:
<1>根据数据规模设置合理的主分片数, 可以通过测试得到最合适的分片数
<2>设置合理的副本数目, 不是越多越好

读性能优化-查询语句调优

查询语句调优主要有以下几种常见手段:
<1>尽量使用Filter上下文, 减少算分的场景, 由于Filter有缓存机制, 可以极大提升查询性能
<2>尽量不使用Script进行字段计算或者算分排序等
<3>结合profile、explain API分析慢查询语句的症结所在, 然后再去优化数据模型

如何设定shard数

1.ES的性能基本是线性扩展的, 因此只要测出1个shard的性能指标, 然后根据实际性能需求就能算出需要的shard数.比如单shard写入eps是10000, 而线上eps需求是50000, 那么可以通过添加到5个shard来满足需求.
2.测试1个shard的流程如下:
<1>搭建与生产环境相同配置的单节点群
<2>设定一个单分片零副本的索引
<3>写入时机生产数据进行测试, 获取写性能指标
<4>针对数据进行查询请求, 获取读性能指标
3.压测工具可以采用esrally
4.压测流程较为复杂, 可以根据经验来设定. 如果是搜索引擎场景, 单shard大小不要超过15GB, 如果是日志场景, 单shard大小不要超过50GB(shard越大查询性能越低)
5.此时只要估算出你索引的总数据大小, 然后再除以上面的单shard大小也可以的到分片数

X-Pack Monitoring

1.在es和kibana都安装x-pack(elasticsearch.yml关闭账号密码登录, 重启es和kibana)

ELK: Data Modeling

数据建模

1.英文为Data Modeling,为创建数据模型的过程
2.数据模型(Data Model):  
<1>对现实世界进行抽象描述的一种工具和方法  
<2>通过抽象的实体及实体之间联系的形式去描述业务规则,从而实现对现实世界的映射

数据建模规则

1.概念模型:确定系统的核心需求和范围边界,设计实体和实体间的关系
2.逻辑模型:进一步梳理也无需求,确定每个实体的属性、关系和约束等
3.物理模型:  
<1>结合具体的数据库产品,在满足业务读写性能等需求的前提下确定最终的定义 <2>Mysql、MongoDB、elasticsearch等  
<3>第三范式

ES中的数据建模

ES是基于Lucene以倒排索引为基础实现的存储体系,不遵循关系型数据库中的范式约定

Mapping字段的相关设置

1.enabled:  
<1> true | false  
<2>仅存储,不做搜索或聚合分析
2.index:  
<1> true | false  
<2>是否构建倒排索引
3.index_options  
<1> docs | freqs | positions | offsets  
<2>存储倒排索引的哪些信息
4.norms  
<1>true | false  
<2>是否存储归一化相关参数, 如果字段仅用与过滤和聚合分析
5.doc_values  
<1>true | false  
<2>是否启用doc_values, 用于排序和聚合分析  
6.field_data  
<1>true | false  
<2>是否为text类型启用fielddata, 实现倒排和聚合分析
7.store  
<1>false | true  
<2>是否存储该字段值
8.coerce  
<1>true | false  
<2>是否开启自动类型转换功能, 比如字符串转为数字,浮点转为整型
9.multifields多字段  
<1>灵活使用多字段特性来解决多样的业务需求
10.dynamic  
<1>true | false | strict  
<2>控制mapping自动更新
11.date_detection  
<1>true | false  
<2>是否自动识别日期类型

Mapping字段属性的设定流程

1.是何种类型
2.是否需要检索
3.是否需要排序和聚合分析
4.是否需要另行存储

是何种类型

1.字符串类型:需要分词则设定为text类型,否则设置为keyword类型
2.枚举类型:基于性能考虑将其设定为keyword类型,即便该数据为整型
3.数值类型:尽量选择贴近的类型,比如byte即可表示所有数值时,即选用byte,不要用long
4.其他类型:比如布尔类型、日期、地理位置数据等

是否需要检索

1.完全不需要检索、排序、聚合分析的字段:enabled设置为false
2.不需要检索的字段:index设置为false
3.需要检索的字段,可以通过如下配置设定需要的存储粒度:  
<1>index_options结合需要设定  
<2>norms不需要归一化数据时即可关闭

是否需要排序和聚合分析

不需要排序或者聚合分析功能:  
<1>doc_values设定为false  
<2>fielddata设定为false

是否需要另行存储

是否需要专门存储当前字段的数据?  
<1>store设定为true,即可存储该字段的原始内容(与_source中的不想关)  
<2>一般结合_source的enabled设定为false时使用

关联关系处理

es不擅长处理关系型数据库中的关联关系,比如文章表blog与评论表comment之间通过blog_id关联,在es中可以通过如下两种手段变相解决:
<1>Nested Object
<2>Parent/Child

Nested Object方式处理关联关系

1.不指定为nested的话,将存储为Object Array
2.Nested Object Array是将每一个指定为nested类型的数据独立存储

Parent/Child方式处理关联关系

es提供了类似关系数据库中join的实现方式,使用join数据类型实现

Parent/Child常见的query语法

常见query语法包括如下几种:
1.parent_id返回某父文档的子文档
2.has_child返回包含某子文档的父文档
3.has_parent返回包含某父文档的子文档

parent_id

parent_id查询:返回某父文档的子文档

has_child

返回包含某子文档的父文档

has_parent

has_parent查询:返回包含某父文档的子文档

Nested Object vs Parent/Child

Nested Object:
优点:文档存储在一起,因此可读性能高
缺点:更新父或子文档时需要更新整个文档
场景:子文档偶尔更新,查询频繁
Parent/Child:
优点:父子文档可以独立更新,互补影响
缺点:为了维护join的关系,需要占用部分内存,可读性能较差
场景:子文档更新频繁

Reindex

1.指重建所有数据的过程,一般发生在如下情况:  
<1>mapping设置变更,比如字段类型变化、分词器字典更新等  
<2>index设置变更,比如分片数更改等  
<3>迁移数据
2.es提供了现成的API用于完成该工作  
<1>_update_by_query在现有索引上重建  
<2>_reindex在其他索引上重建

Reindex-_update_by_query

Reindex-_reindex

Reindex-Task

1.数据重建的时间受源索引文档规模的影响,当规模越大时,所需时间越多,此时需要通过设定url参数wait_for_completion为false来异步执行,ES以task来描述此类执行任务
2.ES提供了Task API来查看任务的执行进度和相关数据

数据模型版本管理

对Mapping进行版本管理
<1>包含在代码或者以专门的文件进行管理,添加好注释,并加入Git等版本管理仓库中,方便回顾
<2>为每个增加一个metadata字段,在其中维护一些文档相关的元数据,方便对数据进行管理

防止字段过多

1.字段过多主要有如下的坏处: 
<1>南与维护,当字段成千上百时,基本很难有人能明确知道每个字段的含义 
<2>mapping的信息存储在cluster state里面,过多的字段会导致mapping过大,最终导致更新变慢
2.通过设置index.mapping.total_fields.limit可以限定索引中最大字段数,默认是1000
3.可以通过key/value的方式解决字段过多的问题,但并不完美
4.一般字段过多的原因是由于没有高质量的数据建模导致的,比如dynamic设置为true
5.考虑拆分成多个索引来解决问题

Key/Value方式详解

1.可以极大减少Field数目,但也有一些明显的坏处 
<1>query语句复杂度飙升,且有一些可能无法实现,比如聚合分析相关的 
<2>不利于在Kibana中做可视化分析

ELK: Aggregate Analysis

什么是聚合分析?

聚合分析,英文为Aggregation,是es除搜索功能外提供的针对es数据做统计分析的功能  
<1>功能丰富,提供Bucket、Metric、Pipeline等多种分析方式,可以满足大部分的分析需求  
<2>实时性高,所有的计算结果都是即时返回的,而hadoop等数据系统一般都是T+1级别的

聚合分析-分类

es将聚合分析主要分成如下4类  
<1>Bucket,分桶类型,类似SQL中的GROUP BY语法  
<2>Mertic,指标分析类型,如计算最大值、最小值、平均值等等 <3>Pipeline,管道分析类型,基于上一级的聚合分析结果进行再分析 <4>Matrix,矩阵分析类型

Metric聚合分析

主要分为两类:
1.单值分析,只输出一个分析结果  
<1>min, max, avg, sum  
<2>cardinality
2.多值分析,输出多个分析结果  
<1>stats, extended stats  
<2>percentile, percentile rank  
<3>top hits

Metric聚合分析-Min, Max, Avg, Sum

Metric聚合分析-Cardinality

Cardinality意味集合的势,或者基数,指不同数值的的个数,类似SQL中的dintinct count

Metric聚合分析-Stats

返回一系列数值类型的统计值,包含min, max, avg, sum和count

Metric聚合分析-Extended Stats

对stats的扩展,包括了更多的统计数据,如方差、标准差等

Metric聚合分析-Percentile

Metric聚合分析-Percentile Rank

Metric聚合分析-Top Hits

一般用于分桶后获取该桶内最匹配的顶部文档列表,即详情数据

Bucket聚合分析

1.Bucket,意为桶,即按照一定的规则将文档分配到不同的桶中,达到分类分析的目的
2.按照Bucket的分桶策略,常见的Bucket聚合分析如下  
<1>Terms  
<2>Range  
<3>Date Range  
<4>Histogram  
<5>Date Histogram

Bucket聚合分析-Terms

该分同策略最简单,直接按照term来分桶,如果是text类型,则按照分词后的结果分桶

Bucket聚合分析-Range

通过制定数值的范围来设定分桶规则

Bucket聚合分析-Date Range

通过指定日期的返回来设定分桶规则

Bucket聚合分析-Histogram

直方图,以固定间隔的策略来分割数据

Bucket聚合分析-Date Histogram

针对日期的直方图或者柱状图,是时序数据分析中常用的聚合分析类型

Bucket和Metric聚合分析

Bucket聚合分析允许通过添加子分析来进一步进行分析,该子分析可以是Bucket也可以是Metric.使得es的聚合分析可以变得异常强大.  
<1>分桶之后再分桶:

<2>分桶之后再进行数据分析

Pipeline聚合分析

1.针对聚合分析的结果再进行聚合分析,而且支持链式调用
2.Pipeline的分析结果会输出到原结果中,根据输出位置的不同,分为以下两类:  
<1>Parent结果内嵌到现有的聚合分析结果中:Derivative, Moving Average, Cumulative Sum  
<2>Sibling结果与现有聚合分析结果同级:Max/Min/Avg/Sum Bucket, Stats/Extended Stats Bucket, Percentiles Bucket

Pipeline聚合分析Sibling-Min Bucket

找出所有Bucket中值最小的Bucket名称和值

Pipeline聚合分析Sibling-Max Bucket

找出所有Bucket中值最大的Bucket名称和值

Pipeline聚合分析Sibling-Percentiles Bucket

计算Bucket值得百分位数

Pipeline聚合分析Parent-Derivative

计算Bucket值得导数

Pipeline聚合分析Parent-Moving Average

计算Bucket值的移动平均值

Pipeline聚合分析Parent-Cumulative Sum

计算Bucket值得累积加和

作用范围

es聚合分析默认作用范围是query的结果集,可以通过如下的方式改变其作用范围:
1.filter
2.post_filter
3.global

作用范围-filter

为某个聚合分析设定过滤条件,从而在不更改整体query语句的情况下修改了作用范围:

作用范围-post fileter

作用于文档过滤,但在聚合分析后生效

作用范围-global

无视query过滤条件,基于全部文档进行分析

排序

可以使用自带的关键数据进行排序,比如:
<1>_count文档数
<2>_key按照key值排序

Min聚合的执行流程

假设有主分片P0, P1, P2,则首先从每个主分片获取最小的值,然后在通过coordinating node将从每个节点最小值中取最小的返回。

Terms聚合的执行流程

假设有主分片P0, P1, P2,则从每个分片中获取前5个document,然后通过coordinating node, coordinating node对返回的document进行排序后返回前5个document给用户

Terms不准确问题

设P1有a(5),b(4),c(3),d(4), P2有a(5),b(2),c(3),d(1),则返回P1有a(5), b(4), d(4), P2有a(5), c(3), b(2), 得到的结果为a(10), b(6), d(4), 导致原本c(6)丢失。
Terms并不是永远准确, 数据分析在多个shard上, coordinating node无法获取数据的全貌.

解决Terms不准确问题
1.设置Shard数为1,消除数据分散的问题,但无法承载大数据量
2.合理设置Shard_Size大小,即每次从Shard上多获取额外数据,以提升准确度

Shard_Size大小设定的方法

terms聚合返回结果中有如下两个统计值: 1.doc_count_error_upper_bound被遗漏的term可能的最大值 2.sum_other_doc_count返回结果bucket的term外其他term的文档数 设定show_term_doc_count_error可以查看每个每个bucket误算的最大值

3.shard_size默认大小如下:shard_size=(size * 1.5) + 10 通过调整4.shard_size的大小降低doc_count_error_upper_bound来提升准确度:增大了整体的计算量,从而降低了响应时间

近似统计算法

在es的聚合分析中,Cardinality和Percentile分析使用的是近似统计算法 <1>结果是近似准去的,但不一定精准
<2>可以通过参数的调整使其结果精准,但同时也意味着更多的计算时间和更大的性能消耗

ELK: Search

Search的运行机制

1.Search执行的时候实际分两个步骤运作:  
<1>Query阶段  
<2>Fetch阶段
2.Query-Then-Fetch

Query阶段

设当前有node1有分片P0,R1; node2有分片P1,R2; node3有分片P2,R0
1.node3在接受到用户的search请求后,会先进行Query阶段(此时是Coordinating Node角色)
2.node3在6个主副分片中随机选择3个分片,发送search request
3.被选中的3个分片会分别执行查询并排序,返回from+size个文档Id和排序值
4.node3整合3个分片返回的from+size个文档Id,根据排序值排序后选取的from到from+size的文档Id

Fetch阶段

设当前有node1有分片P0,R1; node2有分片P1,R2; node3有分片P2,R0 1.node3根据Query阶段获取的文档Id列表去对应的shard上获取文档详情数据
2.node3向相关的分片发送multi_get请求
3.node3拼接返回的结果并返回给客户

相关性算分

1.相关性算分在shard与shard间是相互独立的,也就意味着同一个Term的IDF等值在不同的shard上是不同的.文档的相关性算分和它所处的shard相关
2.在文档数量不多的时,会导致相关性算分严重不准的情况发生

解决相关性算分在不同Shard导致分数不同的问题

1.设置分片数为1个,从根本上排除问题,在文档数量不多的时候可以考虑该方案,比如百万到千万级别的文档数量
2.使用DFS Query-then-Fetch查询方式

DFS Query-then-Fetch

DFS Query-then-Fetch是在拿到所有文档后再重新完整的计算一次相关性算分,耗费更多的cpu和内存,执行性能也比较低下,一般不建议使用.使用方式如下:

排序

es默认会采用相关性算分排序,用户可以通过设定sorting参数来自行设定排序规则

字符串排序

1.排序的过程实质是对字段原始内容排序的过程,这个过程中倒排索引无法发挥作用,需要用到正排索引,也就是通过文档Id和字段可以快速的到原始字段内容
2.es对此提供了2种实现方式:  
<1>fielddata默认禁用  
<2>doc values默认启用,除了text类型

FieldData VS DocValues

Fielddata:
创建时机:搜索时即时创建
创建位置:JVM Heap
优点:不会占用额外的磁盘资源
缺点:文档过多时,即时创建会花过多时间,占用过多Heap内存 DocValues:
创建时机:索引是创建,与倒排索引创建时机一致
创建位置:磁盘
优点:不会占用Heap内存
缺点:减慢索引的速度,占用额外的磁盘资源

Fielddata

Fielddata默认是关闭的,可以通过api开启:
<1>此时字符串是按照分词后的term排序,往往很难符合预期
<2>一般是对分词做聚合分析的时候开启
<3>fielddata只针对text类型有效果

DocValues

DocValues默认是启用的,可以在创建索引的时候关闭: <1>如果后面要在开启doc values,需要做reindex操作

docvalue_fields

可以通过该字段获取fielddata或者doc values中存储的内容

分页与遍历

es提供了3中方式来解决分页与遍历的问题:
1.from/size
2.scroll
3.search_after

from/size

1.最常用的分页方案  
<1>from 指明开始位置  
<2>size 指明获取总数

2.深度分页是一个经典的问题:在数据分片存储的情况下如何获取前1000个文档?  
<1>获取从990~1000的文档时,会在每个分片上都会获取1000个文档,然后再由Coordinating Node聚合所有分片的结果后再排序选取前1000个文档  
<2>页数越深,处理文档越多,占用内存越多,耗时越长.尽量避免深度分页,es通过index.max_result_window限定最多到10000条数据

scroll

1.遍历文档集的api,以快照的方式来避免深度分页的问题  
<1>不能用来做实时搜索,因为数据不是实时的  
<2>尽量不要使用复杂的sort条件,使用_doc最高效  
<3>使用稍显复杂

scroll应用

<1>第一步需要发起1个scroll search,如下所示:

<2>第二部调用scroll search的api,获取文档集合,如下所示:

scroll缺点

search_after

1.避免深度分页的性能问题,提供实时的下一页文档获取功能  
<1>缺点是不能使用from参数,即不能指定页数  
<2>只能下一页,不能上一页  
<3>使用简单

search_after使用方法

1.第一步为正常的搜索,但要指定sort值,并保证值唯一
2.第二步为使用上一步最后一个文档的sort值进行查询

search_after如何避免深度分页

1.通过唯一排序值定位将每次要处理的文档数都控制在size内
2.缺点:只能获取下一页,不能获取指定的页数

应用场景:

from/size:需要实时获取顶部的部分文档,且需要自由翻页
scroll:需要全部文档,如导出所有数据的功能
search_after:需要全部文档,不需要自由翻页