前言
本篇就是 es 学习笔记的倒数第二篇咯,集中于 es 的集群管理。同样的,本篇也是较少涉及相关配置文件的编写,主要聚焦于步骤方法总结以及本人感悟。之前的几篇也在下面啦~
【Elasticsearch学习笔记-基础篇1】Elasticsearch介绍及设计概念
【Elasticsearch学习笔记-基础篇2】Elasticsearch倒排索引、分析及打分
【Elasticsearch学习笔记-基础篇3】Elasticsearch 聚集(aggregation)与过滤器(filter)
【Elasticsearch学习笔记-基础篇4】Elasticsearch 文档间的关系
老规矩,先奉上学习地图:
实际上在集群管理中,文章中基本上是向外扩展的内容,因为像是慢查询、线程池、容灾这些问题都比较老生常谈,在这里我就不过多总结了。
1. 向集群中加入一个节点
在开始前,我们先复习一下广播、多播、单播。
传送门:https://blog.youkuaiyun.com/qq_25800311/article/details/100212574
在 es 中,一个节点加入集群有两种传统方式:多播(组播)、单播。
- 多播(组播)
也可以称为“组播”,将网络中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。
网上视频会议、视频点播比较时候使用多播。因为如果用单播,逐个节点传输,有多少个目标节点就会有多少次传送过程,这样显然效率极低,是不可取的;如果采用不区分目标,全部发送的广播方式,虽然一次可以传送完数据,但是显然达不到区分特定数据接收对象的目的。
采用多播方式,既可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。
IP网络的多播一般通过多播IP来实现。多播IP地址就是D类IP地址,即224.0.0.0至239.255.255.255之间的IP地址。
现在知道为啥 es 的默认组播地址是 224.2.2.4 了吧,而且也就是说明,只要知道 cluster.name 就可以在广域网上找到集群。
- 单播
信息的接受和传递只在两个节点之间进行,点对点通信。网络上绝大部分数据是以单播的形式传输的,例如在收发电子邮件、浏览网页时,必须与邮件服务器、Web服务器建立连接,此时使用的就是单播数据传输方式。
在 es 中,单播类似于 “口口相传”。举个例子,如果现在有7个不同集群的节点,其中3个节点的地址在 A 节点的配置中,4个节点的地址在 B 节点的配置中,而 A 与 B 节点的地址在 C 节点的配置中,那么 C 节点就会知道 7 + A+ B 节点的地址,并通过 cluster.name 选择集群加入。
与组播比起来,单播会更安全、应用的也更广泛。而且据我了解,目前的云服务器如果不特殊配置,应该都不支持组播服务了。
但如果是集群多繁杂,又需要快速验证某些功能的时候,组播或许也是个不错的选择。
解决脑裂
首先,什么是脑裂?
在一个高可用系统中,当联系着的节点断开联系时,本来为一个整体的系统,分裂成两个独立节点,两个节点开始争抢共享资源造成系统混乱、数据损坏的现象,成为“脑裂”。
简单来说,就是原来1个集群中的节点一起协同做事,但由于某些原因,有些节点断开了连接,断开的两个部分都认为是对方失去了联系,于是没有主节点的集群重新推选出主节点。至此,由1个集群脑裂为2个集群。
而解决办法也是非常简单,将 es 集群主节点选举条件设置为:
选举节点数 = (总节点数 / 2) + 1
即集群中候选节点要大于设置值的一半,这样脑裂的少部分节点就不会有主节点,也就不会形成新的集群。
2. 在集群中删除节点
集群中的某一个节点失联,实际上就算是集群中删除了一个节点。在损失节点后,es 集群状态会变成黄色,但由于 es 的特性,如果集群中每一组主副分片,至少保证有一个分片存在,就不会造成数据的丢失,并且 es 会重新选举和生成分片,最后均衡配载。
所以,在分布式和容灾处理这块,es 个人认为做的还是很优秀的。
但如果一组主副分片都丢失了,那就只能重新索引数据了。
3. 在集群中升级节点
节点升级的操作步骤相比于加入节点和删除节点要稍微多一些。
在生产环境中,如果大面积停机升级集群在某些情况下是不被允许的。由于 es 的主副分片特性,我们可以采用 轮流重启 的策略来升级集群中的所有节点。一般来说,针对每个节点,我们有以下步骤:
- 关闭集群分配设置。(重要)
这样在关闭并升级一个节点时,无论是重新被选举的主分片还是原来的主分片,都不会集群中重新分配分片。如果这一步省略了,重新处理的各种开销会更大。PUT _cluster/settings { "persistent": { "cluster.routing.allocation.enable": "none" } }
- 关闭升级节点
- 升级节点
- 启动升级后节点
- 等待升级节点加入集群
- 开启集群分配设置
对应上面关闭的操作,如果在升级过程中,主分片有新写入的数据,回复时间可能会更长。PUT _cluster/settings { "persistent": { "cluster.routing.allocation.enable": "all" } }
- 等待集群状态变成绿色
实际上,黄色也没有关系,集群不影响使用。
需要注意的是,上面的步骤是升级每一个节点的必须步骤,而不是整个集群的。
分片复制机制
在 es 中,主分片向副分片复制数据并不是比对文档,将不一致的文档进行复制,而是比对多个文档组成的分段(segment),如果分段缺失或分段不一致,那么主分片就会复制这整个分段到副分片。所以就可能会出现虽然新增的文档不多,但同步很慢的情况。
4. 扩展策略
在扩展集群时,有些问题我们需要避免注意。
分片标准
在之前的讨论中,事实上我们从来没有讨论过分片与节点的关系。
而实际上,这块往往也是最复杂的内容,脱离场景的话,我们也无法凭空而论说分片与节点的关系怎样是最好的。这里我们做一个简化版的讨论,为一般情况提供思路。这两者的关系无非就是分片比节点多、少和相同。
首先,分片比节点少肯定不是一个好方法,因为这样会有空节点的出现,白白浪费内存资源。
其次,分片(主 + 副)与节点数一样多也不是一个好方法,因为部署性价比不高,节点之间过多的通信会导致效率的下降。
一般来说,我会采取 主分片数 = 节点数 的配置来配置集群。现在,问题就变为我们该以什么标准来对场景的主分片数做期望。
据网上的建议(不清楚靠不靠谱),一个分片的最大容量最好不要超过30g,推荐为5g左右。 大家可以结合这个标准与实际场景,来估算一下具体的主分片数。
多索引优化
我们可以为索引指定主分片的数量,来更好的做分片管理。实际上,我们也应该尽量合理的根据业务切分索引,而不是把所有数据都放在一个索引下。
这个感觉就很象是微服务切分,或者开发模块的切分,是门手艺,需要的是经验。
最大化吞吐量
这里面间接的体现为加速索引和查询的效率,在具体技术操作上,我们也可以达到这一点。
在索引数据的时候,整个过程是:主分片索引数据+副分片同步数据。那么,呼之欲出的解决办法就是我们需要在大量索引数据时,减少副分片的数量。
与之相反的是,在搜索时,副分片理论是越多越好的,es 会在主副分片间做负载均衡。
5. 索引别名
这个比较强的是,非常便于开发,相当于在分片与代码之间多了一个映射层,即使底层的别名指向新的分片,也不需要更改代码。
POST /_aliases
{
"actions": [
{
"add": {
"alias": "nickname",
"index": "index"
}
},
{
"remove": {
"alias": "nickname",
"index": "index"
}
}
}
6. 自定义路由
通过之前的学习得知,在没有自定义路由前,es 自动根据文档 id 的散列值作为路由,将文档分配到不同的分片中。但如果,我们将现实(或业务)意义作为路由,通过名词的散列值,将相同现实(或业务)意义的文档分配在同一路由下,即在同一分片中。
PUT my_index/_doc/1?routing=user1&refresh=true
{
"title": "This is a document"
}
GET my_index/_doc/1?routing=user1
or
GET my_index/_search?routing=user1,user2
{
"query": {
"match": {
"title": "document"
}
}
}
这样的最大好处,就是在管理数据的同时,能够最大限度地 缩小搜索范围 ,让搜索可以在特定的分片子集中执行。即使集群中有100个甚至更多的分片也没有关系,因为实际上搜索的范围也只是路由导向的分片。
值得注意的是,由于 主分片是没办法在有数据之后更改数量 ,因此建议在一开始确定好分片数量与路由数量的关系。 个人推荐在 主分片数量较多 时使用路由,在主分片较少时,性能差别不大。