在抖音亿级日活流量的情况下,每天收到的用户反馈也是大量的,而用户反馈对于产品的发展与未来是至关重要的,因此用户体验管理平台(简称VoC)就应运而生,VoC 平台旨在通过技术平台化的方式,结合反馈驱动的机制,以产品化、数据化的手段从反馈中挖掘出对抖音系产品留存、增长或口碑提升的可能点,推动体验问题治理改进,提升产品体验。同时也为公司各业务线提供通用和定制化的体验数据总览、用户原声分析以及体验专项、实验专项等看板或专项管理能力。由其定位可知 VoC 平台需要具备实时大数据处理能力和高性能、高效能的指标计算能力,同时也需要保证足够的准确性与稳定性。
下面简要介绍本文优化实践涉及到 VoC 平台的主要功能 ,和在平台业务发展中产生的 痛点问题的优化实践 ,由于篇幅有限本篇将主要对 ES 慢上游响应 场景的优化实践进行介绍,下篇文章将主要介绍针对 ES 引擎局限性 的优化实践及经过优化后整体的收益分析。
体验数据总览
体验数据总览主要是帮助运营、产品同学快速掌握反馈数据的整体情况。包括关键指标、反馈重点问题、全渠道反馈词频等重点模块。
- 关键指标-反馈量级:快速掌握反馈数据量级;
- 关键指标-反馈变化趋势:直观感受反馈趋势变化,初步定位原因;
- 反馈重点问题:场景&问题维度掌握反馈,进一步深挖反馈量级变化的原因。
用户原声分析
用户原声分析主要是帮助运营、产品同学按照自己感兴趣的特定场景分析全渠道用户反馈并生成对应的用户画像分析,同时支持用户按照特定维度对比反馈变化趋势,细化分析维度,直击用户心声。其中,对比维度分为两种:一种是有固定数量、固定枚举值的,例如手机系统有 iOS、Android,这种对比维度的枚举数量有限且枚举值已知,趋势图展示会展示全部枚举值下聚合的趋势线;一种是数量、枚举值数量大于 10 或不固定的,例如 App 小版本号等,这种对比维度的枚举值无法预先掌握,趋势图展示仅展示均值 TOP 10 枚举的趋势线。
性能痛点
伴随着 VoC 平台上线以来的业务线横向拓宽、数据逐步积累到亿数量级以及分析能力拓展,平台所采用的 ES 搜索引擎逐渐暴漏出查询慢、OOM 等问题,严重影响了用户操作平台的使用体验。同时,VoC 对各业务线提供了一定范围内的标签整合能力,方便业务整合业务场景进行数据分析,但该能力承载在另外一个数据反馈和风控平台(SkyNet)中提供统一标签配置能力,由于历史遗留问题存在一定程度的性能问题,且由于标签是 VoC 平台相关数据统计的最基本条件,标签获取的慢响应会拖慢所有查询接口的响应。能否解决以上两个问题,是现阶段我们提升用户体验与维护平台口碑的关键。
痛点一:上游性能局限性
VoC 平台目前已完成公司大部分业务线的数据接入,为了能够保证平台数据的时效性和有效性,需要实时地对接公司多个平台获取相关信息。由于每个上游平台的复杂度以及请求承载能力不同,VoC 平台在频繁请求众多上游接口的过程中经常出现响应时间过长甚至超时的问题,严重地损害了平台自身的稳定性。
目前 VoC 上游请求响应最慢和稳定性最差的接口主要集中在数据反馈和风控平台(SkyNet)标签映射这一部分,这部分主要是提供整合业务原始标签便于业务进行指标看板分析的映射标签的能力,显然这种标签树融合配置、映射与反映射的功能会存在大量的递归、剪枝等复杂逻辑,所以这部分上游接口的响应速度很慢。同时,标签作为 VoC 平台的基础查询条件,上游接口的慢响应会影响每一个接口的响应速度,如何在有限的条件下降低上游接口响应慢带来的影响,是性能优化着重要考虑的问题。 目前 VoC 平台指标接口流程如下所示:
痛点二:ES 引擎局限性
Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,VoC 平台的用户反馈数据均通过 ES 进行存储与查询,而上文提到的 VoC 功能中反馈变化趋势、反馈重点问题、反馈趋势维度对比,本质上都是围绕在时间、标签或是其他一些数据筛选项维度上的 ES 聚合或嵌套聚合。同时,ES 提供的聚合是一种 Bucket 聚合算法,Bucket 聚合按照一定的规则,将文档分配到不同的桶中,达到分类的目的,Bucket 聚合支持嵌套,也就是在桶里再次分桶。然而,ES 为了保证自身在面对海量数据与复杂查询条件时也能够稳定的运行,针对 Bucket 聚合设置了 max_bucket_size 限制,一旦本次查询生成的桶数目超出了该限制,ES 会立刻终止本次聚合并抛出异常,以免桶数过多超出内存限制最终触发 OOM。
我们可以来计算这样一个场景,统计周期为 6 个月、小时时间粒度下的标签对比维度反馈量趋势中一共有多少个桶:仅按时间相关的限制我们可以得到 6 个月、小时粒度 的点共有 63024=4320 个,标签如果按 100 个计算,我们需要 ES 提供的聚合桶数就是 43.2 万 个,但是实际上目前我们 ES 集群限制的聚合桶数仅仅只有 2 万 个、标签数目也远不止 100 个。这就意味着大时间区间、小时间粒度、多对比维度这三个条件,任意一个足够极端时,我们的接口都有可能出现查询慢、ES 触发 Bucket 超限甚至 OOM 的异常情况。
上游性能局限性优化
虽然我们没有办法改变上游的接口性能,但由于 VoC 平台所依赖的上游数据绝大多数是变动不频繁的,我们可以通过设计针对上游接口的缓存方案,来降低上游性能对 VoC 平台接口性能的影响。本文基于数据反馈和风控平台(SkyNet) 标签映射接口阐述缓存方案的设计。
优化措施一:按请求参数缓存
按请求参数缓存是 B/S 架构下服务端最为常用的一种缓存方案,考虑到上游性能问题,这里不仅要对 VoC 自身接口做缓存也要对上游接口的响应做缓存。在上文的痛点中提过,映射标签到原始标签的转换因为上游性能问题成为瓶颈,针对该问题添加缓存后的方案流程如下:
添加请求参数缓存后的标签映射转换效率得到一定程度提升,但是这个提升仅局限在高频使用场景,这个原因是:上游接口的请求参数是一个映射标签列表,而 VoC 平台是支持用户自由的选择标签来过滤数据查看分析的,所以这里的请求参数可能性无限多,当且仅当按照同样的标签过滤条件查看页面时,这个上游缓存才能生效,换句话说就是高频使用场景性能提升更为明显。如何提升低频使用场景的缓存覆盖是后续方案设计着重要解决的问题。
优化措施二:按标签维度缓存
针对请求参数可能性无限多的问题,我们没有办法将全部的可能性全部加到缓存中,重新审视标签映射转换流程可以发现,标签映射相关接口请求上游的目的是完成映射标签到原始标签的转换,用于后续 ES 查询请求的构建,这个过程中最细的粒度是一个映射标签到多个原始标签的映射,这个转换流程如下所示:
不难看出,当前流程每一次查询都将前端请求中的映射标签参数全部透传到上游请求中,多次请求中很容易出现同一映射标签,同一映射标签在上游中的重复计算会造成不必要的开销。同时映射标签数目目前是万数量级的,我们没有办法把所有标签的排列组合全部加入预缓存,但却可以将每一个映射标签加入预缓存。综上,标签维度的缓存可以进一步提升缓存的覆盖面积,优化后的转换流程如下所示:
- 通过 Faas 定时异步刷新全量标签缓存,基本能够保证 VoC 平台全部接口大部分时间内都不会再请求上游,而是通过 Redis Pipeline 去获取缓存中已有的标签映射信息,仅在上游标签映射版本改动时才会调用上游获取最新的标签映射信息。
- 处理待转换的映射标签时,先分类出其中缓存中已有与缓存中暂无的映射标签,仅将未命中缓存的部分作为请求参数请求上游,降低上游计算量,提升响应速度。
标签维度的缓存与预缓存逻辑,将每个接口都需要进行的上游请求,转化为 Faas 中异步进行的缓存刷新,提升了缓存对于低频使用场景的覆盖,极大地降低了上游响应慢带来的影响,大范围提升了 VoC 平台的接口响应速度。
最终方案总览
综上,针对数据反馈和风控平台(SkyNet)为例的上游性能局限性解决方案为:
- 请求参数缓存:用户高频使用场景性能有保障;
- 标签维度缓存:用户低频使用场景能够复用历史标签映射转换结果,尽可能降低低频场景对于上游的依赖;
- 高频场景预缓存与刷新缓存:通过分析一段时间的埋点数据,对请求参数缓存进行了高频场景的预缓存,尽最大可能保证进入平台以及高频场景使用的第一感受,同时消除缓存失效引起的性能骤降;
- 标签映射预缓存与刷新缓存:定期刷新全量标签映射,将 99% 时间的上游请求都收拢到 Faas 任务中异步执行,整体避免上游依赖。
收益分析
在 VoC 平台的关键指标中用户进入平台首先看到的就是关键指标中的两个指标:反馈总量和反馈变化趋势,这里的性能分析是屏蔽了请求参数缓存进行的,目的是在于测试非高频场景下的性能优化结果:
反馈总量接口响应随着时间周期的增大性能提升更为明显,且基本稳定在 3.2s 左右;反馈趋势接口性能优化接近 80%,提升显著,但是当时间周期增加到 6 个月时性能会发生劣化,推测和 Filters 聚合的过滤条件下沉有关,这一部分还需要在后面的实践中进一步分析确认。 第二个痛点问题针对 ES 引擎局限性的性能优化将在下篇继续介绍。