ElasticSearch

ES 的整体存储架构图

在这里插入图片描述

索引是什么

ES集群包含多个索引,每个索引包含一种表,表包含多个文档,并且每个文档包含不同的属性。

请解释什么是分片(SHARDs)

随着索引文件的增加,磁盘容量、处理能力都会变得不够,在这种情况下,将索引数据切分成小段,这就叫分片(SHARDS)。它的出现大大改进了数据查询的效率。

什么是副本(REPLICA),

副本是分片的完整拷贝,副本的作用是增加了查询的吞吐率和在极端负载情况下获得高可用的能力。副本有效的帮助处理用户请求。
所谓高可用主要指:如果某主分片1出了问题,对应的副本分片1会提升为主分片,保证集群的高可用。

ES支持哪些类型的查询

主要分为匹配(文本)查询和基于Term的查询。

文本查询包括基本匹配,match phrase, multi-match, match phrase prefix, common terms, query-string, simple query string.

Term查询,比如term exists, type, term set, range, prefix, ids, wildcard, regexp, and fuzzy。

elasticsearch的倒排索引是什么

传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。
而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。
其中词典中存储词元,倒排表中存储该词元在哪些文中出现的位置。有了倒排索引,就能实现o(1)时间复杂度的效率检索文章了,极大的提高了检索效率。
在这里插入图片描述
学术的解答方式:

倒排索引,相反于一篇文章包含了哪些词,它从词出发,记载了这个词在哪些文档中出现过,
由两部分组成——词典和倒排表。

加分项:倒排索引的底层实现是基于:FST(Finite State Transducer)数据结构。

lucene从4+版本后开始大量使用的数据结构是FST。FST有两个优点:

  • 空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;

  • 查询速度快。O(len(str))的查询时间复杂度。

Elasticsearch索引文档的过程

这里的索引文档应该理解为文档写入ES,创建索引的过程。

文档写入包含:单文档写入和批量bulk写入,这里只解释一下:单文档写入流程。

记住官方文档中的这个图。

在这里插入图片描述
第一步:客户写集群某节点写入数据,发送请求。(如果没有指定路由/协调节点,请求的节点扮演路由节点的角色。)

第二步:协调节点接受到请求后,默认使用文档_id参与计算来确定文档属于哪个分片(也支持通过 routing)。请求会被转到另外的节点,假定节点3。因此分片0的主分片分配到节点3上。

bash# 路由算法:根据文档id或路由计算目标的分片id
shard = hash(document_id) % (num_of_primary_shards)

第三步:当分片所在的节点接收到来自协调节点的请求后,会将请求写入到 Memory Buffer(区域默认大小10% 堆内存 size),然后定时(默认是每隔 1 秒)将 memory buffer 中的数据写入一个新的 segment 段文件中,并写入到F ilesystem Cache,这个从 Momery Buffer 到 Filesystem Cache 的过程就叫做 refresh;每个 Segment 段文件实际上是一些倒排索引的集合, 只有经历了 refresh 操作之后到达了Filesystem Cache中,这些数据才能变成可检索的。

第四步:当然在某些情况下,存在 Memery Buffer 和 Filesystem Cache 的数据可能会丢失,ES 是通过 translog 的机制来保证数据的可靠性的。
其实现机制是接收到请求后,同时也会写入到 translog事务日志文件 中,当 Filesystem cache 中的数据写入到磁盘中时,才会清除掉,这个过程
叫做 flush;在机器宕机重启时,es 会从磁盘中读取 translog 事务日志文件中最后一个提交点 commit point 之后的数据,恢复到 memory buffer
和 Filesystem cache 中去。

注:ES 数据丢失的问题:translog 也是先写入 Filesystem cache,然后默认每隔 5 秒刷一次到磁盘中,所以默认情况下,可能有 5 秒的数据会仅仅停留在 memory buffer 或者 translog 事务日志文件的 Filesystem cache中,而不在磁盘上,如果此时机器宕机,会丢失 5 秒钟的数据。
也可以将 translog 设置成每次写操作必须是直接 fsync 到磁盘,但是性能会差很多。

第五步:在 flush 过程中,内存中的缓冲将被清除,内容被写入一个新Segment段,Segment段的 fsync 将创建一个新的提交点,同时强行将 Filesystem Cache 中目前所有的数据都 fsync 到磁盘文件中,旧的 translog 将被删除并开始一个新的 translog。

注:ES 的 flush 操作主要通过以下几个参数控制:

index.translog.flush_threshold_period:每隔多长时间执行一次flush,默认30m
index.translog.flush_threshold_size:当事务日志大小到达此预设值,则执行flush,默认512mb
index.translog.flush_threshold_ops:当事务日志累积到多少条数据后flush一次。

第六步:flush 触发的时机是定时触发(默认 30 分钟)或者 translog 变得太大(默认为 512 M)时。
在这里插入图片描述
补充:关于 Lucene 的 Segement

  • Lucene 索引是由多个段组成,段本身是一个功能齐全的倒排索引。
  • 段是不可变的,允许 Lucene 将新的文档增量地添加到索引中,而不用从头重建索引。
  • 对于每一个搜索请求而言,索引中的所有段都会被搜索,并且每个段会消耗 CPU 的时钟周、文件句柄和内存。这意味着段的数量越多,搜索性能会越低。
  • 为了解决这个问题,Elasticsearch 会合并小段到一个较大的段,提交新的合并段到磁盘,并删除那些旧的小段。(段合并)

如果面试官再问:第二步中的文档获取分片的过程?

会借助路由算法获取,路由算法就是根据路由和文档id计算目标的分片id的过程。

shard = hash(_routing) % (num_of_primary_shards)或者shard = hash(document_id) % (num_of_primary_shards)

segment文件的合并流程

当我们往 ElasticSearch 写入数据时,数据是先写入 memory buffer,然后定时(默认每隔1s)将 memory buffer 中的数据写入一个新的 segment 文件中,并进入 Filesystem cache(同时清空 memory buffer),这个过程就叫做 refresh;每个 Segment 事实上是一些倒排索引的集合, 只有经历了 refresh 操作之后,数据才能变成可检索的。
ElasticSearch 每次 refresh 一次都会生成一个新的 segment 文件,这样下来 segment 文件会越来越多。那这样会导致什么问题呢?因为每一个 segment 都会占用文件句柄、内存、cpu资源,更加重要的是,每个搜索请求都必须访问每一个segment,这就意味着存在的 segment 越多,搜索请求就会变的更慢。
每个 segment 是一个包含正排(空间占比90~95%)+ 倒排(空间占比5~10%)的完整索引文件,每次搜索请求会将所有 segment 中的倒排索引部分加载到内存,进行查询和打分,然后将命中的文档号拿到正排中召回完整数据记录。如果不对segment做配置,就会导致查询性能下降
那么 ElasticSearch 是如何解决这个问题呢? ElasticSearch 有一个后台进程专门负责 segment 的合并,定期执行 merge 操作,将多个小 segment 文件合并成一个 segment,在合并时被标识为 deleted 的 doc(或被更新文档的旧版本)不会被写入到新的 segment 中。合并完成后,然后将新的 segment 文件 flush 写入磁盘;然后创建一个新的 commit point 文件,标识所有新的 segment 文件,并排除掉旧的 segement 和已经被合并的小 segment;然后打开新 segment 文件用于搜索使用,等所有的检索请求都从小的 segment 转到 大 segment 上以后,删除旧的 segment 文件,这时候,索引里 segment 数量就下降了。如下面两张图:
所有的过程都不需要我们干涉,es会自动在索引和搜索的过程中完成,合并的segment可以是磁盘上已经commit过的索引,也可以在内存中还未commit的segment:合并的过程中,不会打断当前的索引和搜索功能。
在这里插入图片描述

segment 的 merge 对性能的影响

segment 合并的过程,需要先读取小的 segment,归并计算,再写一遍 segment,最后还要保证刷到磁盘。可以说,合并大的 segment 需要消耗大量的 I/O 和 CPU 资源,同时也会对搜索性能造成影响。所以 Elasticsearch 在默认情况下会对合并线程进行资源限制,确保它不会对搜索性能造成太大影响。

默认情况下,归并线程的限速配置 indices.store.throttle.max_bytes_per_sec 是 20MB。对于写入量较大,磁盘转速较高,甚至使用 SSD 盘的服务器来说,这个限速是明显过低的。对于 ELK Stack 应用,建议可以适当调大到 100MB或者更高。设置方式如下:

PUT /_cluster/settings
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "100mb"
    }
}

或者不限制:

PUT /_cluster/settings
{
    "transient" : {
        "indices.store.throttle.type" : "none" 
    }
}

手动强制合并 segmen

ES 的 API 也提供了命令来支持强制合并 segment,即 optimize 命令,它可以强制一个分片 shard 合并成 max_num_segments 参数指定的段数量,一个索引它的segment数量越少,它的搜索性能就越高,通常会optimize 成一个 segment。

但需要注意的是,optimize 命令是没有限制资源的,也就是你系统有多少IO资源就会使用多少IO资源,这样可能导致一段时间内搜索没有任何响应,所以,optimize命令不要用在一个频繁更新的索引上面,针对频繁更新的索引es默认的合并进程就是最优的策略。如果你计划要 optimize 一个超大的索引,你应该使用 shard allocation(分片分配)功能将这份索引给移动到一个指定的 node 机器上,以确保合并操作不会影响其他的业务或者es本身的性能。

但是在特定场景下,optimize 也颇有益处,比如在一个静态索引上(即索引没有写入操作只有查询操作)是非常适合用optimize来优化的。比如日志的场景下,日志基本都是按天,周,或者月来索引的,旧索引实质上是只读的,只要过了今天、这周或这个月就基本没有写入操作了,这个时候我们就可以通过 optimize 命令,来强制合并每个shard上索引只有一个segment,这样既可以节省资源,也可以大大提升查询性能。

optimize 的 API 如下:

POST /logstash-2014-10/_optimize?max_num_segments=1

segment 性能相关设置

1、查看某个索引中所有 segment 的驻留内存情况:

curl -XGET 'http://host地址:port端口/_cat/segments/索引节点名称?v&h=shard,segment,size,size.memory'

2、性能优化:

(1)合并策略:

合并线程是按照一定的运行策略来挑选 segment 进行归并的。主要有以下几条:

① index.merge.policy.floor_segment:默认 2MB,小于该值的 segment 会优先被归并。
② index.merge.policy.max_merge_at_once:默认一次最多归并 10 个 segment
③ index.merge.policy.max_merge_at_once_explicit:默认 forcemerge 时一次最多归并 30 个 segment
④ index.merge.policy.max_merged_segment:默认 5 GB,大于该值的 segment,不用参与归并,forcemerge 除外
(2)设置延迟提交:

根据上面的策略,我们也可以从另一个角度考虑如何减少 segment 归并的消耗以及提高响应的办法:加大 refresh 间隔,尽量让每次新生成的 segment 本身大小就比较大。这种方式主要通过延迟提交实现,延迟提交意味着数据从提交到搜索可见有延迟,具体需要结合业务配置,默认值1s;

针对索引节点粒度的配置如下:

curl -XPUT http://host地址:port端口/索引节点名称/_settings -d '{"index.refresh_interval":"10s"}'

(3)对特定字段field禁用 norms 和 doc_values 和 stored:

norms、doc_values 和 stored 字段的存储机制类似,每个 field 有一个全量的存储,对存储浪费很大。如果一个 field 不需要考虑其相关度分数,那么可以禁用 norms,减少倒排索引内存占用量,字段粒度配置 omit_norms=true;如果不需要对 field 进行排序或者聚合,那么可以禁用 doc_values 字段;如果 field 只需要提供搜索,不需要返回则将 stored 设为 false;

ES更新和删除文档的过程

1、 删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;

2、 磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。

3、 在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

ES集群中添加或创建索引的过程

  • 要添加新索引,应使用创建索引 API 选项。创建索引所需的参数是索引的配置Settings,索引中的字段 Mapping 以及索引别名 Alias。
  • 也可以通过模板 Template 创建索引。
  • 可以在Kibana中配置新的索引,进行Fields Mapping,设置索引别名。

ES是如何实现 master 选举的

面试官:想了解 ES 集群的底层原理,不再只关注业务层面了。
前置前提:
1、 只有候选主节点(master:true)的节点才能成为主节点。
2、 满足最小主节点数(min_master_nodes),其目的是防止脑裂。
Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分;
获取主节点的核心入口为 findMaster,选择主节点成功返回对应 Master,否则返回 null。
选举流程大致描述如下:
第一步:根据elasticsearch.yml 设置的值最小投票选举数量discovery.zen.minimum_master_nodes确认候选主节点数是否达标
第二步:比较:先判定是否具备 master 资格,具备候选主节点资格的优先返回;
若两节点都为候选主节点,则 id 小的值会主节点。

  • 注意这里的 id 为 string 类型。
  • master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http 功能。
  • 题外话:获取节点 id 的方法。
    1GET /_cat/nodes?v&h=ip,port,heapPercent,heapMax,id,name 2ip port heapPercent heapMax id name

并发下,ES如果保证读写一致

1、 可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;

2、 另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。

3、 对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。

ES读取数据

使用RestFul API向对应的node发送查询请求,协调节点根据文档id来判断在哪个shard上,返回的是primary和replica的所有node节点集合

这样会负载均衡地把查询发送到对应节点,之后对应节点接收到请求,将document数据返回协调节点,协调节点把document排序聚合后返回给客户端
在这里插入图片描述

ES 各种类型的分析器

在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。 分析器由一个Tokenizer和零个或多个TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称下注册分析器,然后可以在映射定义或某些API中引用它们。

Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分析器。

Elasticsearch Analyzer 的类型为内置分析器和自定义分析器。

  • Standard Analyzer
    标准分析器是默认分词器,如果未指定,则使用该分词器。
    它基于Unicode文本分割算法,适用于大多数语言。

  • Whitespace Analyzer
    基于空格字符切词。

  • Stop Analyzer
    在simple Analyzer的基础上,移除停用词。

  • Keyword Analyzer
    不切词,将输入的整个串一起返回。
    自定义分词器的模板
    自定义分词器的在Mapping的Setting部分设置:
    PUT my_custom_index { “settings”:{ “analysis”:{ “char_filter”:{}, “tokenizer”:{}, “filter”:{}, “analyzer”:{} } } }
    脑海中还是上面的三部分组成的图示。其中:
    “char_filter”:{},——对应字符过滤部分;
    “tokenizer”:{},——对应文本切分为分词部分;
    “filter”:{},——对应分词后再过滤部分;
    “analyzer”:{}——对应分词器组成部分,其中会包含:1. 2. 3。

解释Elasticsearch Node

节点是 Elasticsearch 的实例。实际业务中,我们会说:ES集群包含3个节点、7个节点。

这里节点实际就是:一个独立的 Elasticsearch 进程,一般将一个节点部署到一台独立的服务器或者虚拟机、容器中。

不同节点根据角色不同,可以划分为:

主节点

帮助配置和管理在整个集群中添加和删除节点。

数据节点

存储数据并执行诸如CRUD(创建/读取/更新/删除)操作,对数据进行搜索和聚合的操作。

1、 客户端节点(或者说:协调节点) 将集群请求转发到主节点,将与数据相关的请求转发到数据节点

2、 摄取节点

用于在索引之前对文档进行预处理。

解释 Elasticsearch的 NRT

从文档索引(写入)到可搜索到之间的延迟默认一秒钟,因此Elasticsearch是近实时(NRT)搜索平台。

也就是说:文档写入,最快一秒钟被索引到,不能再快了。

写入调优的时候,我们通常会动态调整:refresh_interval = 30s 或者更大值,以使得写入数据更晚一点时间被搜索到。

ES的 document设计

在使用es时 避免使用复杂的查询语句(Join 、聚合),就是在建立索引时,就根据查询语句建立好对应的元数据。

客户端在和集群连接时,是如何选择特定的节点执行请求的

TransportClient利用transport模块远程连接一个ElasticSearch集群。它并不加入到集群中,只是简单的获得一个或者多个初始化的transport地址,并以轮询的方式与这些地址进行通信。

ES中的数据存储功能吗

Elasticsearch是一个搜索引擎,输入写入ES的过程就是索引化的过程,数据按照既定的 Mapping 序列化为Json 文档实现存储。

Master节点和候选Master节点的区别

主节点负责集群相关的操作,例如创建或删除索引,跟踪哪些节点是集群的一部分,以及决定将哪些分片分配给哪些节点。

拥有稳定的主节点是衡量集群健康的重要标志。

而候选主节点是被选具备候选资格,可以被选为主节点的那些节点。

介绍下你们电商搜索的整体技术架构

在这里插入图片描述

对于GC方面,在使用ES时要注意什么

1、 SEE

2、 倒排词典的索引需要常驻内存,无法 GC,需要监控 data node 上 segmentmemory 增长趋势。

3、 各类缓存,field cache, filter cache, indexing cache, bulk queue 等等,要设置合理的大小,并且要应该根据最坏的情况来看 heap 是否够用,也就是各类缓存全部占满的时候,还有 heap 空间可以分配给其他任务吗?避免采用 clear cache等“自欺欺人”的方式来释放内存。

4、 避免返回大量结果集的搜索与聚合。确实需要大量拉取数据的场景,可以采用scan & scroll api 来实现。

5、 cluster stats 驻留内存并无法水平扩展,超大规模集群可以考虑分拆成多个集群通过 tribe node 连接。

6、 想知道 heap 够不够,必须结合实际应用场景,并对集群的 heap 使用情况做持续的监控。

字典树

常用字典数据结构如下所示:
在这里插入图片描述
Trie 的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。它有 3 个基本性质:

1、根节点不包含字符,除根节点外每一个节点都只包含一个字符。

2、从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。

3、每个节点的所有子节点包含的字符都不相同。
在这里插入图片描述
1、可以看到,trie 树每一层的节点数是 26^i 级别的。所以为了节省空间,我们还可以用动态链表,或者用数组来模拟动态。而空间的花费,不会超过单词数×单词长度。

2、实现:对每个结点开一个字母集大小的数组,每个结点挂一个链表,使用左儿子右兄弟表示法记录这棵树;

3、对于中文的字典树,每个节点的子节点用一个哈希表存储,这样就不用浪费太大的空间,而且查询速度上可以保留哈希的复杂度 O(1)。

拼写纠错是如何实现的

1、 拼写纠错是基于编辑距离来实现;编辑距离是一种标准的方法,它用来表示经过插入、删除和替换操作从一个字符串转换到另外一个字符串的最小操作步数;

2、 编辑距离的计算过程:比如要计算batyu和beauty的编辑距离,先创建一个7×8的表(batyu长度为5,coffee长度为6,各加2),接着,在如下位置填入黑色数字。其他格的计算过程是取以下三个值的最小值:

如果最上方的字符等于最左方的字符,则为左上方的数字。否则为左上方的数字+1。(对于3,3来说为0)

左方数字+1(对于3,3格来说为2)

上方数字+1(对于3,3格来说为2)

最终取右下角的值即为编辑距离的值3。
在这里插入图片描述
对于拼写纠错,我们考虑构造一个度量空间(Metric Space),该空间内任何关系满足以下三条基本条件:

d(x,y) = 0 – 假如x与y的距离为0,则x=y

d(x,y) = d(y,x) – x到y的距离等同于y到x的距离

d(x,y) + d(y,z) >= d(x,z) – 三角不等式

1、 根据三角不等式,则满足与query距离在n范围内的另一个字符转B,其与A的距离最大为d+n,最小为d-n。

2、 BK树的构造就过程如下:每个节点有任意个子节点,每条边有个值表示编辑距离。所有子节点到父节点的边上标注n表示编辑距离恰好为n。比如,我们有棵树父节点是”book”和两个子节点”cake”和”books”,”book”到”books”的边标号1,”book”到”cake”的边上标号4。从字典里构造好树后,无论何时你想插入新单词时,计算该单词与根节点的编辑距离,并且查找数值为d(neweord, root)的边。递归得与各子节点进行比较,直到没有子节点,你就可以创建新的子节点并将新单词保存在那。比如,插入”boo”到刚才上述例子的树中,我们先检查根节点,查找d(“book”, “boo”) = 1的边,然后检查标号为1的边的子节点,得到单词”books”。我们再计算距离d(“books”, “boo”)=2,则将新单词插在”books”之后,边标号为2。

3、 查询相似词如下:计算单词与根节点的编辑距离d,然后递归查找每个子节点标号为d-n到d+n(包含)的边。假如被检查的节点与搜索单词的距离d小于n,则返回该节点并继续查询。比如输入cape且最大容忍距离为1,则先计算和根的编辑距离d(“book”, “cape”)=4,然后接着找和根节点之间编辑距离为3到5的,这个就找到了cake这个节点,计算d(“cake”, “cape”)=1,满足条件所以返回cake,然后再找和cake节点编辑距离是0到2的,分别找到cape和cart节点,这样就得到cape这个满足条件的结果。
在这里插入图片描述

ES在部署时,对Linux的设置有哪些优化方法

1、 64 GB 内存的机器是非常理想的, 但是32 GB 和16 GB 机器也是很常见的。少于8 GB 会适得其反。

2、 如果你要在更快的 CPUs 和更多的核心之间选择,选择更多的核心更好。多个内核提供的额外并发远胜过稍微快一点点的时钟频率。

3、 如果你负担得起 SSD,它将远远超出任何旋转介质。 基于 SSD 的节点,查询和索引性能都有提升。如果你负担得起,SSD 是一个好的选择。

4、 即使数据中心们近在咫尺,也要避免集群跨越多个数据中心。绝对要避免集群跨越大的地理距离。

5、 请确保运行你应用程序的 JVM 和服务器的 JVM 是完全一样的。 在 Elasticsearch 的几个地方,使用 Java 的本地序列化。

6、 通过设置gateway.recover_after_nodes、gateway.expected_nodes、gateway.recover_after_time可以在集群重启的时候避免过多的分片交换,这可能会让数据恢复从数个小时缩短为几秒钟。

7、 Elasticsearch 默认被配置为使用单播发现,以防止节点无意中加入集群。只有在同一台机器上运行的节点才会自动组成集群。最好使用单播代替组播。

8、 不要随意修改垃圾回收器(CMS)和各个线程池的大小。

9、 把你的内存的(少于)一半给 Lucene(但不要超过 32 GB!),通过ES_HEAP_SIZE 环境变量设置。

10、 内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上,一个 100 微秒的操作可能变成 10 毫秒。 再想想那么多 10 微秒的操作时延累加起来。 不难看出 swapping 对于性能是多么可怕。

11、 Lucene 使用了_大量的_文件。同时,Elasticsearch 在节点和 HTTP 客户端之间进行通信也使用了大量的套接字。 所有这一切都需要足够的文件描述符。你应该增加你的文件描述符,设置一个很大的值,如 64,000。

12、关闭缓存swap

13、堆内存设置为:Min(节点内存/2, 32GB)

14、设置最大文件句柄数

15、线程池+队列大小根据业务需要做调整

16、磁盘存储raid方式——存储有条件使用RAID10,增加单节点性能以及避免单节点存储故障

补充:索引阶段性能提升方法

1、 使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点。

2、 存储:使用 SSD

3、 段和合并:Elasticsearch 默认值是 20 MB/s,对机械磁盘应该是个不错的设置。如果你用的是 SSD,可以考虑提高到 100–200 MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加 index.translog.flush_threshold_size 设置,从默认的 512 MB 到更大一些的值,比如 1 GB,这可以在一次清空触发的时候在事务日志里积累出更大的段。

4、 如果你的搜索结果不需要近实时的准确度,考虑把每个索引的index.refresh_interval 改到30s。

5、 如果你在做大批量导入,考虑通过设置index.number_of_replicas: 0 关闭副本。

ES如何避免脑裂

所谓集群脑裂,是指 Elasticsearch 集群中的节点(比如共 20 个),其中的 10 个选了一个 master,另外 10 个选了另一个 master 的情况。

  • 当集群 master 候选数量不小于 3 个时,可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes)超过所有候选节点一半以上来解决脑裂问题;
  • 当候选数量为两个时,只能修改为唯一的一个 master 候选,其他作为 data 节点,避免脑裂问题。

ES对于大数据量(上亿量级)的聚合如何实现

Elasticsearch 提供的首个近似聚合是cardinality 度量。它提供一个字段的基数,即该字段的distinct或者unique值的数目。它是基于HLL算法的。HLL 会先对我们的输入作哈希运算,然后根据哈希运算的结果中的 bits 做概率估算从而得到基数。其特点是:可配置的精度,用来控制内存的使用(更精确 = 更多内存);小的数据集精度是非常高的;我们可以通过配置参数,来设置去重需要的固定内存使用量。无论数千还是数十亿的唯一值,内存使用量只与你配置的精确度相关。

ES数据预热

假如说,哪怕是你就按照上述的方案去做了,es 集群中每个机器写入的数据量还是超过了 filesystem cache 一倍,比如说你写入一台机器 60G 数据,结果 filesystem cache 就 30G,还是有 30G 数据留在了磁盘上。

其实可以做数据预热。

举个例子,拿微博来说,你可以把一些大V,平时看的人很多的数据,你自己提前后台搞个系统,每隔一会儿,自己的后台系统去搜索一下热数据,刷到 filesystem cache 里去,后面用户实际上来看这个热数据的时候,他们就是直接从内存里搜索了,很快。

或者是电商,你可以将平时查看最多的一些商品,比如说 iphone 8,热数据提前后台搞个程序,每隔 1 分钟自己主动访问一次,刷到 filesystem cache 里去。

对于那些你觉得比较热的、经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据每隔一段时间,就提前访问一下,让数据进入 filesystem cache 里面去。这样下次别人访问的时候,性能一定会好很多。

ES冷热分离

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

你看,假设你有 6 台机器,2 个索引,一个放冷数据,一个放热数据,每个索引 3 个 shard。3 台机器放热数据 index,另外 3 台机器放冷数据 index。然后这样的话,你大量的时间是在访问热数据 index,热数据可能就占总数据量的 10%,此时数据量很少,几乎全都保留在 filesystem cache 里面了,就可以确保热数据的访问性能是很高的。但是对于冷数据而言,是在别的 index 里的,跟热数据 index 不在相同的机器上,大家互相之间都没什么联系了。如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点,就 10% 的人去访问冷数据,90% 的人在访问热数据,也无所谓了。

ES分页优化

es 的分页是较坑的,为啥呢?举个例子吧,假如你每页是 10 条数据,你现在要查询第 100 页,实际上是会把每个 shard 上存储的前 1000 条数据都查到一个协调节点上,如果你有个 5 个 shard,那么就有 5000 条数据,接着协调节点对这 5000 条数据进行一些合并、处理,再获取到最终第 100 页的 10 条数据。

分布式的,你要查第 100 页的 10 条数据,不可能说从 5 个 shard,每个 shard 就查 2 条数据,最后到协调节点合并成 10 条数据吧?你必须得从每个 shard 都查 1000 条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第 100 页的数据。你翻页的时候,翻的越深,每个 shard 返回的数据就越多,而且协调节点处理的时间越长,非常坑爹。所以用 es 做分页的时候,你会发现越翻到后面,就越是慢。

我们之前也是遇到过这个问题,用 es 作分页,前几页就几十毫秒,翻到 10 页或者几十页的时候,基本上就要 5~10 秒才能查出来一页数据了。

解决方案

不允许深度分页(默认深度分页性能很差)
跟产品经理说,你系统不允许翻那么深的页,默认翻的越深,性能就越差。

类似于 app 里的推荐商品不断下拉出来一页一页的

类似于微博中,下拉刷微博,刷出来一页一页的,你可以用 scroll api,关于如何使用,自行上网搜索。

scroll 会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标 scroll_id 移动,获取下一页下一页这样子,性能会比上面说的那种分页性能要高很多很多,基本上都是毫秒级的。

但是,唯一的一点就是,这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。也就是说,你不能先进入第 10 页,然后去第 120 页,然后又回到第 58 页,不能随意乱跳页。所以现在很多产品,都是不允许你随意翻页的,app,也有一些网站,做的就是你只能往下拉,一页一页的翻。

初始化时必须指定 scroll 参数,告诉 es 要保存此次搜索的上下文多长时间。你需要确保用户不会持续不断翻页翻几个小时,否则可能因为超时而失败。

除了用 scroll api,你也可以用 search_after 来做,search_after 的思想是使用前一页的结果来帮助检索下一页的数据,显然,这种方式也不允许你随意翻页,你只能一页页往后翻。初始化时,需要使用一个唯一值的字段作为 sort 字段。

说说你们公司ES的集群架构,索引数据大小,分片有多少,以及一些调优手段?

根据实际情况回答即可,如果是我的话会这么回答:
我司有多个ES集群,下面列举其中一个。该集群有20个节点,根据数据类型和日期分库,每个索引根据数据量分片,比如日均1亿+数据的,控制单索引大小在200GB以内。 
下面重点列举一些调优策略,仅是我做过的,不一定全面,如有其它建议或者补充欢迎留言。
部署层面:
1)最好是64GB内存的物理机器,但实际上32GB和16GB机器用的比较多,但绝对不能少于8G,除非数据量特别少,这点需要和客户方面沟通并合理说服对方。
2)多个内核提供的额外并发远胜过稍微快一点点的时钟频率。
3)尽量使用SSD,因为查询和索引性能将会得到显著提升。
4)避免集群跨越大的地理距离,一般一个集群的所有节点位于一个数据中心中。
5)设置堆内存:节点内存/2,不要超过32GB。一般来说设置export ES_HEAP_SIZE=32g环境变量,比直接写-Xmx32g -Xms32g更好一点。
6)关闭缓存swap。内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上,一个100微秒的操作可能变成10毫秒。 再想想那么多10微秒的操作时延累加起来。不难看出swapping对于性能是多么可怕。
7)增加文件描述符,设置一个很大的值,如65535。Lucene使用了大量的文件,同时,Elasticsearch在节点和HTTP客户端之间进行通信也使用了大量的套接字。所有这一切都需要足够的文件描述符。
8)不要随意修改垃圾回收器(CMS)和各个线程池的大小。
9)通过设置gateway.recover_after_nodes、gateway.expected_nodes、gateway.recover_after_time可以在集群重启的时候避免过多的分片交换,这可能会让数据恢复从数个小时缩短为几秒钟。
索引层面:
1)使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点。
2)段合并:Elasticsearch默认值是20MB/s,对机械磁盘应该是个不错的设置。如果你用的是SSD,可以考虑提高到100-200MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加 index.translog.flush_threshold_size 设置,从默认的512MB到更大一些的值,比如1GB,这可以在一次清空触发的时候在事务日志里积累出更大的段。
3)如果你的搜索结果不需要近实时的准确度,考虑把每个索引的index.refresh_interval 改到30s。
4)如果你在做大批量导入,考虑通过设置index.number_of_replicas: 0 关闭副本。
5)需要大量拉取数据的场景,可以采用scan & scroll api来实现,而不是from/size一个大范围。
存储层面:
1)基于数据+时间滚动创建索引,每天递增数据。控制单个索引的量,一旦单个索引很大,存储等各种风险也随之而来,所以要提前考虑+及早避免。
2)冷热数据分离存储,热数据(比如最近3天或者一周的数据),其余为冷数据。对于冷数据不会再写入新数据,可以考虑定期force_merge加shrink压缩操作,节省存储空间和检索效率。

详细描述一下 Elasticsearch 搜索的过程?

搜索被执行成一个两阶段过程,即 Query Then Fetch;
Query阶段:
查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache的,但是有部分数据还在Memory Buffer,所以搜索是近实时的。
每个分片返回各自优先队列中 所有文档的 ID 和排序值 给协调节点,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
Fetch阶段:
协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。

如何使用 ES Tokenizer?

详细说明ELK Stack及其内容?

如何使用 Elastic Reporting ?

ES为什么吃内存?

我们先看下 ES 服务器的总体内存消耗情况:
在这里插入图片描述
注:
对于Query Cache、Request Cache、FieldData Cache、Indexing Buffer 以及 Segment 的介绍,在前面的文章以及介绍过了,这里就不重复介绍了:
Elasticsearch搜索引擎之缓存:Request Cache、Query Cache、Fielddata Cache
ElasticSearch搜索引擎:数据的写入流程

要回答 Elasticsearch 为什么这么耗费内存的问题,我们需要从两个角度切入:

(1)ES 是 JAVA 应用,那么就与 JVM 与 GC 息息相关。 我们这里不对 JVM GC 做深入探讨,只需知道:应用层面生成大量长生命周期的对象,是给 heap 造成压力的主要原因。例如读取大量数据在内存中进行排序,或者在 heap 内部缓存大量数据,如果 GC 释放的空间有限,而应用层面持续大量申请新对象,GC 频率就开始上升,不仅会消耗掉很多CPU时间,严重时可能恶性循环,导致整个集群停工。

(2)ES 底层存储引擎是基于 Lucene 的,Lucene 的倒排索引(Inverted Index)是先在内存里生成,然后定期以段文件(segment file)的形式刷到磁盘的。每个段实际就是一个完整的倒排索引,并且一旦写到磁盘上就不会做修改。API 层面的文档更新和删除实际上是增量写入的一种特殊文档,会保存在新的段里,所以不变的段文件非常容易被操作系统缓存,热数据几乎等效于内存访问。

Elasticsearch 的内存分配:

为什么分配给ES的堆内存不能超过物理机内存的一半?

预留一半内存给Lucene使用:

为什么需要预留一半的内存给 Lucene,将所有的内存都分配给 Elasticsearch 不是更好吗?毋庸置疑,堆内存对于 ES 来说绝对是重要的,但还有另外一个非常重要的内存使用者——Lucene。

在讲 Lucene 前,我们先简单介绍一下 segment。每个 segment 段是分别存储到单个文件的,即 segment 文件。它其实是一个包含正排(空间占90~95%) + 倒排(占5~10%) 的完整索引文件,并且一旦写到磁盘上就不会再修改,ES中文档更新和删除实际上是增量写入的一种特殊文档,会保存在新的段里而不修改旧的段。

回到 Lucene,Lucene 实际目的就是把底层 OS 里的数据缓存到内存中。由于 Lucene 的段 segment 是不会变化的,所以很利于缓存,操作系统会将这些段文件缓存起来,以便更快的访问。这些段包括倒排索引(用于全文搜索)和文档值(用于聚合)。

Lucene 的性能依赖于与 OS 的这种交互,如果把所有的内存都给了ES的堆内存,而不留一点给 Lucene,那么全文检索的性能会很差的。所以官方建议是将可用内存的 50% 提供给ES堆,而其他 50% 的剩余内存也并不会被闲置,因为 Lucene 会利用他们来缓存被用读取过的段文件。

 ES的文件存储类型默认使用的是 mmap 内存映射,将 Lucene 索引文件用映射到内存中,这样进程就能够直接从内存中读取 Lucene 数据了。由于使用
 了内存映射,ES 进程读取 Lucene 文件时读取到的数据就会占用了堆外内存的空间。

分配给 ES 的堆内存不要超过 32G:

堆内存为什么不能超过32GB?事实上 JVM 在内存小于 32 G 的时候会采用一种内存对象指针压缩技术。

在 Java 中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是 CPU 的字长的大小,不是 32 bit 就是 64 bit,这取决于你的处理器,指针指向了你的值的精确位置。对于 32 位系统,你的内存最大可使用 4 G。对于 64 系统可以使用更大的内存。但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。并且比浪费的空间更糟糕的是,更大的指针在主内存和缓存器(例如 LLC,L1 等)之间移动数据的时候,会占用更多的带宽。

Java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着 32 位的指针可以引用 40 亿个对象,而不是 40 亿个字节。最终,也就是说堆内存长到 32 G 的物理内存,也可以用 32 bit 的指针表示。

一旦越过那个神奇的 30 - 32 G 的边界,指针就会切回普通对象的指针,每个对象的指针都变长了,就会使用更多的 CPU 内存带宽,也就是说你实际上失去了更多的内存。事实上当内存到达 40 - 50 GB 的时候,有效内存才相当于使用内存对象指针压缩技术时候的 32 G 内存。

所以,即便你有足够的内存,也尽量不要超过 32 G,因为它浪费了内存,降低了 CPU 的性能,还要让 GC 应对大内存。

ElasticSearch 的堆内存该设置:

分配给 Heap 堆的内存不要超过系统可用物理内存的一半,以确保有足够的物理内存留给 Lucene 系统文件缓存,并且不要超过 32 GB。那么 JVM 参数呢?只需要将最小堆大小(Xms)和最大堆大小(Xmx)设置和 heap 一样大小,避免动态分配 heap size 就好了。确保 Xms 和 Xmx 的大小是相同的,其目的是为了能够在 java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源,可以减轻伸缩堆大小带来的压力。
虽然说 32 GB 是 ES 的一个内存设置限制,那如果机器有很大的内存怎么办?比如现在的机器内存普遍都大,设置有 300 - 500 GB 内存的机器。当然,如果有这种机器,那是极好的,接下来有两个方案:

(1)如果主要做全文检索,可以考虑给 Elasticsearch 32 G 内存,剩下的交给 Lucene 用作操作系统的文件系统缓存,所有的 segment 都缓存起来,会加快全文检索。

(2)如果需要更多的排序和聚合,那就需要更大的堆内存。可以考虑一台机器上创建两个或者更多的 ES 节点,而不要部署一个使用 32 + GB 内存的节点。仍然要坚持 50% 原则,假设你有个机器有 128 G 内存,你可以创建两个 node,使用 32 G 内存。也就是说 64 G 内存给 ES 的堆内存,剩下的 64 G 给 Lucene。

PS:如果选择第二种方案,需要配置 cluster.routing.allocation.same_shard.host: true,防止同一个 shard 的主副本存在同一个物理机上,因为
如果存在一个机器上,副本的高可用性就没有了

推荐文章:ES的内存问题分析:
ElasticSearch CPU和内存占用高的优化记录
【Elasticsearch优化】Elasticsearch内存那些事儿

搜索引擎之缓存

ElasticSearch 查询需要占用 CPU、内存资源,在复杂业务场景,会出现慢查询,需要花费大量的时间。为了提高系统的性能,除了增加集群硬件配置这种成本高昂的开销外,还可以使用 ES 的缓存,下面我们就介绍几种 ES 中常用的缓存。

Request cache

什么是 Request cache:

Request Cache,全称是 Shard Request Cache,即分片级请求缓存。当对一个或多个索引发送搜索请求时,搜索请求首先会发送到ES集群中的某个节点,称之为协调节点;协调节点会把该搜索请求分发给其他节点并在相应分片上执行搜索操作,我们把分片上的执行结果称为“本地结果集”,之后,分片再将执行结果返回给协调节点;协调节点获得所有分片的本地结果集之后,合并成最终的结果并返回给客户端。

Request Cache 在每个分片上缓存了本地结果集,这使得频繁使用的搜索请求几乎立即返回结果。默认情况下只会缓存查询中参数 size=0 的搜索请求的结果,因此将不会缓存hits,但会缓存 hits.total,aggregations(聚合) 和 suggestions。所以,request cache 分片请求缓存非常适合日志用例场景,在这种情况下,数据不会在旧索引上更新,并且可以将常规聚合保留在高速缓存中以供重用。

request cache 缓存的失效:

ES 能够保证在使用与不使用 Request Cache 情况下的搜索结果一致,那 ES 是如何保证的呢?这就要通过 Request Cache 的失效机制来了解啦。

Request Cache 缓存失效是自动的,当索引 refresh 时就会失效,也就是说在默认情况下, Request Cache 是每1秒钟失效一次,但需要注意的是,只有在分片的数据实际上发生了变化时,刷新分片缓存才会失效。也就是说当一个文档被索引 到 该文档变成Searchable的这段时间内,不管是否有请求命中缓存该文档都不会被返回。

所以我们可以通过 index.refresh_interval 参数来设置 refresh 的刷新时间间隔,刷新间隔越长,缓存的数据越多,当缓存不够的时候,将使用LRU最近最少使用策略删除缓存数据。

当然,我们也可以手动设置参数 indices.request.cache.expire 指定失效时间(单位为分钟),但是基本上我们没必要去这样做,因为缓存在每次索引 refresh 时都会自动失效。

最后,我们也可以通过 API 手动清除 Request Cache,使用方式如下:

        curl -XPOST '索引的IP:端口/索引名/_cache/clear?request_cache=true'

request cache 的使用与设置:

request cache 的使用:

默认情况下,Request Cache 是关闭的,我们可以在创建新的索引时启用,例如:

curl -XPUT 服务器IP:端口/索引名 -d
'{
  "settings": {
    "index.requests.cache.enable": true
  }
}'

也可以通过动态参数配置来进行设置:

curl -XPUT 服务器IP:端口/索引名/_settings -d 
'{ 
    "index.requests.cache.enable": true 
}'

开启缓存后,需要在搜索请求中加上 request_cache=true 参数,才能使查询请求被缓存,比如:

curl -XGET '服务器IP:端口/索引名/_search?request_cache=true&pretty' -H 'Content-Type: application/json' -d
'{
  "size": 0,
  "aggs": {
    "popular_colors": {
      "terms": {
        "field": "colors"
      }
    }
  }
}'

两个注意事项:

(1)第一:参数 size:0 必须强制指定才能被缓存,否则请求是不会缓存的,即使手动的设置request_cache=true

(2)第二:在使用 script 脚本执行查询时,由于脚本的执行结果是不确定的(比如使用 random 函数或使用了当前时间作为参数),一定要指定 request_cache=false 禁用 Request Cache 缓存。
3.2、request cache 的设置:

Request Cache 作用域为 Node,在 Node 中的 Shard 共享这个Cache空间。默认最大大小为 JVM堆内存的1%。可以使用以下命令在 config / elasticsearch.yml 文件中进行更改:

indices.requests.cache.size: 1%

Request Cache 是以查询的整个DSL语句做为key的,所以如果要命中缓存,那么查询生成的DSL一定要一样,即使修改了一个字符或者条件顺序,都不能利用缓存,需要重新生成Cache。

3.3、request cache 大小的查看方式:

GET /_stats/request_cache?human

GET /_nodes/stats/indices/request_cache?human

QueryCache:

什么是 Query Cache:

Query Cache,也称为 Filter Cache,就是对查询中包含的 Filter 过滤器的执行结果进行缓存,缓存在节点查询缓存中,以便快速查找。每个节点都有一个所有分片共享的查询缓存。当缓存空间存满时,使用 LRU 策略清理最近最少使用的查询结果,以腾出空间存放新结果数据。

ES 在 5.1.1 版本中移除了 term query 的缓存,因为 term query 和 filter query 二者查询时间相差不多。

默认情况下,节点查询缓存最多可容纳10000个查询,最多占总堆空间的10%,为了确定查询是否符合缓存条件,Elasticsearch 维护查询历史记录以跟踪事件的发生。但是,当 segment 的文档数量小于 10000 或者 小于分片文档总数的 3% 时,该查询是不会被缓存的。

由于缓存是按段划分的,因此合并段可使缓存的查询无效,同时,只有对频繁访问的请求才会使用查询缓存,因为查询缓存是基于 bitmap 的,而建立 bitmap 的过程需要一定的时间。

Query Cache 相关参数配置:

(1)index.queries.cache.enabled:控制是否启用节点查询缓存,可以设置 true(默认) 或者 false。该设置只能在创建索引或者索引关闭(close)时设置:

PUT my_index_01{  "settings": {    "index.queries.cache.enabled": false  }}

(2)indices.queries.cache.size:设置查询缓存的对堆内存大小,默认10%,可设置成百分比,也可设置成具体值,如 512m。

Fielddata Cache:

什么是 Fielddata Cache:

ES 的快速搜索特性主要依赖于倒排索引这种数据结构,但如果仅仅依靠倒排索引是很难在查询中做到排序和统计的,因为它并不像关系型数据库那样采用“列式存储”,而是基于一个 “term” 到 “document” 的倒排。这种情况下,如果需要做数据聚合和排序,就需要将倒排索引的数据读取出来,重新组织成一个数组缓存,也就是从倒排索引中生成出来的要自己维护这段Cache, 之后才能够高效的做排序和聚合计算。

为了解决上面的问题,就出现了 Fielddata Cache 字段数据缓存,它主要用于字段的排序和聚合,将所有的字段值加载到内存中,以便提供基于文档快速访问这些值。当第一次在某个分词的字段上执行聚合、排序或通过脚本访问的时候就会触发该字段 Fielddata Cache 的加载,这种缓存是 segment 级别的,当有新的 segment 打开时旧的缓存不会重新加载,而是直接把新的 segment 对应的 Fielddata Cache 加载到内存。

因为是基于 segment 级别的,所以 Fielddata Cache 失效和 Node Query Cache 失效机制相同,当 segment 被合并后,才会失效。

Fielddata Cache 的构建成本很高,一旦 Fielddata 被加载到内存,那么在该 Fielddata Cache 对应的 Segment 生命周期范围内都会驻留在堆内存中,也就是说当触发段合并时会导致合并后的更大段的 Fielddata Cache 加载。同时,由于 Fielddata Cache 默认缓存大小是无限的,这将导致缓存高速增长直到达到 field data 断路器设置的限制。特别是当加载“高基数”的分词字段时(那些分词后存在大量不同词的字段),针对这种字段的聚合排序其实是非常没有意义的,我们更多的要去考虑是否能用 not_analyzed 代替。如果设置了 fielddata cache 大小限制,缓存就会清除缓存中最新最少更新的数据。此设置可以避开 field data 断路器限制,但需要根据需要重建缓存;如果达到 field data 断路器限制,Elasticsearch 底层将阻止进一步增加缓存大小的请求。

由于 Fielddata Cache 是存放在堆内存中,在海量数据聚合的时候,生成的这些 fielddata 可能堆内存放不下,从而引起性能问题,甚至JVM OOM。所以 Elasticsearch2.0 开始,在非 text 字段开启 doc_values,基于 doc_values 做排序和聚合,可以减少对 FielddataCache 的依赖,减少内存消耗,因为 doc_values 在使用时不需要全部载入内存,可以减少节点 OOM 的概率。由于 doc_values 的特性性能上也不会有多少损失,doc_values 是一种正向索引结构以顺序预读的方式进行获取,所以随机获取就很慢了。在 5.0 开始,text 字段默认关闭了 Fielddata 功能, Fielddata Cache 应当只用于 global ordinals。

对 doc_values 的介绍与使用感兴趣的读者可以移步这篇文章:https://blog.csdn.net/a745233700/article/details/117915118

Fielddata Cache 参数配置:

(1)开启与关闭 Fielddate Cache:

默认情况下Fielddate Cache是默认开启的,我们可以通过下面的设置来关闭,关闭后就无法对分词字段执行聚合、排序操作了。

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "text": {
          "type": "string",
          "fielddata": {
            "format": "disabled" 
          }
        }
      }
    }
  }
}

(2)indices.fielddata.cache.size:设置字段数据缓存的最大值,通过百分比 30% 或者具体值 12GB 来设置,默认无限制。

(3)indices.breaker.fielddata.limit:此参数设置 Fielddata 断路器限制大小(公式:预计算内存 + 现有内存 <= 断路器设置内存限制),默认是60%JVM堆内存,当查询尝试加载更多数据到内存时会抛异常(以此来阻止JVM OOM发生)

(4)indices.breaker.fielddata.overhead:一个常数表示内存预估值系数,默认1.03,比如预计算加载100M数据,那么100*1.03=103M会用103M作为参数计算是否超过断路器设置的最大值。

您能解释一下X-Pack for Elasticsearch的功能和重要性吗?

X-Pack 是与Elasticsearch一起安装的扩展程序。

X-Pack的各种功能包括安全性(基于角色的访问,特权/权限,角色和用户安全性),监视,报告,警报等。

Kibana在Elasticsearch的哪些地方以及如何使用?

Kibana是ELK Stack –日志分析解决方案的一部分。

它是一种开放源代码的可视化工具,可以以拖拽、自定义图表的方式直观分析数据,极大降低的数据分析的门槛。

未来会向类似:商业智能和分析软件 - Tableau 发展。

在ES中 cat API的功能是什么?

cat API 命令提供了Elasticsearch 集群的分析、概述和运行状况,其中包括与别名,分配,索引,节点属性等有关的信息。

这些 cat 命令使用查询字符串作为其参数,并以J SON 文档格式返回结果信息。

REST API在ES方面有哪些优势?

REST API是使用超文本传输协议的系统之间的通信,该协议以 XML 和 JSON格式传输数据请求。

REST 协议是无状态的,并且与带有服务器和存储数据的用户界面分开,从而增强了用户界面与任何类型平台的可移植性。它还提高了可伸缩性,允许独立实现组件,因此应用程序变得更加灵活。

REST API与平台和语言无关,只是用于数据交换的语言是XML或JSON。

借助:REST API 查看集群信息或者排查问题都非常方便。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值