ElasticSearch 质量保障体系介绍

一、背景介绍
ElasticSearch(以下统称 ES)是一个基于 Lucene 库构建的开源的、分布式的、RESTful 风格的搜索引擎,它具有强大的文本搜索、数据分析以及数据存储等功能。目前众多搜索场景底层采用 ES 作为搜索引擎,包括用户的核心使用链路上也有它的身影,一旦出现故障,对用户的影响极大,甚至可能阻塞用户的使用,所以它的稳定性也始终都是质量保障的重中之重,在该中间件的测试过程中,除了基础的搜索逻辑校验,也存在较多其他痛点,如:
ES 需要在上线前提前做好性能评估
索引创建过程中全量数据的准确性
实时同步数据的准确性 ES 增量数据同步延迟如何感知
当索引出现问题时,如何快速恢复,将影响面降到最低
ES 搜索语句如何支持快速 debug,提升问题排查效率
那么围绕上面这些问题,我将介绍当前针对 ES 稳定性保障的措施,以及其中可能出现的典型问题,从各个阶段提升使用 ES 作为搜索引擎的稳定性及准确性。
二、保障体系

我们将对 ES 的保障体系分为三个阶段,分别为上线前测试阶段、上线后运行时故障发现阶段、实际故障发生后问题处理阶段
接下来将分别对这六项措施做详细介绍。
1、数据校验
数据作为迁移 ES 的第一道坎,也是最重要的第一步,只有保证了迁移时数据的准确性,才能保证后续搜索的准确。
从数据来源角度看,搜索只是数据的搬运工,而不是生产者,搜索引擎的数据均来自业务数据库。
那么,可以通过对比数据库中的业务数据与搜索引擎数据的一致性,校验搜索引擎内的数据是否准确,具体涉及的业务同步逻辑根据需求确定,这里不多做赘述。
典型问题:
ES 乐观锁冲突导致的数据不一致
由于 ES 的 Update 是先 get 原 doc,然后更新,再 index,所以在这个过程中需要进行锁控制。ES 采用的是乐观锁,我们在同步某一搜索数据的过程中,可能涉及到多个任务同时写 ES 一个索引的问题;为了减少不同任务写入数据的相互干扰,将任务改造成写入字段不冲突,也即不同任务写不同字段。

举个例子,如上图所示,假设任务 1 写入 A,B 字段,任务 2 写入 C,D 字段,某一时刻,1 和 2 同时写入,此时假设 2 比 1 快一点点,1 写入冲突后,重试,等待下一个 bulk 提交,任务 1 写入成功。
不过这种只是理想情况,我们再来看一种情况:

这种情况和上一种差不多,唯一的区别是,任务 1 在写入 A1,B1 之后,立马产生了 A2,B2 的数据,而且放在后面的一个 bulk 提交,此时 1 和 2 冲突,2 成功了,1 重试,重试之后的 A1,B1 的这次更新,就在 A2,B2 后面了,这时候就会产生消费的时序问题,导致最终结果不一致。
解决思路:写入 ES 的任务控制为一个,防止多任务同时写入造成冲突。
2、性能测试
作为测试的基础,性能测试在 ES 的测试中也必不可少,特别是当不同索引都存在在一个集群的情况下,如果没有做好性能评估,那么一个索引的性能问题可能影响同集群的其他索引请求,导致引发连锁问题。
目前搜索使用 ES 的场景主要是读,这里主要以读数据性能为例:ES 读性能与集群配置、索引数据量、搜索语句的复杂程度都有关,所以实际最准确的性能结论,需要以具体搜索场景下的性能测试为准。
过程简述:根据业务实际使用的业务场景,搭建数据完整的测试集群及测试索引,并根据实际搜索场景对搜索引擎进行性能测试。
典型问题:
ES 集群性能不均衡:
问题描述:一个集群内的同等规格(硬件配置一样)的机器,性能存在很大差异,主要体现在 cpu 性能上,差异率达到 50%以上
导致后果:ES 集群整体性能存在短板效应,因为 es 请求是分布式的,请求的最大耗时由最长的分片请求耗时决定。
测试发现过程:
1、第一次压测过程,集群某个节点 cpu 使用率高于其他节点一倍,可能的原因分析:
节点存在热数据,有几种方法可以判断是否有热数据
方法一:将性能差的 A 节点数据与 B 节点互换,再次压测,如果性能瓶颈还是 A 节点,说明没有热数据,如果性能瓶颈出现在 B 节点说明有热数据
方法二:将索引副本设置为 X,那么加上主分配,总共 X+1 个备份,意味着每个节点的数据是完全一样,继续压测,如果集群还是出现性能不均衡问题,说明没有热数据
机器层面性能存在差异
2、采用上述方法二判断节点没有热数据后,决定换掉性能差的节点,继续第二次压测,结果各节点性能相对均衡。
LargeTerms 性能极差,如:

优化方案:
根据压测结果调整 ES 集群配置
尽可能减少 ES 索引量,如可以将已软删除数据从索引中删除
针对 LargeTerms,合适的场景通过 script query 来替换 terms query, 达到优化性能效果
将大的 terms 列表拆分成多个小的列表,然后分批次进行查询,最后合并查询结果
3、数据延迟报警
根据搜索推荐场景的不同,索引数据分为离线同步与实时同步两种。
离线同步的数据往往对数据实时性要求不高,用户敏感度也较低。
实时同步的数据大多对数据实时性有较高的要求,且用户感知较明显。比较典型的例子,用户上传了某个素材,但是无法及时在列表中搜索到。
因此,可针对这部分实时同步链路设置数据同步延迟报警,其基本实现逻辑如下:
通过定时任务,获取最近时间段内的业务增量
再去搜索引擎中查询对应数据是否正常同步
示例:

某时收到了如上的报警情况,去 ES 查询后发现数据确实未同步进去,那么实际排查下来发现是由于业务突发的大量输入导致的同步链路阻塞。

4、实时数据订正兜底
数据订正主要在核心数据链路上使用,作为在数据同步系统出现故障时,实时数据同步的最后一层保障。
基于上述的数据延迟报警,若是数据同步链路发生异常或故障,可以作为一种应急兜底方案;而为了降低数据库的读写负担,该兜底程序仅在故障应急时开启。
在目前实时的保障链路中,用户高感知的核心场景是接入了数据订正程序的,也是成功兜住了几次严重的线上故障。
实现方案简述:定时监听业务库的数据变更,并根据实际业务同步逻辑完成 ES 的数据写入
关于 ES 的数据订正,有几个重要事项需要关注:
乐观锁冲突:为了防止多个任务同时写 ES,可将获取增量的时间提前
更新操作:注意要使用 upsert,使用 update 可能会覆盖所有字段
离线字段处理:ES 订正和 mysql 不同,ES 的所有数据都是写在一个索引中,而 mysql 是存在不同的表中,需要梳理 ES 中需要订正的核心字段,且在订正时需要注意不能将其他字段覆盖
增量获取:分库分表后,通过扫表的方式获取耗时太长,不能满足订正的要求,采用了监听数据变更的方式获取增量
5、降级措施——灾备措施
潜在问题:
ES 本身机器存在的不稳定性:节点失联等
索引定期更新(离线同步),更新过程中的不确定性导致的新生成索引异常
相关措施:
多集群互备
对于一些相对重要性较高的索引,采取多集群互备的方案。
例:xx 搜索索引
主索引位于:01 集群
备用索引位于:02 集群 (日常可用作预发环境索引,可保证备用集群索引的切实可用)。
一旦线上 01 集群发生故障,可以快速切换至 02 集群,并保证使用体验一致。
注意在实际使用的过程中,需要提前验证索引切换能否生效。
多索引互备
对于一些定期更新全量的索引,保留最近三天的索引数据

通过别名可切换使用的索引

多搜索引擎互备
ES 降级至热点表
mysql 搜索性能差,且很多场景无法满足,降级一般有损
实现相对简单
ES 降级至其他的搜索引擎,基本能够百分百覆盖搜索场景,但缺点也非常明显:
需要额外搭一套数据同步,开发成本高
不同的搜索引擎语法完全不同,需要开发两套,并且后续的需求开发工作量也会翻倍
一般只有在非常重要且核心的链路会考虑采用这样的互备方式
6、ElasticSearch 版本 debug 平台
除了以上的测试、及线上故障处理操作以外,还有一个非常重要的工作场景,即问题定位。
概述:基于常见的搜索问题咨询:为什么没有办法搜到 XXX?
通过搜索请求和 targetid,就能够返回具体哪个条件未满足。

结合前端实现,最终 debug 工具使用如下图所示:

具体常用场景举例:
用户反馈无法搜索到“汤臣一品”的小区户型,那么通过用户搜索链路的调用链 id 以及对应的“汤臣一品”的小区户型 id,即可定位到该用户是在“杭州市”下进行搜索,不满足该户型所在城市“上海市”。
那么使用这个工具,问题反馈方即可以快速定位到不匹配的条件,无需拆解用户的搜索条件并逐个排查,大大提升了问题排查效率。
三、总结与展望
本文介绍了在使用与测试 ES 过程中需要考虑的测试方向与测试手段,并介绍了其中发现的典型问题以供参考。
通过以上的手段,在使用 ES 作为搜索引擎的过程中没有因为该中间件出现过严重的线上故障,偶发的几次其他原因导致的线上问题也及时发现并通过种种降级手段化险为夷。
如线上离线索引出现异常时,基于多索引互备的策略,快速将线上请求切换至备用索引,及时控制住了线上影响面。通过文中介绍的应急手段至少处理了五次以上的线上问题,均没有上升为线上故障。
随着 AI 的发展,对 AI 搜索的需求也逐渐出现,而 ES.x 版本新增了 KNN 向量近邻检索能力,能够很好地结合传统搜索和向量检索能力,未来在这方面还可以进一步探索质量保证方案,从 AI 搜索效果保障为目标,进一步探索。

版权声明: 本文为 InfoQ 作者【杭州群核科技质量效能】的原创文章。
原文链接:【http://xie.infoq.cn/article/4bd52d0d1ef37bcd7e4f06998】。文章转载请联系作者。
评论