1、什么是ElasticSearch:
Elasticsearch 是基于 Lucene 的 Restful 的分布式实时全文搜索引擎,每个字段都被索引并可被搜 索,可以快速存储、搜索、分析海量的数据。
集群:是一个或多个服务器的集合,共同保存数据并提供所有节点的联合索引和搜索功能。集群有唯一 标志,为"ElasticSearch"。
节点:节点是属于集群一部分的单体服务器,储存有数据并参与集群索引和搜索功能。如果节点设置为 按名称加入集群,则该节点只能是集群的一部分。
索引:类似关系型数据库中的数据库,有一个定义多重类型的映射。索引是逻辑名称空间,可映射到一 个或多个主分片,并且可以有不限个数的副本分片。
文档:文档类似关系型数据库中的数据行,不同的是处在索引中的文档可以有不同的结构或字段,但是 通用字段应该具有相同的数据类型。
2、ElasticSearch 的基本概念:
(1)index 索引:索引类似于mysql 中的数据库,Elasticesearch 中的索引是存在数据的地方,包含了 一堆有相似结构的文档数据。
(2)type 类型:类型是用来定义数据结构,可以认为是 mysql 中的一张表,type 是 index 中的一个 逻辑数据分类 。
(3)document 文档:类似于 MySQL 中的一行,不同之处在于 ES 中的每个文档可以有不同的字段, 但是对于通用字段应该具有相同的数据类型,文档是es中的最小数据单元,可以认为一个文档就是一条 记录。
(4)Field 字段:Field是Elasticsearch的最小单位,一个document里面有多个field
(5)shard 分片:单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在 多台服务器上存储。有了shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务 器上去执行,提升吞吐量和性能。
(6)replica 副本:任何服务器随时可能故障或宕机,此时 shard 可能会丢失,通过创建 replica 副 本,可以在 shard 故障时提供备用服务,保证数据不丢失,另外 replica 还可以提升搜索操作的吞吐量。
节点是指ElasticSearch的实例。
当启动Elasticsearch的实例,就会启动至少一个节点。 相同集群名的多个节点的连接就组成了一个集群,在默认情况下,集群中的每个节点都可以处理http请 求和集群节点间的数据传输,集群中所有的节点都知道集群中其他所有的节点,可以将客户端请求转发 到适当的节点。
节点有以下类型:
主(master)节点:在一个节点上当node.master设置为True(默认)的时候,它有资格被选作为主节 点,控制整个集群。
数据(data)节点:在一个节点上node.data设置为True(默认)的时候。该节点保存数据和执行数据相 关的操作,如增删改查,搜索,和聚合。
客户端节点:当一个节点的node.master和node.data都设置为false的时候,它既不能保持数据也不能 成为主节点,该节点可以作为客户端节点,可以响应用户的情况,并把相关操作发送到其他节点。
3、ElasticSearch的分片:
Shard即数据分片,是ES的数据载体。在ES中数据分为primary shard(主分片)和replica shard(副本分片),每一个primary承载单个索引的一部分数据,分布于各个节点,replica为某个primary的副本,即备份。分片分配的原则是尽量均匀的分配在集群中的各个节点,以最大程度降低部分shard在出现意外时对整个集群乃至服务造成的影响。每个分片就是一个Lucene的实例,具有完整的功能。
4、ElasticSearch的分片分配策略:
ES使用数据分片(shard)来提高服务的可用性,将数据分散保存在不同的节点上以降低当单个节点发生故障时对数据完整性的影响,同时使用副本(repiica)来保证数据的完整性。关于分片的默认分配策略,在7.x之前,默认5个primary shard,每个primary shard默认分配一个replica,即5主1副,而7.x之后,默认1主1副。
ES在分配单个索引的分片时会将每个分片尽可能分配到更多的节点上。但是,实际情况取决于集群拥有的分片和索引的数量以及它们的大小,不一定总是能均匀地分布。Paimary只能在索引创建时配置数量,而replica可以在任何时间分配,并且primary支持读和写操作,而replica只支持客户端的读取操作,数据由es自动管理,从primary同步。
ES不允许Primary和它的Replica放在同一个节点中,并且同一个节点不接受完全相同的两个Replica同一个节点允许多个索引的分片同时存在。
5、text 和 keyword类型的区别:
两个类型的区别主要是分词:
keyword 类型是不会分词的,直接根据字符串内容建立倒排索引,所以 keyword类型的字段只能通过精确值搜索到;
Text 类型在存入 Elasticsearch 的时候,会先分词,然后 根据分词后的内容建立倒排索引
6、query 和 filter 的区别:
(1) query:查询操作不仅仅会进行查询,还会计算分值,用于确定相关度;
(2) filter:查询操作仅判断是否满足查询条件,不会计算任何分值,也不会关心返回的排序问题,同 时,filter 查询的结果可以被缓存,提高性能。
7、倒排索引过程:
倒排索引是区别于正排索引的概念: 正排索引:是以文档对象的唯一 ID 作为索引,以文档内容作为记录。 倒排索引:Inverted index,指的是将文档内容中的单词作为索引,将包含该词的文档 ID 作为记录。
倒排索引的生成过程
下面通过一个例子来说明下倒排索引的生成过程。
假设目前有以下两个文档内容: 苏州街维亚大厦 桔子酒店苏州街店 其处理步骤如下:
- 正排索引给每个文档进行编号,作为其唯一的标识。
- 生成倒排索引。
首先要对字段的内容进行分词,分词就是将一段连续的文本按照语义拆分为多个单词,这里两个文 档包含的关键词有:苏州街、维亚大厦… 然后按照单词来作为索引,对应的文档 id 建立一个链表,就能构成上述的倒排索引结构。
有了倒排索引,能快速、灵活地实现各类搜索需求。整个搜索过程中我们不需要做任何文本的模糊匹配。
倒排索引的结构根据倒排索引的概念,我们可以用一个 Map来简单描述这个结构。这个 Map 的 Key 的即是分词后的 单词,这里的单词称为 Term,这一系列的 Term 组成了倒排索引的第一个部分 —— Term Dictionary (索引表,可简称为 Dictionary)。 倒排索引的另一部分为 Postings List(记录表),也对应上述 Map 结构的 Value 部分集合。 记录表由所有的 Term 对应的数据(Postings) 组成,它不仅仅为文档 id 信息,可能包含以下信息:
文档 id(DocId, Document Id),包含单词的所有文档唯一 id,用于去正排索引中查询原始数 据。
词频(TF,Term Frequency),记录 Term 在每篇文档中出现的次数,用于后续相关性算分。
位置(Position),记录 Term 在每篇文档中的分词位置(多个),用于做词语搜索(Phrase Query)。
偏移(Offset),记录 Term 在每篇文档的开始和结束位置,用于高亮显示等。
8、Elasticsearch是如何实现master选举的?
Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个 RPC 来发 现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分 对所有可以成为 master 的节点(node.master: true)根据 nodeId 字典排序,每次选举每个节 点都把自己所知道节点排一次序,然后选出第一个(第 0 位)节点,暂且认为它是 master 节 点。 如果对某个节点的投票数达到一定的值(可以成为 master 节点数 n/2+1)并且该节点自己也选 举自己,那这个节点就是 master。否则重新选举一直到满足上述条件。 master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以 关闭 http功能。
9、详细描述一下Elasticsearch索引文档的过程。
首先客户端向集群发出索引文档的请求,它会选择任何一个节点,这个节点当接收到请求后会根据路由算法找到应该放的那个主分片的位置,从而索引数据。
这个节点当接收到请求后会根据路由算法找到应该放的那个主分片的位置,从而索引数据,
之后为了保证数据的完整性,它会将它的副本数据进行同步,同步完成后客户端就可以进行访问了。 细节方面:
用户的索引请求发过来之后,首先协调结点默认使用文档ID参与哈希计算(也支持通过routing),
分片位置索引 = 将文档ID或路由ID进行哈希计算后的值 % 所有分片总数
随后会在内存(memory)中建立一个索引(Index),这个Index会在内存中形成一个分段对象 (Segment), 为了防止数据出现问题,会同时在索引数据之后写入到日志(Translog)当中, 在此过程中,每隔1秒钟,会向Segment会将数据刷新到系统文件缓存区(OS Cache),以方便接收用户的查询, 因为如果让用户查询直接访问内存或磁盘,会使速度变慢。 当过了30分钟或者Translog中的数据超过了512M,Os Cache中的Segment会将数据刷写(flush)到 磁盘当中,刷写后内存中的缓冲将被清除。 此时一旦刷写的数据比较多了的话(磁盘中有多个Segment),磁盘就会将这些分段进行合并。
当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时(默 认是每隔1秒)写入到 Filesystem Cache,这个从MomeryBuffer到Filesystem Cache的过程就叫做 refresh;
当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过 translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当 Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush;
在 flush 过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提交 点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translog。
flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时。
10、详细描述一下Elasticsearch写入数据主要流程?
- Es 客户端选择集群中的一个节点发起写请求;
- Es 集群将节点标记为协调节点;
- 协调节点对写入的 document 进行路由,选择 primary shard 写入数据(图中只是表示简单路由到一个节点,实际会把数据路由到多个节点);
- 该 primary shard 上的数据写入完毕后,将数据同步到副分片中;
- 协调节点告诉客户端 primary shard 和 replica shard 已经写入数据完毕;
写入请求将数据同时发送到 Es 的 Buffer 缓存和 translog 日志,此时数据在 Es 进程的 Buffer 缓存中,无法查询到;
默认每 1 秒,Es Buffer 中的数据会被 Refres 到 segment file os cache 中,并清空 ES 进程的 Buffer 缓存,此时数据就可以被查到了,这个过程就是refresh。
index.refresh_interval:1s
新segment会被先写入到 os cache –这一步代价会比较低,稍后再被刷新到磁盘–这一步代价比较高。
每隔1秒钟,es将buffer中的数据写入一个新的segment file,这个segment file中就存储最近1秒内 buffer中写入的数据
操作系统里面,磁盘文件其实都有一个东西,叫做 os cache,即操作系统缓存,就是说数据写入磁盘文件之前,会先进入os cache,先进入操作系统级别的一个内存缓存中去。只要buffer中的数据被refresh操作输入os cache中,这个数据就可以被搜索到了。
重复上面的步骤,新的数据就不断进入buffer和translog,不断将buffer数据写入一个又一个新的segment file中去,每次refresh完buffer清空,translog保留
随着这个过程的推进,translog会变得越来越大。当translog达到一定的大小的时候,或者到达设置的默认时长(30min)后, 就会触发commit操作,这种操作也叫 flush(可以通过 API 触发)
flush操作 = refresh + 将translog中的记录刷到磁盘上 + 更新commit point信息 + 清空translog文件.
1)、先强行将 Es Buffer 中的数据写入 segment file os cache 中,然后清空 Es Buffer;
2)、向磁盘写入一个 commit point 文件,该文件标识着 commit point 对应的所有 segment file;
3)、强行将 segment file os cache 中的数据都 fsync 到磁盘文件中去;
4)、清空translog中的数据 (6.x版本为了实现sequenceIDs,不删除translog)
translog 文件:
commit point 文件:
总结如下:
ES的任意节点都可以作为协调节点(coordinating node)接受请求
当协调节点接受到请求后进行一系列处理,然后通过_routing字段找到对应的primary shard,并将请求转发给primary shard,primary shard完成写入后,将写入并发发送给各replica, raplica执行写入操作后返回给primary shard, primary shard再将请求返回给协调节点。数据写入ES buffer,然后每隔1s,将数据refresh到os cache,到了os cache数据就能被搜索到。默认每隔5s,将 translog os cache 数据写入到translog文件, 当translog达到一定量或者默认每隔30min,会触发commit操作,将缓存区的数据flush到segment file磁盘文件中。数据写入到segment file之后,同时就建立好了倒排索引
10、Elasticsearch的查询原理
包括单个ID查询文档和多个ID查询文档的流程。在搜索查询方面,通过两阶段查询,首先在各个分片拷贝中搜索匹配的文档标识符,然后在协调节点合并结果并获取完整文档
单个ID查询文档
例如:当 ES客户端 将 单文档id Get请求发送到节点 2 时,节点 2 将作为协调节点来处理查询请求。由于哈希取模运算确定了文档所在的分片为 shard3,因此查询流程如下:
协调节点处理:节点 2(协调节点)接收到查询请求后,它将确定文档所在的分片为 shard3。
分片路由:节点 2 根据分片路由 知道 shard3 的主分片在节点 3 上,因此它将查询请求转发到节点 3。
文档检索:一旦查询请求到达节点 3,它将在 shard3 的主分片上执行文档检索操作并将检索到的文档结果返回给协调节点(节点 2)
最终返回:最后协调节点(节点 2)将收到来自数据节点(节点 3)的文档结果,并将其返回给客户端。
多个ID查询文档
对于 mget 请求,Elasticsearch 客户端会一次性发送多个文档ID,并且在协调节点(这里是节点 2)进行路由和汇总查询结果。
多文档ID查询(mget 请求)的流程:
协调节点处理:节点 2(协调节点)接收到 mget 请求后,会根据每个文档ID的哈希值确定对应的分片。
分片路由:协调节点会根据每个文档ID的哈希值确定其所在的分片,并将查询请求路由到负责相应分片的节点上。
并行查询:每个数据节点接收到查询请求后,并行地搜索相应的文档。每个节点将同时处理其负责的文档ID的查询请求。
结果汇总:每个数据节点将查询结果返回给协调节点。
结果返回:协调节点收到来自数据节点的查询结果后,会将它们汇总并返回给 Elasticsearch 客户端。
11、Elasticsearch 集群脑裂问题?有哪些解决方法?
“脑裂”问题可能的成因:(有两个master)
网络问题:集群间的网络延迟导致一些节点访问不到 master,认为 master 挂掉了从而选举出新 的master,并对 master 上的分片和副本标红,分配新的主分片 。
节点负载:主节点的角色既为 master 又为 data,访问量较大时可能会导致 ES 停止响应造成大面 积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点。
内存回收:data 节点上的 ES 进程占用的内存较大,引发 JVM 的大规模内存回收,造成 ES 进程 失去响应。
脑裂问题解决方案
减少误判:discovery.zen.ping_timeout 节点状态的响应时间(超过这个时间就会重新选举 master),默认为 3s,可以适当调大,如果 master在该响应时间的范围内没有做出响应应答, 判断该节点已经挂掉了。
调大参数(如 6s,discovery.zen.ping_timeout:6),可适当减少误判。 选举触发:discovery.zen.minimum_master_nodes:1 该参数是用于控制选举行为发生的最小集群主节点数量。当备选主节点的个数大于等于该参数的值, 且备选主节点中有该参数个节点认为主节点挂了,进行选举。官方建议为(n/2)+1,n 为主节点个数 (即有资格成为主节点的节点个数)
该参数是用于控制选举行为发生的最小集群主节点数量。当备选主节点的个数大于等于该参数的值, 且备选主节点中有该参数个节点认为主节点挂了,进行选举。官方建议为(n/2)+1,n 为主节点个数 (即有资格成为主节点的节点个数)
角色分离:即 master 节点与 data 节点分离,限制角色 主节点配置为:node.master: true node.data: false 从节点配置为:node.master: false node.data: true
12、Elasticsearch了解多少,聊聊你们公司ES的集群架 构,索引数据大小,分片有多少,以及一些调优手段 。
比如:ES集群架构13个节点,索引根据通道不同共20+索引,根据日期,每日递增20+ 索引:10分片,每日递增1亿+数据,每个通道每天索引大小控制:150GB之内。
索引层面调优手段:
1、设计阶段调优
1)根据业务增量需求,采取基于日期模板创建索引,通过roll over API滚动索引;
2)使用别名进行索引管理;
3)每天凌晨定时对索引做force_merge操作,以释放空间;
4)采取冷热分离机制,热数据存储到SSD,提高检索效率;冷数据定期进行shrink操作,以缩减存储;
5)采取curator进行索引的生命周期管理;
6)仅针对需要分词的字段,合理的设置分词器;
7)Mapping阶段充分结合各个字段的属性,是否需要检索、是否需要存储等。
2、写入调优
1)写入前副本数设置为0;
2)写入前关闭refresh_interval设置为-1,禁用刷新机制;
3)写入过程中:采取bulk批量写入;
4)写入后恢复副本数和刷新间隔;
5)尽量使用自动生成的id。
3、查询调优
13、详细描述一下Elasticsearch更新和删除文档的过程
14、Elasticsearch在部署时,对Linux的设置有哪些优化方法?
15、在并发情况下,Elasticsearch如果保证读写一致?
16、ES的参数优化
优化 fsync
为了保证不丢失数据,就要保护 translog 文件的安全:
该方式提高数据安全性的同时, 降低了一点性能.
==> 频繁地执行 fsync
操作, 可能会产生阻塞导致部分操作耗时较久. 如果允许部分数据丢失, 可设置异步刷新 translog 来提高效率,还有降低 flush 的阀值,优化如下:
"index.translog.durability": "async",
"index.translog.flush_threshold_size":"1024mb",
"index.translog.sync_interval": "120s"
优化 refresh
写入 Lucene 的数据,并不是实时可搜索的,ES 必须通过 refresh 的过程把内存中的数据转换成 Lucene 的完整 segment 后,才可以被搜索。
默认 1秒后,写入的数据可以很快被查询到,但势必会产生大量的 segment,检索性能会受到影响。所以,加大时长可以降低系统开销。对于日志搜索来说,实时性要求不是那么高,设置为 5 秒或者 10s;对于 SkyWalking,实时性要求更低一些,我们可以设置为 30s。
设置如下:
"index.refresh_interval":"5s"
优化 merge
index.merge.scheduler.max_thread_count 控制并发的 merge 线程数,如果存储是并发性能较好的 SSD,可以用系统默认的 max(1, min(4, availableProcessors / 2)),当节点配置的 cpu 核数较高时,merge 占用的资源可能会偏高,影响集群的性能,普通磁盘的话设为1,发生磁盘 IO 堵塞。设置max_thread_count 后,会有 max_thread_count + 2 个线程同时进行磁盘操作,也就是设置为 1 允许3个线程。
设置如下:
"index.merge.scheduler.max_thread_count":"1"
优化线程池配置
前文已经提到过,write 线程池满负荷,导致拒绝任务,而有的数据无法写入。
而经过上面的优化后,拒绝的情况少了很多,但是还是有拒绝任务的情况。
所以我们还需要优化 write 线程池。
为了更直观看到 ES 线程池的运行情况,我们安装了 elasticsearch_exporter 收集 ES 的指标数据到 prometheus,再通过 grafana 进行查看。
经过上面的各种优化,拒绝的数据量少了很多,但是还是存在拒绝的情况,如下图:
write 线程池采用 fixed 类型的线程池,也就是核心线程数与最大线程数值相同。线程数默认等于 cpu 核数,可设置的最大值只能是 cpu 核数加 1,也就是 16 核 CPU,能设置的线程数最大值为 17。
优化的方案:
-
线程数改为 17,也就是 cpu 总核数加 1
-
队列容量加大。队列在此时的作用是消峰。不过队列容量加大本身不会提升处理速度,只是起到缓冲作用。此外,队列容量也不能太大,否则积压很多任务时会占用过多堆内存。
config/elasticsearch.yml文件增加配置
# 线程数设置
thread_pool:
write:
# 线程数默认等于cpu核数,即16
size: 17
# 因为任务多时存在任务拒绝的情况,所以加大队列大小,可以在间歇性任务量陡增的情况下,缓存任务在队列,等高峰过去逐步消费完。
queue_size: 10000
锁定内存,不让 JVM 使用 Swap
Swap 交换分区:
Swap 交换分区对性能和节点稳定性非常不利,一定要禁用。它会导致垃圾回收持续几分钟而不是几毫秒,并会导致节点响应缓慢,甚至与集群断开连接。
减少分片数、副本数
分片
索引的大小取决于分片与段的大小,分片过小,可能导致段过小,进而导致开销增加;分片过大可能导致分片频繁 Merge,产生大量 IO 操作,影响写入性能。
因为我们每个索引的大小在 15G 以下,而默认是 5 个分片,没有必要这么多,所以调整为 3 个。
"index.number_of_shards": "3"
分片的设置我们也可以配置在索引模板。
副本数
减少集群副本分片数,过多副本会导致 ES 内部写扩大。副本数默认为 1,如果某索引所在的 1 个节点宕机,拥有副本的另一台机器拥有索引备份数据,可以让索引数据正常使用。但是数据写入副本会影响写入性能。对于日志数据,有 1 个副本即可。对于大数据量的索引,可以设置副本数为0,减少对性能的影响。
"index.number_of_replicas": "1"
17、解析es的返回json文档信息。·
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
// 假设responseString是从Elasticsearch获取的JSON响应字符串
String responseString = "{\"took\":1,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":1,\"max_score\":1.0,\"hits\":[{\"_index\":\"index_name\",\"_type\":\"doc\",\"_id\":\"1\",\"_score\":1.0,\"_source\":{\"field1\":\"value1\",\"field2\":\"value2\"}}]}}";
ObjectMapper mapper = new ObjectMapper(); // 创建ObjectMapper实例
JsonNode rootNode = mapper.readTree(responseString); // 将JSON字符串解析为JsonNode树
// 获取"hits"节点
JsonNode hitsNode = rootNode.path("hits").path("hits");
for (int i = 0; i < hitsNode.size(); i++) {
JsonNode hit = hitsNode.path(i);
String index = hit.path("_index").asText();
String type = hit.path("_type").asText();
String id = hit.path("_id").asText();
float score = (float) hit.path("_score").asDouble();
JsonNode source = hit.path("_source");
// 处理source中的字段
String field1 = source.path("field1").asText();
String field2 = source.path("field2").asText();
// 打印获取的数据
System.out.println("Index: " + index + ", Type: " + type + ", Id: " + id + ", Score: " + score + ", Field1: " + field1 + ", Field2: " + field2);
}