基本概念

  • 文档型存储的中间件
  • 以JSON格式进行数据存储
  • 支持对文档数据的增删改查,即CRUD操作。即CRUD操作。
  • 使用了分片和复制技术,支持处理超大规模数据。
  • 基于Java编写
  • Elasticsearch基于Hash模式进行分片
  • 集群的配置方式: Elasticsearch天然是分布式的,主副分片自动分配和复制
  • 全文检索: Elasticsearch全文检索功能强大,字段自动索引
  • Elasticsearch适合用于搜索以及存储日志数据(全文检索),但数据并非实时,却决于refresh_interval参数的配置

集群中概念

  • 集群(cluster): 由一个或多个节点组成, 并通过集群名称与其他集群进行区分

  • 节点(node): 单个 ElasticSearch 实例. 通常一个节点运行在一个隔离的容器或虚拟机中

  • 索引(index): 在 ES 中, 索引是一组文档的集合

  • 分片(shard): 因为 ES 是个分布式的搜索引擎, 所以索引通常都会分解成不同部分, 而这些分布在不同节点的数据就是分片. ES自动管理和组织分片, 并在必要的时候对分片数据进行再平衡分配, 所以用户基本上不用担心分片的处理细节.

  • 副本(replica): ES 默认为一个索引创建 5 个主分片, 并分别为其创建一个副本分片. 也就是说每个索引都由 5 个主分片成本, 而每个主分片都相应的有一个 copy。对于分布式搜索引擎来说, 分片及副本的分配将是高可用及快速搜索响应的设计核心.主分片与副本都能处理查询请求,它们的唯一区别在于只有主分片才能处理索引请求.副本对搜索性能非常重要,同时用户也可在任何时候添加或删除副本。额外的副本能给带来更大的容量, 更高的呑吐能力及更强的故障恢复能力。

  • 在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了

关于分片配置

分配分片时主要考虑数据集的增长趋势.

要知道, 分配的每个分片都是有额外的成本的:

  • 每个分片本质上就是一个Lucene索引, 因此会消耗相应的文件句柄, 内存和CPU资源
  • 每个搜索请求会调度到索引的每个分片中. 如果分片分散在不同的节点倒是问题不太. 但当分片开始竞争相同的硬件资源时, 性能便会逐步下降
  • ES使用词频统计来计算相关性. 当然这些统计也会分配到各个分片上. 如果在大量分片上只维护了很少的数据, 则将导致最终的文档相关性较差

如果真的担心数据的快速增长, 建议多关心这条限制: ElasticSearch推荐的最大JVM堆空间是30~32G, 所以把你的分片最大容量限制为30GB, 然后再对分片数量做合理估算. 例如, 数据能达到200GB, 推荐最多分配7到8个分片.

总之, 不要现在就为可能在三年后才能达到的10TB数据做过多分配. 如果真到那一天, 也会很早感知到性能变化的.

在开始阶段, 一个好的方案是根据你的节点数量按照1.5~3倍的原则来创建分片. 例如,如果有3个节点, 则推荐创建的分片数最多不超过9(3x3)个.

但每个索引的数据量只有1GB甚至更小. 对于这种类似场景, 建议只需要为索引分配1个分片.

如果使用ES的默认配置(5个分片), 并且有基于日期的索引需求, 那么6个月下来, 拥有的分片数将达到890个. 再多的话, 集群将难以工作--除非提供了更多(例如15个或更多)的节点.

分片副本

分片

  • 分片是Elasticsearch索引的数据块。Elasticsearch会将每个索引划分为多个分片,分布在不同节点上。
  • 分片的好处是可以支持水平扩展,提高查询效率。比如一个索引有3个主分片,此时每个节点可以存储部分数据。
  • 我们可以在创建索引时就预先设置主分片数量,默认是5个,但数量不可变更。

副本

  • 副本是主分片的一个完整拷贝,它可以提供冗余,防止主分片损坏的情况。
  • 我们可以设置每个主分片的副本数量, 默认是1个。副本可以动态调整。
  • 副本会与主分片均衡分布在不同节点。

ES各种节点的分工

  • 客户端节点

  当主节点和数据节点配置都设置为false的时候,该节点只能处理路由请求,处理搜索,分发索引操作等,从本质上来说该客户节点表现为智能负载平衡器。独立的客户端节点在一个比较大的集群中是非常有用的,他协调主节点和数据节点,客户端节点加入集群可以得到集群的状态,根据集群的状态可以直接路由请求。

  • 数据节点

  数据节点主要是存储索引数据的节点,主要对文档进行增删改查操作,聚合操作等。数据节点对cpu,内存,io要求较高, 在优化的时候需要监控数据节点的状态,当资源不够的时候,需要在集群中添加新的节点。

  • 主节点

  主资格节点的主要职责是和集群操作相关的内容,如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。稳定的主节点对集群的健康是非常重要的,默认情况下任何一个集群中的节点都有可能被选为主节点,索引数据和搜索查询等操作会占用大量的cpu,内存,io资源,为了确保一个集群的稳定,分离主节点和数据节点是一个比较好的选择.

建议

在一个生产集群中我们可以对这些节点的职责进行划分,建议集群中设置3台以上的节点作为master节点,这些节点只负责成为主节点,维护整个集群的状态。再根据数据量设置一批data节点,这些节点只负责存储数据,后期提供建立索引和查询索引的服务,这样的话如果用户请求比较频繁,这些节点的压力也会比较大,所以在集群中建议再设置一批client节点(node.master: false node.data: true),这些节点只负责处理用户请求,实现请求转发,负载均衡等功能。

Elasticsearch查询的基本流程

  1. 客户端发送请求: 客户端向一个任意节点(协调节点)发送查询请求。
  2. 协调节点协调: 协调节点负责将查询请求分发到涉及到的所有分片上。
  3. 分片并行处理: 每个分片在本地执行查询,并返回结果给协调节点。
  4. 协调节点合并结果: 协调节点将从各个分片收到的结果进行合并、排序,最终返回给客户端。

副本在查询过程中的作用

  • 负载均衡: 每个分片都有一个主分片和多个副本分片。当查询请求到达时,协调节点会尽量将请求分发到不同的节点上的副本分片上,从而实现负载均衡,提高查询性能。
  • 容错性: 如果某个分片的主分片不可用,协调节点会自动将请求转发到副本分片上,保证服务的连续性。

为什么说查询请求可以分发到多个副本上?

  • 分片和副本的关系: 每个分片都有一个主分片和多个副本分片。副本分片是主分片的完整副本,存储相同的数据。
  • 查询的并行性: 为了提高查询性能,Elasticsearch会将查询请求分发到多个副本分片上并行执行。
  • 结果合并: 协调节点会将所有副本分片返回的结果进行合并,最终返回给客户端。

形象地比喻:

想象一个图书馆,每个书架就是一个分片,每个分片上的书就是文档。当你要查找一本书时,图书馆管理员(协调节点)会将你的查询请求分发给多个书架(分片)上的图书管理员(副本)同时查找。每个图书管理员在自己的书架上找到相关书籍后,将结果反馈给总管理员,总管理员再将所有结果汇总,然后告诉你。

总结:

虽然查询请求是分发到分片上进行处理的,但是由于每个分片都有多个副本,所以实际上查询请求可以并行地分发到多个副本上。这种机制不仅提高了查询性能,还增强了系统的容错性。

需要注意的是:

  • 主分片和副本分片的作用不同: 主分片负责写入操作,副本分片主要用于读操作和容错。
  • 副本数量的设置: 副本数量的设置会影响查询性能、数据冗余度和存储成本。
  • 查询路由: Elasticsearch会根据查询条件和分片路由算法,将查询请求路由到合适的分片上。

集群脑裂问题

集群职责划分

elasticsearch中集群节点有不同的职责划分:

默认情况下,集群中的任何一个节点都同时具备上述四种角色。

但是真实的集群一定要将集群职责分离:

  • master节点:对CPU要求高,但是内存要求第
  • data节点:对CPU和内存要求都高
  • coordinating节点:对网络带宽、CPU要求高

职责分离可以让我们根据不同节点的需求分配不同的硬件去部署。而且避免业务之间的互相干扰。

一个典型的es集群职责划分如图:

脑裂问题

脑裂是因为集群中的节点失联导致的。

例如一个集群中,主节点与其它节点失联:

此时,node2和node3认为node1宕机,就会重新选主:

当node3当选后,集群继续对外提供服务,node2和node3自成集群,node1自成集群,两个集群数据不同步,出现数据差异。

当网络恢复后,因为集群中有两个master节点,集群状态的不一致,出现脑裂的情况:

解决脑裂的方案是,要求选票超过 ( eligible节点数量 + 1 )/ 2 才能当选为主,因此eligible节点数量最好是奇数。对应配置项是discovery.zen.minimum_master_nodes,在es7.0以后,已经成为默认配置,因此一般不会发生脑裂问题

例如:3个节点形成的集群,选票必须超过 (3 + 1) / 2 ,也就是2票。node3得到node2和node3的选票,当选为主。node1只有自己1票,没有当选。集群中依然只有1个主节点,没有出现脑裂。

小结

master eligible节点的作用是什么?

  • 参与集群选主
  • 主节点可以管理集群状态、管理分片信息、处理创建和删除索引库的请求

data节点的作用是什么?

  • 数据的CRUD

coordinator节点的作用是什么?

  • 路由请求到其它节点

  • 合并查询到的结果,返回给用户

集群分布式存储

当新增文档时,应该保存到不同分片,保证数据均衡,那么coordinating node如何确定数据该存储到哪个分片呢?

分片存储测试

插入三条数据:

测试可以看到,三条数据分别在不同分片:

结果:

分片存储原理

elasticsearch会通过hash算法来计算文档应该存储到哪个分片:

说明:

  • _routing默认是文档的id
  • 算法与分片数量有关,因此索引库一旦创建,分片数量不能修改!

新增文档的流程如下:

解读:

  • 1)新增一个id=1的文档
  • 2)对id做hash运算,假如得到的是2,则应该存储到shard-2
  • 3)shard-2的主分片在node3节点,将数据路由到node3
  • 4)保存文档
  • 5)同步给shard-2的副本replica-2,在node2节点
  • 6)返回结果给coordinating-node节点

集群分布式查询

elasticsearch的查询分成两个阶段:

  • scatter phase:分散阶段,coordinating node会把请求分发到每一个分片

  • gather phase:聚集阶段,coordinating node汇总data node的搜索结果,并处理为最终结果集返回给用户

集群故障转移

集群的master节点会监控集群中的节点状态,如果发现有节点宕机,会立即将宕机节点的分片数据迁移到其它节点,确保数据安全,这个叫做故障转移。

1)例如一个集群结构如图:

现在,node1是主节点,其它两个节点是从节点。

2)突然,node1发生了故障:

宕机后的第一件事,需要重新选主,例如选中了node2:

node2成为主节点后,会检测集群监控状态,发现:shard-1、shard-0没有副本节点。因此需要将node1上的数据迁移到node2、node3:

实现主从

在 Elasticsearch 中,可以通过以下方式实现类似 MySQL 主从的效果,并指定查询只在从节点上进行:

配置主节点和数据节点

  • 配置某些节点只作为主节点(不存储数据),而其他节点作为数据节点。这样可以确保主节点只处理管理任务,而数据节点处理数据存储和查询。

  • elasticsearch.yml 文件中配置:

    1
    2
    node.master: true
    node.data: false

    对于数据节点:

    1
    2
    node.master: false
    node.data: true

使用副本分片进行查询

  • Elasticsearch 默认会选择从所有合格的节点和分片中进行负载均衡查询。可以通过设置 cluster.routing.use_adaptive_replica_selectionfalse

    来关闭自适应副本选择,这样查询会以循环方式在所有副本之间分配【这可能会导致搜索速度变慢】。

    1
    2
    3
    4
    5
    6
    PUT /_cluster/settings
    {
    "transient": {
    "cluster.routing.use_adaptive_replica_selection": false
    }
    }

指定副本分片在特定节点上

可以使用 allocation awareness 功能,将副本分片分配到特定的节点组。

  • 首先,使用自定义节点属性指定每个节点的位置。 例如,如果希望 Elasticsearch 将分片分布在不同的机架上,可以在每个节点的配置文件 elasticsearch.yml 中设置一个名为rack_id (机架id)的感知属性。

    1
    node.attr.rack_id: rack_one

    还可以在启动节点时设置自定义属性:

    1
    ./bin/elasticsearch -Enode.attr.rack_id=rack_one
  • 通过在每个符合主节点条件的节点的配置文件 elasticsearch.yml 中设置cluster.routing.allocation.awareness.attributes,告诉 Elasticsearch 在分配分片时考虑一个或多个感知属性。

    1
    cluster.routing.allocation.awareness.attributes: rack_id 

    这样可以确保副本分片分配到指定的节点组。

查询只在特定节点上进行

  • 可以使用 preference参数来指定查询只在特定的分片上进行。例如:

    1
    GET /index/_search?preference=_shards:1

    这样可以确保查询只在指定的1分片上进行。

通过这些配置,就可以实现类似 MySQL 主从的效果,并确保查询只在从节点或特定节点上进行