ElasticSearch的面试题

ElasticSearch的面试题

ElasticSearch是开源的高扩展的分布式全文搜索引擎。

ElasticSearch基本操作

ES里的Index可以看做一个库,而Types想当于表,Documents则相当于表的行,这里的Types的概念已经被逐渐弱化,ElasticSearch6.X中,一个index下已经只能包含一个type,ElasticSearch7.X中,Type的概念已经被删除了。

索引操作

创建索引

PUT 请求 http://127.0.0.1:9200/shopping

查看索引

GET 请求 http://127.0.0.1:9200/shopping  查看对应索引

GET 请求 http://127.0.0.1:9200/_cat/indices?v

删除索引

DELETE 请求 http://127.0.0.1:9200/shopping

文档操作

索引已经创建好了,接下来就是创建文档,并添加数据。这里的文档可以类比为关系型数据库中的表数据,添加数据格式为json

创建文档

_id 是随机生成的

POST 请求 http://127.0.0.1:9200/shopping/_doc

			
{
	"title":"小米手机",
	"category":"小米",
	"price":3999.00
}

_id指定ID

POST/PUT 请求 http://127.0.0.1:9200/shopping/_doc/1001

这样操作_id 就会变成 1001

查询文档

GET 请求 http://127.0.0.1:9200/shopping/_doc/1001

查询所有文档

GET 请求 http://127.0.0.1:9200/shopping/_search

更新文档

更新全量数据

http://127.0.0.1:9200/shopping/_doc/1001
{
	"title":"小米手机",
	"category":"小米",
	"price":4999.00
}

局部更新数据 更新一个字段的数据

POST 请求 http://127.0.0.1:9200/shopping/_update/1001
{
	"doc" : {
		"title":"华为手机"
	}
}

删除文档

DELETE 请求 http://127.0.0.1:9200/shopping/_doc/1001

条件查询

1、 GET 请求

 http://127.0.0.1:9200/shopping/_search?q=category:小米

2、请求体查询

http://127.0.0.1:9200/shopping/_search
{
	"query":{
		"match" : {
			"category":"小米"
		}
	}
}

3、分页查询

http://127.0.0.1:9200/shopping/_search

{
	"query":{
		"match_all" : {
			
		}
	},
	# 分页查询
	"from":0,
	"size":2,
	# 指定查找的结果
	"_source":["title"],
	# 排序
	"sort":{
		"price":{
			"order":"desc"
		}
	}
}

4、多条件查询

and

{
	"query" :{
		"bool" :{
			# 多个条件同时成立
			"must":[
				"match":{
					"category":"小米"
				},{
				"match":{
					"price":1999.00
				}
			]
		}
	}
}

or

{
	"query" :{
		"bool" :{
			# 多个条件只要一个成立即可
			"should":[
				"match":{
					"category":"小米"
				},{
				"match":{
					"category":"华为"
				}
			]
		}
	}
}

范围

{
	"query" :{
		"bool" :{
			# 多个条件只要一个成立即可
			"should":[
				"match":{
					"category":"小米"
				},{
				"match":{
					"category":"华为"
				}
			],
			"filter" :{
				"range":{
					"price":{
						"gt":5000
					}
				}
			}
		}
	}
}

完全匹配

完全匹配到小米

{
	"query":{
		"match_phrase" : {
			"category":"小米"
		}
	}
}

高亮匹配

{
	"query":{
		"match_phrase" : {
			"category":"小米"
		}
	},
	"highlight":{
		"fields" :{
			"category":{}
		}
	}
}

聚合操作

{
	"aggs":{ //聚合操作
		"price_group":{ // 名称,可自定义 
			"terms":{ // 分组
				"field":"price" //分组字段
			}
		}
	},
	//不需要原始数据
	"size":0
}
	
		
{
	"aggs":{ //聚合操作
		"price_avg":{ // 名称,可自定义 
			"avg":{ // 平均值
				"field":"price" //分组字段
			}
		}
	},
	//不需要原始数据
	"size":0
}

映射关系

PUT 请求 http://127.0.0.1:9200/user/_mapping
{
	"properties":{
		"name":{
		  //可以被分词查询
		  "type":"text",
		  "index":true
		},
		"sex":{
			"type":"keyword",
			"index":true
		},
		"tel":{
			"type":"keyword",
			"index":false
		}
	}
}

ElasticSearch集群

核心概念

集群Cluster

一个集群就是由一个或多个服务器节点组织在一起,共同持有整个的数据,并一起提供索引和搜索功能。一个ElasticSearch集群中有一个唯一的名字标识,这个名字默认就是"elasticsearch"。这个名字很重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群。

节点Node

集群中包含很多服务器,一个节点就是其中的一个服务器。作为集群的一部分,它存储数据,参与集群的索引和搜索功能。一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。

配置集群

elsticsearch.yml
cluster.name  --- 集群名称 必须统一
node.name   --- 节点名称 不能重复
node.master: true
node.data: true

network.host --- 主机名称  localhost
http.port --- 端口号 1001
transport.tcp.port --- 监听端口 9301

//跨域配置
http.cors.enabled: true
http.cors.allow-origin: "*"


http.port: 1002
transport.tcp.port: 9302
discovery.seed_hosts: ["localhost:9301"]
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5

分片

  • 1、允许你水平分割/扩展你的内容容量
  • 2、允许你在分片之上进行分布式的、并行的操作,进而提供性能/吞吐量

副本

  • 1、在分片/节点失败的情况下,提供高可用性。因为这个原因,注意到复制分片从不与原/主要分片置于同一个节点上是非常重要的
  • 2、扩展你的搜索量/吞吐量,因为搜索可以在所有的副本上并行运行

路由计算

hash(id) % 主分片数量 = [0,1,2]

分片控制

用户可以访问任何一个节点获取数据,这个节点称之为协调节点

写数据

  • 1、客户端请求集群节点 – 协调节点
  • 2、协调节点将请求转换到指定的节点
  • 3、主分片需要将数据保存
  • 4、主分片需要将数据发送给副本
  • 5、副本保存后,进行反馈
  • 6、主分片进行反馈
  • 7、客户端获取反馈

读数据

  • 1、客户端发送查询请求到协调节点
  • 2、协调节点计算数据所在的分片以及全部的副本位置
  • 3、为了能够负载均衡,可能轮询所有节点
  • 4、将请求转发给具体的节点
  • 5、节点返回查询结果,将结果反馈给客户端

ElasticSearch读写一致性

  • 对于更新操作:可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,每个文档都有一个_version版本号,这个版本号在文档被改变时加一。Elasticsearch使用这个_version 保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。利用_version的这一优点确保数据不会因为修改冲突而丢失。比如指定文档的version来做更改。如果那个版本号不是现在的,我们的请求就失败了。

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

    • one: 要求我们这个写操作,只要有一个primary shardactive活跃可用的,就可以执行
    • all: 要求我们这个写操作,必须所有的primary shardreplica shard都是活跃的,才可以执行这个写操作
    • quorum: 默认的值,要求所有的shard中,必须是大部分的shard都是活跃的,可用的,才可以执行这个写操作
  • 对于读操作,可以设置 replicationsync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication async 时,也可以通过设置搜索请求参数_preference primary 来查询主分片,确保文档是最新版本。

ElasticSearch操作流程

ES的写入流程

  • 客户端选择一个 node 发送请求过去,这个 node 就是coordinating node (协调节点)
  • coordinating nodedocument 进行路由,将请求转发给对应的 node(有 primary shard
  • 实际的node上的primary shard处理请求,然后将数据同步到replica node
  • coordinating node 等到 primary node 和所有 replica node 都执行成功之后,就返回响应结果给客户端。

ES写入底层原理

  • 数据先写入memory buffer,然后定时(默认每隔1s)将memory buffer中的数据写入一个新的segment 文件中,并进入 Filesystem cache(同时清空 memory buffer),这个过程就叫做 refresh;ES 的近实时性:数据存在 memory buffer 时是搜索不到的,只有数据被 refresh Filesystem cache 之后才能被搜索到,而 refresh 是每秒一次, 所以称 es 是近实时的,可以通过手动调用 esapi 触发一次 refresh 操作,让数据马上可以被搜索到;
  • 由于 memory BufferFilesystem Cache 都是基于内存,假设服务器宕机,那么数据就会丢失,所以 ES 通过 translog 日志文件来保证数据的可靠性,在数据写入 memory buffer 的同时,将数据写入 translog 日志文件中,在机器宕机重启时,es 会自动读取 translog 日志文件中的数据,恢复到 memory bufferFilesystem cache 中去。ES 数据丢失的问题:translog 也是先写入 Filesystem cache,然后默认每隔 5 秒刷一次到磁盘中,所以默认情况下,可能有 5 秒的数据会仅仅停留在 memory buffer 或者 translog 文件的 Filesystem cache中,而不在磁盘上,如果此时机器宕机,会丢失 5 秒钟的数据。也可以将 translog 设置成每次写操作必须是直接 fsync 到磁盘,但是性能会差很多。
  • flush 操作:不断重复上面的步骤,translog会变得越来越大,当 translog 文件默认每30分钟或者 阈值超过 512M 时,就会触发 commit 操作,即 flush操作。
    • buffer 中的数据 refresh Filesystem Cache 中去,清空 buffer
    • 创建一个新的 commit point(提交点),同时强行将 Filesystem Cache 中目前所有的数据都fsync到磁盘文件中;
    • 删除旧的 translog 日志文件并创建一个新的 translog 日志文件,此时 commit 操作完成

ES更新和删除流

删除和更新都是写操作,但是由于Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;所以 ES 利用.del文件 标记文档是否被删除,磁盘上的每个段都有一个相应的.del 文件

  • 如果是删除操作,文档其实并没有真的被删除,而是在.del文件中被标记为 deleted 状态。该文档依然能匹配查询,但是会在结果中被过滤掉。
  • 如果是更新操作,就是将旧的doc标识为 deleted 状态,然后创建一个新的 doc

memory buffer refresh 一次,就会产生一个 segment 文件 ,所以默认情况下是 1s 生成一个segment文件,这样下来 segment 文件会越来越多,此时会定期执行 merge

每次merge的时候,会将多个 segment 文件合并成一个,同时这里会将标识为 deleteddoc 给物理删除掉,不写入到新的 segment 中,然后将新的segment文件写入磁盘,这里会写一个 commit point ,标识所有新的 segment 文件,然后打开 segment 文件供搜索使用,同时删除旧的 segment 文件。

ES的搜索流程

搜索被执行成一个两阶段过程,即 Query Then Fetch

Query阶段

客户端发送请求到 coordinate node,协调节点将搜索请求广播到所有的 primary shardreplica shard。每个分片在本地执行搜索并构建一个匹配文档的大小为from + size的优先队列。每个分片返回各自优先队列中 所有文档的ID和排序值 给协调节点,由协调节点及逆行数据的合并、排序、分页等操作,产出最终结果。

Fetch阶段

协调节点根据 doc id 去各个节点上查询实际的 document 数据,由协调节点返回结果给客户端。

  • coordinate node doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在primary shard以及其所有 replica 中随机选择一个,让读请求负载均衡。
  • 接收请求的 node 返回 documentcoordinate node
  • coordinate node 返回 document 给客户端。

Query Then Fetch 的搜索类型在文档相关性打分的时候参考的是本分片的数据,这样在文档数量较少的时候可能不够准确,DFS Query Then Fetch 增加了一个预查询的处理,询问 TermDocument frequency,这个评分更准确,但是性能会变差。

ElasticSearch提高查询效率

ES在数据量很大的情况下(数十亿级别)如何提高查询效率

解题思路

需要从ES搜索优化作答

es的性能优化,主要是围绕着fileSystem cache,也可以叫做OS cache来进行;es写入数据实际上数据最终都会写入到磁盘中去,当我们搜索读取的时候,系统会将数据放入到os cache中,而es严重依赖于这个os cache,如果我们给机器的内存足够多,在es里存的数据小于内存容量,那么搜索效率是非常高的。

filesystem cahce

你往es里写的数据,实际上都写到磁盘文件里去了,查询的时候,操作系统会将磁盘文件里的数据自动缓存到filesystem cache里面去,es的搜索引擎严重依赖于底层的filesystem cache,你如果给filesystem cache更多的内存,尽量让内存可以容纳所有的idx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高

减少字段

es减少数据量仅仅放要用于搜索的几个关键字段即可,尽量写入es 数据量跟es机器的filesystem cache是差不多的就可以了;其他不用来检索的数据放hbase里或者mysql

数据预热

每隔一段时间,访问一下数据,然后数据进入到os cache中。这样用户来访问 时候就访问到os cache中的数据,就比较快。

冷热分离

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

document模型设计

es里面的复杂的关联查询尽量别用,比如join/ nested /parent-child 搜索都要尽量避免

不允许深度分页

可以使用scroll api scroll会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标scroll_id移动,获取下一页。除了使用scroll api,也可以使用search_after来做,search_after的思想使用前一页的结果来帮助检索下一页的数据,这种方式也不允许你随意翻页,你只能一页页往后翻。

ElasticSearch优化

磁盘选择

  • 使用SSD
  • 使用RAID 0
  • 使用多块硬盘,并允许ElasticSearch通过多个path.data目录配置把数据条带化分配到他们上面
  • 不要使用远程挂载的存储,比如NFS或者SMBCIFS

合理设置分片数

  • 控制每个分片占用磁盘容量不超过ES的最大JVM的堆空间设置(一般设置不超过32G),因此如果索引的总容量在500G左右,那么分片大小在16个左右即可
  • 考虑一下node的数量,一般一个节点有时候就是一台物理机,如果分片数过多,大大超过了节点数,很可能会导致一个节点上存在多个分片,一旦该节点故障,
  • 即使保持了1个以上的副本,同样有可能会导致数据丢失,集群无法恢复。所以,一般都设置分片数不超过节点数的3倍。
  • 主分片,副本和节点最大数之间数量,我们分配可以参考
    节点数<=主分片数*(副本数+1)

推迟分片分配

  • 对于节点瞬时中断的情况,默认的情况,集群会等待一分钟来查看节点是否重新加入,如果这个节点在此期间重新加入,重新加入的节点会保持其现有的分片数据,不会触发新的分片分配
  • 通过修改参数delayed_timeout,可以延长再均衡的时间,可以全局设置也可以在索引级别进行修改。

路由选择

带路由查询

写入速度优化

  • 加大Translog Flush,目的是降低IopsWritelock
  • 增加Index Refresh间隔,目的是减少Segment Merge的次数
  • 调整Bulk线程池和队列
  • 优化节点间的任务分布
  • 优化Lucene层的索引建立,目的是降低CPU及IO

批量数据提交

  • Bulk默认设置批量提交的数据量不能超过100M。数据条数一般是根据文档的大小和服务器性能而定的。但是单次批处理的数据大小应从5MB-15MB逐渐增加,
  • 当性能没有提升,把这个数据量作为最大值。

优化存储设备

密集使用硬盘的应用

合理使用合并

ES默认采用较保守的策略,让后台定期进行段合并

减少Refresh的次数

  • Lucene在新增数据时,采用了延迟写入的策略,默认情况下索引的refresh_interval为1秒
  • Lucene将待写入的数据先写到内存中,超过1秒(默认)时就会触发一次refresh,然后refresh会把内存中的数据刷新到操作系统的文件缓存系统中。
  • 如果我们对搜索的实效性要求不高,可以将refresh周期延长

加大flush设置

Flush的主要目的是把文件缓存系统中的段持久化到硬盘,当Translog的数据量达到512MB或者30分钟时,会触发一次Flush
index.translog.flush_threshold_size 参数默认值是512MB

减少副本数量

如果我们需要大批量进行写入操作,可以先禁止Replica复制,设置index.number_of_replicas:0 关闭副本。再写入完成后,Replica修改回正常的状态。

内存设置

jvm.options
不能超过物理内存的50%
堆内存的大小最好不要超过32GB

-Xms31g
-Xmx31g

ElasticSearch选举Master节点

Elasticsearch 的分布式原理

Elasticsearch会对存储的数据进行切分,将数据划分到不同的分片上,同时每一个分片会保存多个副本,主要是为了保证分布式环境的高可用。在 Elasticsearch 中,节点是对等的,节点间会选取集群的 Master,由 Master 会负责集群状态信息的改变,并同步给其他节点。

Elasticsearch 的性能会不会很低:只有建立索引和类型需要经过 Master,数据的写入有一个简单的Routing规则,可以路由到集群中的任意节点,所以数据写入压力是分散在整个集群的。

Elasticsearch 如何 选举 Master

Elasticsearch 的选主是ZenDiscovery模块负责的,主要包含Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要ping通)这两部分;

  • 确认候选主节点的最少投票通过数量,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes;
  • 对所有候选 master 的节点(node.master: true)根据 nodeId 字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。
  • 如果对某个节点的投票数达到阈值,并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。

补充:master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data节点可以关闭http功能。

Elasticsearch是如何避免脑裂现象

(1)当集群中 master 候选节点数量不小于3个时(node.master: true),可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes),设置超过所有候选节点一半以上来解决脑裂问题,即设置为 (N/2)+1

(2)当集群master候选节点 只有两个时,这种情况是不合理的,最好把另外一个node.master改成false。如果我们不改节点设置,还是套上面的(N/2)+1公式,此时discovery.zen.minimum_master_nodes应该设置为2。这就出现一个问题,两个master备选节点,只要有一个挂,就选不出master

猜你喜欢

转载自blog.csdn.net/flash_love/article/details/131847659