1、Elasticsearch 中的倒排索引是什么?
倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。区别于传统的正向索引,倒排索引会再存储数据时将关键词和数据进行关联,保存到倒排表中,然后查询时,将查询内容进行分词后在倒排表中进行查询,最后匹配数 据即可。Elasticsearch 使用一种称为倒排索引的结构,ES中的倒排索引其实就是 lucene 的倒排索引,它适用于快速的全文搜索。正向索引(forward index),就是搜索引擎会将待搜索的文件都对应一个文件 ID,搜索时将这个ID 和搜索关键字进行对应,形成 K-V 对,然后对关键字进行统计计数。但是互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。所以,搜索引擎会将正向索引重新构建为反向索引(inverted index,倒排索引),即把文件ID对应到关键词的映射,转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,并保存到倒排表中,查询时会将内容进行分词后在倒排表中进行查询,最后匹配数据即可。这些文件中都出现这个关键词。
2、Elasticsearch 中的集群、节点、索引、文档、类型是什么?
集群:是一个或多个节点(服务器)的集合,它们共同保存您的整个数据,并提供跨所有节点的联合索 引和搜索功能。集群由唯一名称标识,默认情况下为“elasticsearch”。此名称很重要,因为如果节点设 置为按名称加入群集,则该节点只能是集群的一部分。
节点:属于集群一部分的单个服务器。它存储数据并参与群集索引和搜索功能。
索引:就像关系数据库中的“数据库”。它有一个定义多种类型的映射。索引是逻辑名称空间,映射到一 个或多个主分片,并且可以有零个或多个副本分片。 MySQL =>数据库 Elasticsearch =>索引
文档:类似于关系数据库中的一行。不同之处在于索引中的每个文档可以具有不同的结构(字段),但 是对于通用字段应该具有相同的数据类型。 MySQL => Databases => Tables => Columns / Rows Elasticsearch => Indices => Types =>具有属性的文档
类型:是索引的逻辑类别/分区,其语义完全取决于用户。
3、什么是近实时搜索?
在 Elasticsearch 和磁盘之间是文件系统缓存。在内存索引缓冲区中的文档会被写入到一个新的段中。 但是这里新段会被先写入到文件系统缓存,这一步代价会比较低,稍后再被刷写到磁盘—这一步代价比较高。不过只要文件已经在缓存中,就可以像其它文件一样被打开和读取了。在 Elasticsearch 中,写入和打开一个新段的轻量的过程叫做 refresh 。 默认情况下每个分片会每秒自动刷新一次,即刷新文件系统缓存。这就是为什么我们说 Elasticsearch 是 近实时搜索:文档的变化并不是立即对搜索可见,但会在一秒之内变为可见。这些行为可能会对新用户造成困惑:他们索引了一个文档然后尝试搜索它,但却没有搜到。这个问题的解决办法是用 refresh API 执行一次手动刷新:/users/_refresh。
4、如何理解 Elasticsearch 的近实时的性质,并改善它的不足?
并不是所有的情况都需要每秒刷新。可能你正在使用 Elasticsearch 索引大量的日志文件,你可能想优化索引速度而不是近实时搜索, 可以通过设置 refresh_interval , 降低每个索引的刷新频率。refresh_interval 可以在既存索引上进行动态更新。 在生产环境中,当你正在建立一个大的新索引时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来。
# 关闭自动刷新
PUT /users/_settings
{ "refresh_interval": -1 }
# 每一秒刷新
PUT /users/_settings
{ "refresh_interval": "1s" }
5、是否了解字典树?
常用字典数据结构
-
排序列表Array/List:使用二分法查找,不平衡 -
HashMap/TreeMap:性能高,内存消耗大,几乎是原始数据的三倍 -
Skip List 跳跃表:可快速查找词语,在lucene、redis、Hbase等均有实现。相对于TreeMap等结构,特别 适合高并发场景(Skip List介绍) -
Trie:适合英文词典,如果系统中存在大量字符串且这些字符串基本没有公共前级,则相应的trie树将非常消耗内存(数据结构之trie树) -
Double Array Trie:适合做中文词典,内存占用小。很多分词工具均采用此种算法(深入双数组Trie) -
Ternary Search Tree 三叉树:每一个node有3个节点,兼具省空间和查询快的优点(Ternary Search Tree) -
Finite State Transducers(FST) :一种有限状态转移机,Lucene4有开源实现,并大量使用
字典树又称单词查找树,Trie 树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排 序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是: 利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高(空间换时间)。
- Trie 的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有 3 个基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
对于中文的字典树,每个节点的子节点用一个哈希表存储,这样就不用浪费太大的空间,而且查询速度上 可以保留哈希的复杂度 O(1)。
6、Elasticsearch 索引文档的流程?
首先客户端向集群发出索引文档的请求,它会选择任何一个节点,这个节点当接收到请求后会根据路由算法找到应该放的那个主分片的位置,从而索引数据,之后为了保证数据的完整性,它会将它的副本数据进行同步,同步完成后客户端就可以进行访问了。细节方面:
用户的索引请求发过来之后,首先协调结点默认使用文档ID参与哈希计算(也支持通过routing),shard = hash(document_id) % (num_of_primary_shards),即分片位置索引 = 将文档ID或路由ID进行哈希计算后的值 % 所有分片总数。随后会在内存(memory)中建立一个索引(Index),这个Index会在内存中形成一个分段对象(Segment),为了防止数据出现问题,会同时在索引数据之后写入到日志(Translog)当中,在此过程中,每隔1秒钟,会向Segment会将数据刷新到系统文件缓存区(OS Cache),以方便接收用户的查询,因为如果让用户查询直接访问内存或磁盘,会使速度变慢。当过了30分钟或者Translog中的数据超过了512M,Os Cache中的Segment会将数据刷写(flush)到磁盘当中,刷写后内存中的缓冲将被清除。此时一旦刷写的数据比较多了的话(磁盘中有多个Segment),磁盘就会将这些分段进行合并。
7、Elasticsearch 搜索的流程?
- 搜索被执行成一个两阶段过程,我们称之为 Query Then Fetch(查询后取回);
- 在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本
地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询 Filesystem Cache 的,但是有部分数据还在 Memory Buffer,所以搜索是近实时的。 - 每个分片返回各自优先队列中 所有文档的 ID 和排序值 给 协调节点,它合并这些值到自己的优先队
列中来产生一个全局排序后的结果列表。 - 接下来就是取回阶段,协调节点辨别出哪些文档需要被取回,并向相关的分片提交多个 GET 请求。每
个分片加载并丰富文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了, 协调节点返回结果给客户端。 - Query Then Fetch 的搜索类型在文档相关性打分的时候参考的是本分片的数据,这样在文档数量较少
的时候可能不够准确,DFS Query Then Fetch 增加了一个预查询的处理,询问 Term 和 Document frequency,这个评分更准确,但是性能会变差。
8、并发情况下,Elasticsearch 如果保证读写一致?
- 可以通过版本号使用乐观锁并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;
- 对于写操作:一致性级别支持 quorum/one/all,默认为 quorum。
- quorum:即只有当大多数(一半以上)分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。
- one:即只要主分片数据保存成功,那么客户端就可以进行查询操作了。
- all:是最高的一致性级别,要求所有分片的数据要全部保存成功,才可以继续进行。
- 对于读操作:可以设置 replication 为 sync(默认为同步),这使得操作在主分片和副本分片都完成后才会返回;设置 replication 为 async(异步)时,也可以通过设置搜索请求参数_preference 为 primary 来查询主分片,确保文档是最新版本。
9、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 为主节点个数 (即有资格成为主节点的节点个数)
- 角色分离:即 master 节点与 data 节点分离,限制角色
主节点配置为:node.master: true node.data: false 从节点配置为:node.master: false node.data: true
10、Elasticsearch 的 master 选举流程?
- Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个 RPC 来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分
- 对所有可以成为 master 的节点(node.master: true)根据 nodeId 字典排序,每次选举每个节点都把自
己所知道节点排一次序,然后选出第一个(第 0 位)节点,暂且认为它是 master 节点。 - 如果对某个节点的投票数达到一定的值**(可以成为 master 节点数 n/2+1)并且该节点自己也选举自己**,
那这个节点就是 master。否则重新选举一直到满足上述条件。 - master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http
功能。
|