如果你平时负责维护 Elasticsearch(ES)集群,肯定遇到过这样的场景:业务说查询扛不住了,第一反应是哪台机器 CPU 打满,第二反应是加节点。而在 Serverless 环境下,加节点这件事变得既方便又昂贵,于是一个很自然的念头就冒出来了——不是有副本吗?让 ES 副本去分担读流量,这不就是现成的负载均衡吗?
这个思路在原理上完全成立。ES 的副本分片(replica shard)确实能承接读请求,只要把副本数和节点分布规划好,查询压力就能被摊到多台机器上。但真正落地时你会发现,Serverless 的弹性伸缩、计费模式、分片调度都会和副本机制产生一系列冲突,处理不好,轻则副本一直 unassigned,重则集群性能比原来更差。这篇文章就围绕“Serverless 中用于负载均衡的 ES 副本”这个命题,把原理、配置、踩坑一次讲透,适合准备把 ES 迁到 Serverless 平台、或者正在优化现有 ES 读能力的团队参考。
1. 先把概念拆干净:ES 副本凭什么能做负载均衡
1.1 主分片与副本分片的职责分工
很多刚接触 ES 的人会把“副本”理解成简单的数据备份,这个理解没错,但不够完整。ES 里一个索引的数据会拆成多个分片(shard),每个分片本质上是一个完整的 Lucene 索引。分片分为主分片(primary shard)和副本分片(replica shard)。主分片负责接收写入请求,副本分片从主分片同步数据,主要负责两件事:一是高可用,主分片所在节点挂了,副本可以被提升为新的主分片;二是分摊读压力,查询请求可以打到任意一个分片副本上,而不必每次都访问主分片。
这里的关键点在于“读请求可以打到副本上”。因为 ES 的查询分发机制会把一次搜索请求同时发到目标分片的所有可用副本上,而不是只发给主分片。也就是说,如果你有一个主分片和两个副本分片,分布在不同节点上,那么一次查询会被拆成三份并行执行,每个节点只处理自己持有的那份数据,最后在协调节点汇总。这就是副本参与负载均衡的基本原理。
用大白话讲:主分片就像项目的正式版本,只有它能被编辑;副本就像分发给团队成员的只读拷贝,大家看自己的拷贝干活,不用挤在一起抢正式版。你的正式版只有一份,但只读拷贝可以有很多份,分到不同人手里,效率自然就上来了。
理解这个分工之后,你就明白了为什么副本数是可以动态调整的。主分片数量在创建索引时一旦确定就改不了,需要 reindex 才能调整;而副本数量随时可以改,一条命令就能把 1 个副本变成 2 个或 3 个。这个特性对 Serverless 环境尤其重要,因为你可以在流量波峰临时上调副本数,波谷再降回来,代价只是等待分片复制完成的那几分钟。
1.2 一次搜索请求是如何被“扇出”到副本的
要说清楚副本怎么帮 ES 实现负载均衡,得先看一次完整查询的路径。假设客户端向集群发了一个 GET /products/_search 请求,请求先到达某个节点,这个节点可能是任意一个节点,因为 ES 集群中每个节点默认都能接收请求,节点之间可以互相转发。接收请求的这个节点,在这一轮查询里就充当了协调节点(coordinating node)。
协调节点拿到查询后,并不会自己把整个索引都搜一遍,而是先根据索引的分片配置,算出这个索引一共有多少个分片,然后向每个分片的主分片和所有副本分片发起内部查询请求。ES 的默认搜索行为是“所有分片都查,结果合并”,也就是说,一次查询实际是并行发给了索引的全部分片(包括副本),每个分片只负责查自己那部分数据,返回局部排序后的 top N 结果,协调节点再把所有局部结果合并、排序、分页,最终返回给客户端。
这个过程在 ES 里叫“扇出”(fan-out)。正是这种扇出机制,让 ES 天然具备了分布式并行查询的能力。当一个查询进来时,如果索引有 16 个主分片、每个主分片有 1 个副本,那么这 16 个分片对(shard copy)分散在多个节点上,查询就会被均匀地打给所有分片副本。从负载均衡的角度看,ES 自动帮你做了最粗粒度的流量分散——不管请求落在哪个节点,最终计算压力都会被摊到全集群。
这里有个细节值得注意:ES 在分发查询时,对每个分片对只会选择一个副本去查,不会同时查主分片和它的副本,否则结果就重复了。默认情况下,ES 会尽量选择“本地优先”的副本,也就是和协调节点在同一台机器上的那个副本,这样可以节省网络开销。所以如果你在 A 节点上发起查询,而某个分片的副本恰好也在 A 节点,那么这次查询就只用本地磁盘,不走网络,性能最好。
1.3 “等开销负载均衡”这句话应该怎么理解
热词里有一句“等开销负载均衡”,这句话看起来有点绕,但在 ES 副本场景里非常关键。先说结论:所谓等开销,指的是当每个分片上的查询计算量、IO 开销基本相等时,集群整体的负载分布才是最均衡的。
ES 默认的分片分配策略目标是尽量
