1. 节点

节点按照职责可以分为 master节点数据节点协调节点,每个节点类型可以进行单独配置。默认情况下,集群不会对节点角色进行划分,所有节点都是平等的,可以担任所有的职责。但是在生产环境中需要对这些节点的角色进行最优划分,否则在高并发请求的情况下,集群容易出现服务阻塞超时甚至服务崩溃的隐患。

1. master节点

负责维护整个集群的相关工作,管理集群的变更,如创建/删除索引、节点健康状态监测、节点上/下线等。

master节点是由集群节点通过选举算法选举出来的,一个集群中只有一个节点可以成为master节点,但是可以有一个或多个节点参与master节点的选举。在默认情况下,任意节点都可以作为master的候选节点,可以通过配置项node.master对当前节点是否作为master的候选节点进行控制。

为了避免选举时产生脑裂,建议master的候选节点数量是 2n+1。

2. 数据节点

主要负责索引数据的保存工作,此外也执行数据的其他操作,如文档的删除、修改和查询操作。

数据节点的很多工作是调用Lucene库进行Lucene索引操作,因此这种节点对于内存和I/O的消耗比较大,生产环境中应多注意数据节点的计算机负载情况。

3. 协调节点

客户端可以向ES集群的节点发起请求,这个节点叫作协调节点。

在默认情况下,协调节点可以是集群中的任意节点,此时它的生命周期是和一个单独的请求相关的。也就是说,当客户端向集群中的某个节点发起请求时,此时该节点被称为当前请求的协调节点;当它将响应结果返回给客户端后,该协调节点的生命周期就结束了。

当然,为了降低集群的负载,可以设置某些节点作为单独的协调节点。在节点的配置文件中设置 node.master 和 node.data 配置项为 false,此时,这个节点就不会被选中为master节点并且不再担任数据节点,而客户端就可以把这类节点作为协调节点来使用,把所有的请求都分发到这些节点上。

2. 分片

一个分片(shard)是一个最小级别“工作单元(worker unit)”,它只是保存了索引中所有数据的一部分。

分片可以是主分片(primary shard) 或者是 复制分片(replica shard)(或副分片)。

比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,ES 提供了将索引划分成多份的能力,这些份就叫做 分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因:

  • 允许你水平分割/扩展你的内容容量。
  • 允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量。

至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由 ES 管理的,对于作为用户的你来说,这些都是透明的。

分片还有 副本 replica shard,在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,ES 允许你创建分片的一份或多份拷贝,这些拷贝叫做 复制分片,或者直接叫复制。

2.1. 主分片与副分片

1. 是否后期可修改
  • 主分片数量,在创建索引时指定。创建索引后不能修改。
  • 副分片数量,在创建索引后,依然能修改。

因为数据存储时,会拿hash值,根据索引中主分片数量取余,定位所在分片的地址。如果后续修改了主分片数量,会导致数据的地址错误。

2. 数量
  • 主分片:默认数量为5,单个分片的大小建议在20G~50G。主分片的数量决定了索引最多能存储多少数据(实际的数量取决于数据、硬件和应用场景)。
  • 副分片:默认数量为1。副分片的数量保证数据的高可靠性,防止数据丢失。

每个主分片都有一个或多个副本分片,当主分片异常时,副本可以提供数据的查询等操作。

主分片和对应的副本分片是不会在同一个节点上的,所以副本分片数的最大值是 n -1(其中 n 为节点数)

3. 意义
  • 主分片:可水平分割/扩展的内容容量;可在分片之上进行分布式的、并行的操作,进而提供性能/吞吐量。
  • 副分片:在分片/节点失败的情况下,提供了高可用性。(注意:副本不能与对应的主分片置于同一节点上);扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行。
4. 副分片选举生成主分片

es为了更好地稳定性和容灾,除了进行必要的索引备份外,副本的添加可以更好地维持集群数据完整性。

当出现某个节点从集群脱离,在集群其他节点的副本,此时会选举出主分片,所以这里就有主分片和副本之间的数据同步问题。

es 的分片选举参考:https://blog.csdn.net/lingboo111/article/details/130958145

5. 主从复制

当有数据写入时,请求节点收到请求,先转给主分片所在节点上执行请求,如果成功,它转发请求到相应的几个复制分片所在的节点上。当所有的复制分片报告成功,主分片所在节点报告成功到请求的节点,请求的节点再报告给客户端。

6. 增减节点时,分片自动负载均衡

假设有6台机器总共7个分片, 肯定就会有1台机器有2个分片, 此时给 es 集群新增1台机器进来,那么有2个分片的那台机器会自动给1个分片分配给新加入的机器上去.

2.2. 集群状态

ES 的集群监控信息中包含了许多的统计数据,其中最为重要的一项就是集群健康。集群健康存储在status字段中,主要包括 green、yellow、red 三种状态。它的三种颜色含义如下:

  • green:所有的主分片和副本分片都正常运行。
  • yellow:所有的主分片都正常运行,但不是所有的副本分片都正常运行。
  • red:有主分片没能正常运行。

3. 索引

3.1. 倒排索引

1. 正排索引

在说倒排索引之前我们先说说什么是正排索引。
正排索引也称为"前向索引",它是创建倒排索引的基础。这种组织方法在建立索引的时候结构比较简单,建立比较方便且易于维护;因为索引是基于文档建立的,若是有新的文档加入,直接为该文档建立一个新的索引块,挂接在原来索引文件的后面。若是有文档删除,则直接找到该文档号文档对应的索引信息,将其直接删除。
他适合根据文档ID来查询对应的内容。但是在查询一个 keyword 在哪些文档里包含的时候需对所有的文档进行扫描以确保没有遗漏,这样就使得检索时间大大延长,检索效率低下。

文档ID文档内容
1elasticsearch是最流行的搜索引擎
2php是世界上最好的语言
3搜索引擎是如何诞生的

只能在一起简单的场景下使用,如果通过 keyword 来挨个检索,性能太低。

2. 倒排索引

根据字面意思可以知道他和正序索引是反的。在搜索引擎中每个文件都对应一个文件ID,文件内容被表示为一系列关键词的集合。例如“文档1”经过分词,提取了3个关键词,每个关键词都会记录它所在在文档中的出现频率及出现位置。
那么上面的文档及内容构建的倒排索引结果会如下:

单词文档ID列表
elasticsearch1
流行1
搜索引擎1,3
php2
世界2
最好2
语言2
如何3
诞生3

比如我们要查询‘搜索引擎’这个关键词在哪些文档中出现过。

  1. 首先,我们通过倒排索引可以查询到,该关键词出现的文档位置是在1和3中;
  2. 然后,再通过正排索引查询到文档1和3的内容,并返回结果。

3.2. 索引不可变

ES 的搜索高效的原因并不是像 Redis 那样重依赖内存的,而是通过建立特殊的索引数据结构--倒排索引实现的。倒排索引可以说是 ES 搜索高效和支持非结构化数据检索的主要原因了,但是倒排索引被写入磁盘后是不可改变的,它永远不会修改

写入磁盘的倒排索引是不可变的,优势主要表现在:

  • 不需要锁。因为如果从来不需要更新一个索引,就不必担心多个程序同时尝试修改,也就不需要锁。
  • 一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性,只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。
  • 其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。
  • 写入单个大的倒排索引,可以压缩数据,较少磁盘 IO 和需要缓存索引的内存大小。

当然,不可变的索引有它的缺点:

  • 当对旧数据进行删除时,旧数据不会马上被删除,而是在 .del文件中被标记为删除。而旧数据只能等到段更新时才能被移除,这样会造成大量的空间浪费。
  • 若有一条数据频繁的更新,每次更新都是新增新的标记旧的,则会有大量的空间浪费。
  • 每次新增数据时都需要新增一个段来存储数据。当段的数量太多时,对服务器的资源例如文件句柄的消耗会非常大。
  • 在查询的结果中包含所有的结果集,需要排除被标记删除的旧数据,这增加了查询的负担。

总体来看,倒排索引是拿空间换时间。牺牲了空间资源,追求更高的性能。

3.3. 段

1. 段

的概念提出主要是因为:在早期全文检索中为整个文档集合建立了一个很大的倒排索引,并将其写入磁盘中。如果索引有更新,就需要重新全量创建一个索引来替换原来的索引。这种方式在数据量很大时效率很低,并且由于创建一次索引的成本很高,所以对数据的更新不能过于频繁,也就不能保证时效性。

而且在底层采用了分段的存储模式,使它在读写时几乎完全避免了锁的出现,大大提升了读写性能。(像 ConcurrentHashMap 的分段思想)。

2. 提交点

每一个段本身都是一个倒排索引,但索引在 Lucene 中除表示所有段的集合外,还增加了提交点的概念。

为了提升写的性能,Lucene并没有每新增一条数据就增加一个段,而是采用延迟写的策略,每当有新增的数据时,就将其先写入内存中,然后批量写入磁盘中。若有一个段被写到硬盘,就会生成一个提交点,提交点:就是一个列出了所有已知段和记录所有提交后的段信息的文件

3. 段合并

由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。

Elasticsearch通过在后台进行段合并来解决这个问题。小的段被合并到大的段,然后这些大的段再被合并到更大的段。

4. 近实时搜索

ES 是怎么做到近实时全文搜索?

磁盘是瓶颈。提交一个新的段到磁盘需要fsync操作,确保段被物理地写入磁盘,即时电源失效也不会丢失数据。但是fsync是昂贵的,严重影响性能,当写数据量大的时候会造成 ES 停顿卡死,查询也无法做到快速响应。

所以fsync不能在每个文档被索引的时就触发,需要一种更轻量级的方式使新的文档可以被搜索,这意味移除fsync。

为了提升写的性能,ES 没有每新增一条数据就增加一个段到磁盘上,而是采用 延迟写 的策略。

每当有新增的数据时,就将其先写入到内存中,在内存和磁盘之间是文件系统缓存,当达到默认的时间(1秒钟)或者内存的数据达到一定量时,会触发一次刷新(Refresh),将内存中的数据生成到一个新的段上并缓存到文件缓存系统上,稍后再被刷新到磁盘中并生成提交点。

这里的内存使用的是ES的JVM内存,而文件缓存系统使用的是操作系统的内存。新的数据会继续的被写入内存,但内存中的数据并不是以段的形式存储的,因此不能提供检索功能。由内存刷新到文件缓存系统的时候会生成了新的段,并将段打开以供搜索使用,而不需要等到被刷新到磁盘。

在 Elasticsearch 中,这种写入和打开一个新段的轻量的过程叫做 refresh (即内存刷新到文件缓存系统)。默认情况下每个分片会每秒自动刷新一次。 这就是为什么说 Elasticsearch 是近实时的搜索了:文档的改动不会立即被搜索,但是会在一秒内可见。


KerryWu
641 声望159 粉丝

保持饥饿