
背景介绍
在数据驱动的时代,企业面临数据密集型业务场景的挑战,尤其在数据存储、检索与实时分析方面存在迫切需求。Elasticsearch(ES)作为一款开源的分布式搜索引擎,凭借其高性能、高可用性与强大灵活的查询特性,使其广泛应用于日志分析、向量搜索和复杂查询等场景。
在同程旅行的核心业务中,ES 为会员、搜索和订单等多个关键业务系统提供底层支撑。目前集群规模已扩展至数百个集群、数千个节点,并且仍在快速增长中。因此也急需构建一套满足高可靠性、高性能与低成本要求的 ES 平台,以支撑业务的持续高速发展。
本文将分享同程 ES 平台的架构设计思考、关键难点的解决方案与实践成果以及未来展望。
名词解释

ES 大规模应用面临的挑战
随着接入的业务系统越来越多,集群规模越来越庞大,对接的业务开发也越来越多,我们遇到了诸多痛点和挑战,总结起来主要有以下三点:
对用户要求高:用好 ES 的门槛较高,使用不当易引发稳定性风险
ES 凭借快速的迭代能力,在向量搜索和可观测性领域持续突破,但同时对用户也有较高的门槛,如果使用不得当,很容易造成集群 CPU 飙升,内存 OOM 等问题,主要表现在以下几方面:
查询设计不合理:使用 id 字段做 GROUP BY 操作,或在大索引上使用 wildcard 模糊查询,或使用 size:9999 拉数据,均会放大资源消耗。
数据类型误用:对 keyword 类型字段执行 < 、 > 等范围比较,导致集群性能下降。
随着接入业务方增多,不规范使用引发的稳定性风险显著上升。
对运维要求高:ES 版本升级困难、可观测性能力弱等造成维护成本较高
版本迭代快且兼容性不佳
ES 官方版本更新频率高,其大版本(Major Version)升级常伴随架构重构与功能迭代,可能引入不兼容变更,给生产环境带来显著技术风险。当前主流版本若长期停留在老旧分支(如 2019 年发布的 6.8.x 系列),将面临双重挑战:
同版本升级稳定性风险:即使同版本的小版本迭代(如 8.6.2 →8.13.0),也是会有分片分配失败的例子。典型案例包括分片副本分配失败,如果操作不当,可能导致集群状态异常,且缺乏安全回滚机制,极端情况下需重新部署节点。
跨版本升级兼容性断裂:
数据模型变更:7.x 版本移除 _mapping/type 字段,需重构索引设计与业务查询逻辑;
语法兼容性断裂:如 date_histogram 聚合查询修改 interval 关键字与值,旧版 DSL 可能需改造;
新特性滞后:向量检索、堆内存优化等 8.x 核心能力无法在老旧版本应用,技术债务持续累积。
业务迁移的实施阻力:跨版本迁移要求业务侧配合修改查询语法与接入方式,协调窗口期困难。若强制升级可能引发查询异常,影响用户体验;若长期搁置版本更新,又将导致系统无法获得性能优化。
可观测性能力差
监控粒度不足:仅覆盖节点级 CPU/内存 指标,缺乏查询模板粒度的资源消耗;
根因分析困难:故障分析就算结合多种指标,也很难定位到因大聚合导致 CPU 升高的异常索引。
运维模式仍以被动响应为主,未能构建涵盖故障预防、实时监控、精准止损的完整闭环,高可用要求存在显著差距。
对平台要求高:应对突增流量、中心故障等异常场景缺少有效止损方案
高可用解决方案不能满足要求
ES 已成为所有核心业务的基础依赖,任何故障都可能对多个系统产生广泛影响。在真实的线上场景,丢包、硬件故障与网络故障等都是不可避免的问题,这些都对稳定性造成了比较大的影响。而且在同程,我们底层系统有一个通用要求,能够支持任意 IDC 级别的故障,如出现 IDC 级别的故障,要能够快速的进行止损。
传统的单集群、单中心、多副本方案在这种场景下难以满足业务需求,可能导致系统延迟升高、性能下降,稳定性无法保障。
止损能力比较缺乏
在应对流量突增,大查询等场景时,在集群侧没有很好的止损手段,只能通过慢 DSL 等分析,找出对应的业务,请求业务来做相应的处理,完全不受底层的控制,会拉长故障处理时长,增大影响。
如何体系化地解决 ES 大规模应用三重困境
问题分析
针对以上多方面的痛点,我们需要从系统设计的层面来分析,确定相应的解决方案,主要有以下问题。
缺少提前规避问题的能力
完全依赖后置分析来发现和解决问题,缺乏前置约束能力,无法在问题发生前对业务使用进行有效规范,这对系统的稳定性构成了极大的威胁。由于 ES 语法灵活且功能丰富,业务错误使用或滥用可能导致系统性能下降、资源耗尽甚至服务中断。因此,能够提前发现潜在风险并主动规避问题,是平台必须具备的核心能力。
缺少故障发生时的止损能力
业务直连底层存储
当业务直接连接底层存储时,其运行完全依赖底层存储的能力来提供相关保障。例如,ES 集群本身缺乏语法审查、限流和熔断等功能,无法约束业务不正确的使用方式,并在故障时无法及时止损。因此,我们要增加一些高级特性(如限流、熔断等基础能力),同时支持平滑的升级底层存储。
任何的高可用的系统都可能存在风险
即使是设计为高可用的系统,也无法完全规避潜在风险,例如代码中的 Bug、大规模流量导致的丢包问题、硬件故障,甚至 IDC 级别的故障。为此,必须建立一套快速响应和解决问题的机制,以便在问题发生时迅速止损。即使面对 IDC 故障等极端情况,也需要通过完善的解决方案快速恢复系统,最大限度降低损失。
架构存在明显缺陷
原生使用 ES 在进行集群升级等不可逆操作时,存在明显的缺陷。一旦升级过程中出现问题,缺乏有效的解决手段,可能导致以下严重后果:
故障周期长:升级失败后,系统难以快速恢复,导致业务长时间受影响;
不可恢复:部分情况下,升级操作不可逆,可能导致数据丢失或服务无法恢复;
不满足高稳定性场景需求:在对系统稳定性要求极高的业务场景中,这种风险是不可接受的。
因此,我们需要设计一套解决方案,不仅要满足系统对高稳定性的要求,规避升级时给业务造成影响,还要支持对不可逆操作的有效管控,确保集群升级的安全性和可控性。
可观测性能力薄弱的问题
原生 Kibana 功能有限,集群抖动时常因采集中断导致监控数据缺失,难以在故障发生时提供有效的数据支撑。因此,我们需要构建一套更为完善和丰富的可观测性能力,既能够在日常运行中对系统进行全面监控和数据分析,提前发现潜在瓶颈,又能在故障发生时或故障后提供可靠的数据支持,帮助快速定位问题和制定解决方案。
解决方案
综上的问题分析,我们构建了一套同程特点的 ES 平台:

新的架构引入了多种服务组件,以有效应对并解决上述问题,从而在功能性、稳定性和可观测性方面实现了全面优化。下面来详细拆解分析下整个架构。
Proxy+ 管控平台 + 应用网关:重构接入架构与预分析方案,提前规避潜在风险
为了体系化解决原生 ES 在缺乏提前规避风险能力方面的痛点——导致对用户要求较高的问题,我们提出了一种创新的解决方案。其核心思想是在尽可能不限制 ES 原有能力的前提下,通过规范业务使用,提前发现并规避问题。同时,即使未能事先限制,事后也能快速止损。
接入方式升级
由于直连集群的方式无法很好地满足我们的设计目标,我们对接入方案进行了升级,通过 Proxy 对外提供服务,并强制要求业务方采用新的接入方式。为此,我们引入了两个核心服务组件:
管理平台:面向 ES 业务开发的平台,需要使用的语法需要提前在平台里面定义才能使用;
ES Proxy:所有的业务流量都经过 Proxy 而非直连集群,这样能更好的跟平台能力联动。
基于这两个组件的能力,业务使用 ES 的核心接入方式将从直连 ES 集群变成如下受管控方式:

核心的使用方式也从代码中任意使用的模式,转变为需要提前定义的方式,如下所示:

在使用 ES 时,业务方需要提前在平台中定义好查询模板(基于 ES 查询模板的能力进行复用)。在实际使用过程中,只需传入模板名称、版本号以及少量入参信息即可完成调用。这种方式简化了使用流程,提高了效率,同时也方便了模板的统一管理与维护,如下图:

对于业务来说,虽然接入方式有了变化,但是可以享受到更细粒度的监控报警,而且支持了无需发版即可升级,回滚等较多高级能力,整体是正向收益,推进起来并不算困难。
对于平台来说,可以提前做更严苛的管控,保障尽量将风险控制在上线前。但是接入的业务方非常多,人工的判断费时费力,所以我们引入了 AI 的能力,来提前做预审:

我们踩过哪些坑?
Proxy 与集群关系复杂,难以维护。
在 Proxy 的大量接入与推广中,我们曾踩过一段时间的坑。最初,我们将限流熔断能力集成到 Proxy 里面,并为每套 Proxy 申请独立的域名来做隔离,当 Proxy 多了以后,发现 Proxy 的升级迭代周期变得特别长,而且 Proxy 跟集群的对应关系维护变得特别的复杂,经常有用户使用错误的域名而导致问题发生。为了解决这些问题,我们对架构进行了新一轮的升级优化:

引入了应用网关服务,我们自定义了一个 ES 网关插件,插件有几个能力
为 Proxy 引流:我们打通了跟管控平台的关系,这样可以保障 Proxy 跟集群一一对应,自动将某个集群的流量路由到对应的 Proxy。这样,业务接入时只需使用一套统一的域名,大幅降低了维护成本。例如,所有业务均可通过 es.xxx.com 进行接入。
限流熔断能力:网关本身承载了大量核心业务流量,并具备丰富的限流和熔断基础能力。通过与网关的集成,Proxy 可以充分利用网关的这些能力,变得更加轻量化,从而简化复杂逻辑,减少版本迭代的频率和维护成本。

在线上发布一段时间,也遇见一些奇怪的现象,比如某几台代理的查询模版元数据未更新,导致配置不生效,引发切流失败或者查询模版不存在等问题。经过 Arthas 排查,是因为有阻塞的线程导致元数据拉取不下来。

继续跟进 RestTemplate 的源码发现,我们发现有一个参数未设置。


connectionRequestTimeout 参数未设置,导致一直阻塞,Client 池拿不到资源,则一直阻塞。

解决方案:设置 connectionRequestTimeout。改造并设置 maxTotal、defaultMaxPerRoute。
以上两个参数也非常重要,对请求客户端来说,maxTotal 是客户端的最大连接数限制,defaultMaxPerRoute 为每个路由(目标地址)允许的最大并发连接数,这样可以保障每个客户端最大的并发数,不能设置太高,防止某个目标地址的请求占满整个连接池的资源。

Replay+CCR+DVC:重构高可用架构,构建快速止损机制以保障业务绝对稳定
为了体系化的解决架构上的缺陷,以及解决在故障时止损能力较弱导致的对运维要求高等痛点,我们在架构上做了迭代,核心思想是:保障任何的操作均可回滚,以及支持多地多中心能力建设,给到业务更全面的、支持各种场景的高可用解决方案。
高可用架构解决方案
标准的 ES 的单集群多副本的解决方案不能很好的满足我们业务的发展与稳定性的要求,如 IDC 故障时影响面较大。深耕行业,解决方案都是异地多中心多集群,但是开源服务没有对应的能力,只可使用官方的商业能力,但我们弃用了 ES 官方的 CCR 功能,主要基于以下两点考虑:
成本较高:官方 CCR 的使用成本较大,难以满足我们的成本控制要求;
灵活性不足:CCR 的功能和配置在某些场景下存在局限性,无法很好地适配我们的业务需求。
在多集群的解决方案中,结合公司的特点,我们选择了自研主从架构,采用“一主多从”的高可用解决方案,而非多活的架构。该方案引入了三个核心组件:
Search Replay 流量回放组件:用于回放真实的业务查询流量,并支持流量放大等策略;
CCR 数据同步组件:提供跨集群的数据同步能力;
DVC 数据一致性校验与修复组件:顾名思义,用于比对主从集群的数据一致性,如果不一致则修复。
核心架构逻辑:流量基于 Proxy 同步到 MQ,然后通过 CCR 数据同步组件将数据同步到从集群。以下是局部拆解图示:

该方案通过 RocketMQ + CCR 组件进行集群数据同步有一个显著优势:可以将复杂的逻辑下推到异步的 CCR 服务中,从而保证 Proxy 的设计足够轻量。这不仅减少了迭代的复杂性,还降低了稳定性风险。
CCR 的核心注意事项
保证消费顺序
当用户的写入请求到达代理后,代理会首先向主集群发起写入请求。写入成功后,代理会将业务数据、版本号(_version)、数据流向等信息记录下来,并以 _id 为 key ,按照顺序将数据投递到 MQ。通过 CCR 消费这些消息时,可以确保业务数据正确地落盘。
写入失败兜底
当 MQ 故障或者写入失败时,会将消息记录到磁盘。每个代理都会部署一个 Agent,用于从磁盘中读取失败的数据日志,并将其投递到 MQ。CCR 会持续从 MQ 中消费这些失败的数据,并将其重新写入到目标集群。
Upsert 差异更新
业务场景中可能只会传递 id、routing 和需要更新的字段。为确保写入主集群时返回的数据是全量的,我们设置了_source=true,从而在数据同步到备集群时,能够保证写入的数据是完整的。
消费堆积
在消费目标集群的过程中,可能会出现数据堆积的情况。对此,我们针对消费组(Group)设置了堆积告警,并在平台上支持动态调整消费者的数量。此外,还可以在 MQ 层对队列(queue)进行扩容,以提升消费速度,尽可能降低数据同步的延迟。
主从集群版本的一致
ES 集群的数据版本默认是自增的。当实时写入请求到达从集群时,如果 MQ 中仍有未消费完的消息,数据写入从集群后,该文档的版本号会自增,导致主从集群之间的_version 数据不一致。如果目标集群的版本号较高,可能会引发数据写入冲突(Conflict)。
为了解决这一问题,我们对文档的版本号控制进行了小改造:不再使用默认的自增机制,而是改为基于时间戳的版本号控制。如果时间戳发生冲突,则在时间戳的基础上加 1。这样,主从集群都可以根据最新的数据进行覆盖更新。如果发生主从切换,从集群在接入实时写入流量时,会直接使用当前时间戳作为版本号,确保数据为最新并能进行全量覆盖,从而避免数据冲突。
虽然我们对 CCR 组件做了较多的设计,但是还是避免不了极端情况下主从集群的数据不一致,比如 Proxy 预期外故障等情况。
多集群的架构方案比单集群多副本的方案有个明显的难点,数据一致性的保障。这也是我们弃用双活架构,选择主从架构的一个考虑点,主从架构可以有一个标准数据,可以以主集群数据为基础数据,发现数据不一致时,可以直接重置从集群的数据。所以我们引入了数据一致性校验组件。
数据一致性校验组件 DVC 核心逻辑
在平台中,对每个高可用索引,配置定期校验的 Job。例如,检查 now()-0.5h 之前的半小时内数据差异情况;
对比该时段数据,发现差异则记录索引名称、范围及具体差异;
如果发现有数据差异,会以主集群为基础重新构建从集群数据。当主集群数据多于从集群时,DVC 组件会触发增量同步补齐差异文档。对于从比主多的情况,会对从集群先做条件删除,然后再进行全量覆盖写入。
通过以上机制,我们不仅保证了主集群与目标集群的数据最终一致性,还能在极端情况下快速发现并修复数据差异,为用户提供稳定可靠的数据服务。
目前,我们已经能够在集群故障时快速切换到从集群,并保障从集群的数据能够正常为业务提供服务。然而,是否敢切换到从集群仍是一个非常棘手的问题,因为无法确定从集群的能力是否足以支撑业务需求。行业内类似的故障屡见不鲜,例如某些 DB 的主节点宕机后,从节点无法承载业务流量,导致故障持续时间进一步延长。基于行业经验教训以及对稳定性的高要求,我们自研了 Search Replay 组件。
Search Replay 核心能力
流量镜像与回放:Proxy 负责对查询流量进行镜像, Replay 组件则负责将镜像流量回放到从集群,通过回放操作,可以评估从集群的性能是否能够良好地支撑业务需求;
流量放大实现真实流量压测:压测一直是 ES 的一个痛点,不合理的压测条件可能会导致全部流量进入缓存,从而使压测结果呈现出不切实际的高性能,无法反映真实的业务能力。因此,通过对真实流量进行放大,可以更真实地模拟业务场景,进而更准确地评估集群的性能能力。
基于 Proxy + CCR + Replay 等组件的能力,我们已经能够从架构层面很好地支持异地多中心等高级特性,同时也具备了应对集群故障和 IDC 故障等场景的止损方案,确保即使在极端情况下,业务的稳定性也能够得到保障。
如何把这套架构的收益做到更大,更好的保障稳定性与解决业务的痛点,我们基于实践经验,得出对应的几个最佳实践。
基于该主从架构的最佳实践
秒级扩容的最佳实践
ES 是一个重度依赖存储的服务,其扩容等效率受限于索引大小等因素的限制,扩容周期较长。近年来,同程经历了多次高质量的营销类活动,例如机票盲盒、DeepTrip 等。这类活动会带来突发的流量激增,这些流量对于业务非常有价值。如果采用常规的限流方案,可能会对业务造成损失。因此,我们尝试了一种新的解决思路,将 ES 打造成具备类 Serverless 能力的服务,支持秒级扩容。
我们的解决方案较为简单,不同于传统 MySQL 的主从架构,我们设计了一套多主一从的架构。通过这种架构,确保从集群的规模比主集群更大,性能更强,处理能力更高。当某个主集群出现流量激增时,可以利用更强大的从集群来提供支撑,从而尽量避免使用有损的限流方案。

读写分离的最佳实践
ES 这种强大的搜索与分析引擎,业务也会用它进行数据分析。但是数据分析往往有以下特点:
高 CPU 消耗
时效性不高
对集群有稳定性风险
针对这种问题,我们参照 MySQL 建设的思想,设计了读写分离的方案。将分析类查询路由至专用从集群,避免影响主集群的实时读写性能。

如图,可以按查询模板粒度,将查询切换集群,更好的保障主集群。
利用架构的能力,甚至可以做到一主多从,让主集群的资源给核心业务,而且两者互不影响。对于业务来说,以上所有的操作,无需调整一行代码和配置。
无缝跨大版本升级的最佳实践
版本升级是风险最大的运维场景,也是唯一一个不可逆的操作,如果支持了版本升级的可回滚,则所有操作均支持了可回滚。
如果运维 ES 较长时间肯定会遇到一个问题,以 6.8 为界线,很多集群会停留在 6.8 版本,因为 7.x 不能向下兼容,但是很多高级特性都是 7 以后的版本才有的,比如内存熔断等能力,所以把版本升级到 7+ 版本,对稳定性也有很大的助力。

借助于架构的能力,我们改进了版本升级的操作 SOP ,不再是原地升级主集群,而是优先升级从集群,通过 Replay 组件来确定从集群版本对业务无影响后,再进行集群翻转与切换。
通过先升级从集群 + 流量验证的机制,实现了 6.8 →新版本的无缝迁移。解决了一个很大的稳定性的风险。
在新版本切换过程中也遇到了比较多的问题,比如我们在一次高版本升级在备集群进行回放时(6.8->8.3),发现目标集群 CPU 飙升。

通过使用 profile 和 deepwiki 进行深入分析和排查,我们发现问题的根源在于 ES 高版本(8.4+)中 cardinality 聚合的默认行为发生了变化。具体来说,execution_hint 参数的默认值为 save_time_heuristic,该策略优先考虑执行效率,但在某些场景下会导致堆内存迅速升高,进而引发频繁的 GC ,最终导致 CPU 飙升。所以提前的流量回放对版本升级非常重要,如果原地升级版本,会出现不能恢复的情况。
可观测性能力建设:自研可观测性能力,提供丰富的监控报警与排障数据支撑的能力
原生的 ES 的可观测能力较弱,包括主流的 Kibana 指标也比较欠缺,在集群抖动等情况下,还会出现断图等场景比较影响使用。所以我们从多个视角,从 0 自研了 ES 的可观测性体系,来解决监控和报警相关的诉求。
用户侧可观测性能力建设
由于平台强制需要提前定义使用语法,配合 Proxy 的能力,可以很容易做到了查询语法粒度的监控报警。相比与慢 DSL 的分析,会更直观。

做到这种最细粒度的监控报警在处理问题时非常关键,如某个业务的查询 QPS 飙升,导致集群的波动,仅需要对这一种语法做处理,而不会影响其它业务。
运维侧可观测性能力建设
需要一个更细粒度的指标替代 Kibana ,可以对所有集群的状态了如指掌。
ES 提供了丰富的原生监控指标(通过_nodes/stats,_cluster/stats 等 API),包括:
节点级指标:CPU 使用率、内存占用、 search、bulk 线程池状态、磁盘 IO、JVM 等。
索引级指标:查询耗时、写入延迟、文档数、分片分布、缓存命中等。
集群级指标:分片状态、集群健康、数据分布等。
基于 OpenMetrics 标准,通过自研的 Metrics 服务定期采集这些指标,并暴露 /metrics 接口。
相比 Kibana 的解决方案,我们全面拥抱了 Prometheus 生态,采集关键指标到 Prometheus。这样可以做更好的故障发现与排障。



除了上述的痛点,我们基于多年的 ES 运维经验,发现一些普遍的痛点,也是一些行业通用难题,并针对性的做了对应的解决方案。
平台侧可观测性能力建设
在多年的运维实践中,我们发现两类异常 Case 在所有异常中占比极高,超过了 80%。这两类异常分别是:内存波动甚至导致 OOM ,以及 CPU 波动甚至达到 100%。针对这两种常见问题,我们专门构建了针对性的监控、报警和故障排查能力,以便快速定位和解决问题。
内存波动可观测能力建设
对于目前 7.x 之前的版本,经常存在大查询导致 OOM 的情况。非突增流量的索引问题排查,我们只能通过慢日志或者 Dump 日志去分析,整个耗时会是小时级别。

对于 7.x 之后,高版本的熔断能力做了很大增强,OOM 问题得到了很大的缓解,但是部分集群还是经常发现 heap 波动,内存占用不稳定,GC 耗时高等情况。所以我们需要监控到具体哪些业务造成了内存的波动,原生的 ES 没有这个能力,我们基于源码做了定制,在熔断器做了个轻量的切面,配合 proxy 传入的模板 +Prometheus 的可观测能力,可以监控每个查询模板实际占用的内存的趋势与大小。

如图,我们可以清晰的观测到某一时间段的内存波动趋势,并且具体到索引模版。
配合上述我们的网关限流、熔断能力,可以快速止损。基于这个数据我们也可以把占比较大的,以及 top 的给业务提出改造要求,降低集群资源使用。
CPU 波动可观测能力建设
与内存占用不同,无法对每个查询的 CPU 消耗做精准的监测与限制。根据历史的运维经验以及参考行业内其它技术栈的解决方案,如 MySQL 的慢查询监控与熔断能力,我们设计了一套类 MySQL 的监控、管理能力,主要针对慢查询。


如图,在集群 CPU 波动时,我们能够比较清晰的监控到查询语法,绝大多数情况下可快速的定位到问题。
总结与展望
通过以上平台核心能力的建设与完善,我们做到了 10s 内将一个中心的几百套集群,几千个节点全部翻转到另外中心,实现了分钟内解决中心级别的故障,在最少用户打扰前提下让业务使用从黑盒到可控,同时集群运维管理与维护效率等方面也得到了大幅提升。
当前我们也正在探索引入如 DeepSeek 等新一代智能化能力,未来希望借助大模型的强大分析与推理能力,进一步优化平台的智能化水平,具体而言:
业务优化建议:基于大模型的智能洞察,继续深入分析业务在平台上的查询模版语法,自动识别潜在的性能优化点,提供优化建议,帮助业务更合理的使用 Elasticsearch;
智能化运维支持:
借助大模型的 AI 审核能力,对查询模版的审批提供意见,并可自动审批一些常见问题模版,提升运维效率;
集群故障发生后,利用 AI 技术对历史指标和日志进行深度关联分析,快速定位问题根因,生成专业的修复建议,显著缩短故障恢复时间;
通过 AI 能力赋能,团队计划在以下领域实现突破:
性价比提升:优化业务使用,提升资源利用率;
稳定性增强:通过智能监控与预测性维护,进一步降低故障率,提升系统可靠性。
这将使平台向自治化运维方向迈出重要的一步,为 Elasticsearch 集群持续高效运行和业务快速发展提供更坚实的技术支撑。
同程基础架构热招岗位:基础架构分布式存储\数据库中间件\k8s 开发方向
简历投递邮箱:wwr5406@ly.com
评论