卧疾丰暇豫,翰墨时间作。这篇文章主要讲述百亿级监控场景大数据分位值计算实践相关的知识,希望能为你提供帮助。
文章图片
一、背景百度广告业务系统建立在分布式系统之上,面向商业服务,每天发生各类接口调用PV达到百亿次,产生TB级的监控数据,对监控系统的设计也提出了巨大的挑战。分位值对接口性能的敏感度高,在性能分析中具有很大价值。
1.1 分位值是什么
分位值是一组数据中排名在某个百分比的值。如:开机时360提示“您的电脑击败了全国80%的用户”,即代表启动时间在全国所有电脑中排名20%分位值。
1.2 Why分位值?
在接口性能分析中,分位值至关重要。因为许多极端请求都集中在99%分位值以上,数量少但影响大,通过平均值无法观察到。99%的正常请求会把1%的极端数字平均掉,导致看起来系统响应很快。但这些少数极端请求会造成1%用户极为不好的用户体验。
二、分位值常见计算方案
2.1 流式计算
实时采集数据样本,上传到Spark,Flink等计算集群,进行流式计算。
- 优点:最高可以得到100%精确的分位值;
- 缺点:在百亿级场景下,将全量数据导入计算集群,消耗资源巨大,成本极高。不适用监控场景。
2.2 离线计算
离线计算,将数据直接导入数仓,再由定时进行任务批量计算分位值。
对于APM线上监控的场景,对实时性要求较高,不适用离线计算架构。
2.3 压测端计算
使用JMeter,LoadRunner等压测工具,在压测过程中采集数据样本,实时排序,即时计算。
- 优点:精度高,实时性强;
- 缺点:但局限于单个应用,压测阶段。规模有限,且没有长周期的变化趋势等分析能力。
2.4 即席查询计算
如Prometheus等监控工具,可收集各个实例数据样本,在需要查询分位值时,再即时计算。
- 优点:节约计算资源,可按需计算,灵活性强;
- 缺点:耗费存储资源,尤其是在百亿级场景下,存储数据结构的选取大幅影响资源开销和即席查询响应时间,对技术可行性有决定性影响。
3.1 分而治之
分而治之是大数据计算的基本思路。但是分位值如何归并,是分而治之的重点难题。分位值的特点决定了分位值不能简单地分治计算再归并。与平均值不能再求平均同理,同一个应用的多个实例求得的分位值再求平均没有数学意义,不能代表集群整体的分位值。
既然分位值本身不能归并,那么原始数据可以归并吗?当然是可以的。既然原始数据可以归并,那么原始数据的摘要可以归并吗?也是可以的。
解释:摘要是对原数据样本的一个数据分布的抽象,可以在一定误差内,等同于原数据样本。常见的数据摘要算法有,直方图,T-Digest,GK算法等。
3.2 采集端聚合
基于数据摘要,我们可以在各个实例本地维护一个数据分布(即摘要),每次得到一个请求耗时样本数据,就更新数据分布,在本地实现一次聚合。每隔固定周期将本地聚合的数据分布上传到数据仓库。
「这是第一次聚合」,可以将单个实例每小时数百万次请求,压缩成占用几十kb的数据分布,大幅降低数据规模,使承载百亿级场景成为可能。
3.3 汇聚层归并
在数据仓库中存储各个实例上传的数据分布,不进行计算。直到有用户查询某个接口的分位值数据时,才筛选出该接口下的所有数据分布,归并成一个数据分布,再用该数据分布推算出分位值。
「这是二次聚合」,假设一个接口有100台实例,每小时采集一次,则一天只有2400个数据分布。归并2400个数据分布,在0.1s内就可以完成。
通过两级聚合,可将计算开销分摊至数万实例上,对业务性能几乎没有影响,又无需引入额外计算资源。结合即席计算的优势,可实现秒级查询,和高度灵活性,高度符合APM场景。
四、具体实现总体架构如下图:
文章图片
▲总体架构
原理具体分为如下步骤:
- 「接口拦截」 在接口调用时进行拦截,获取接口响应时间;
- 「端上聚合」 将拦截到的响应时间聚合进数据分布中,每到达聚合时间区间后,将本地的数据分布上传到数据仓库,然后清空数据分布;
- 「二次聚合」 在数据仓库端,对各个实例上传的数据分布按接口名进行二次聚合,按每个接口分别合并成接口总数据分布,从而推算接口总分位值。
文章图片
▲接口拦截
接口拦截的具体技术可以通过手工埋点、java字节码增强等方式来实现。
Step2. 端上聚合数据分布是对接口响应时间数据样本的摘要,用来在后面的环节中近似估算分位值。数据分布需要满足如下特征的数据结构:
- 描述原数据样本的分布结构
- 可接收新的数据样本,调整数据分布
- 符合结合律,即多个数据分布可以合并成一个总的数据分布
- 可以用数据分布估算出原数据样本的近似分位值
- 使用常数大小内存空间,且精度可控
每次拦截到接口调用后,获取到本次调用的相应时间,然后更新本接口的数据分布。流程如下:
文章图片
▲更新分布
由于每个应用实例上会包含若干个(几十到上百个)接口。对于每个接口我们需要分别记录响应时间的数据分布。我们利用一个表格数据结构来存储各个接口的数据分布。聚合项存储数据结构:
实例元信息:
- 「应用名:app1」
- 「实例:instance1 (10.20.30.40)」
- 「聚合开始时间:2020-10-10 18:00:00」
- 「聚合周期:1h」
- 「下次提交时间:2020-10-10 19:00:00」
文章图片
当采集到数据样本时,先根据接口名,在上述表格中找到接口对应的行(如果没有则新建一行),然后将数据样本(即本次调用响应时间)合并进接口名对应的数据分布中。流程如下图:
文章图片
聚合
应用实例在本地分别记录各个接口和其响应时间的数据分布。每到达既定时间后,将上表全部内容发送到数据仓库,并清空本地表格。流程如下:
文章图片
上传
数据分布数据为内存中的二进制数据,在上传的过程中需要进行序列化。序列化的具体实现一般推荐如下处理:
- 「采用小端格式」数据仓库多为C++实现。如果选择C/C++实现的数据仓库,则需采用小端格式序列化。因为x86架构基于小端格式,序列化数据保持一致格式,方便C/C++解析。如果采用Java实现或其它大端格式的数仓(如Hive),则无需采用小端格式。实践中,我们采用百度自研的Apache Doris数仓,并使用C++开发分位值二次聚合算子。
- 「压缩」由于接口响应时间样本数据往往具有强偏向性,数据分布数据结构内部会存在大量零值,启用压缩可以显著压缩数据体积,减小网络传输和存储资源占用。具体压缩算法可以采用GZIP,BZIP2等。实践中我们采用GZIP算法,压缩比率在30%-40%左右。
- 「编码」由于直方图数据本身是二进制格式,通过例如BASE64等算法编码成文本格式,更加易于传输、存储和调试。实践中,BASE64编码对体积有20%左右增加,但存储空间相对廉价,相比带来的优势,可以接受。
文章图片
为了计算某个接口的响应时间分位值p,我们需要将各个实例上传的该接口的数据分布聚合项做二次聚合,用二次聚合后的数据分布聚合项来计算分位值p,则此分位值即代表该接口在整个应用集群(所有实例)上的总分位值。
文章图片
▲二次聚合
这里分位值p是由用户查询时实时指定的。也就是说,每次计算分位值的时候,都可以支持任意分位值的计算,而不是预设的分位值。我们的T-Digest结构在百亿数据下每日1GB左右数仓存储,最高精度0.1%左右。
五、技术优点总体架构简单,性能高,成本高度可控,易于维护,稳定,风险低,多快好省地满足线上系统监控分位值计算场景。同时对于其它大数据分位值需求场景,也是一种非常优秀的参考方案。
推荐阅读:
当技术重构遇上DDD,如何实现业务、技术双赢?
接口文档自动更改?百度程序员开发效率MAX的秘诀
技术揭秘!百度搜索中台低代码的探索与实践
百度智能云实战——静态文件CDN加速
化繁为简--百度智能小程序主数据架构实战总结
百度搜索中台海量数据管理的云原生和智能化实践
百度搜索中“鱼龙混杂”的加盟信息,如何靠AI 解决?
---------- END ----------
百度 Geek 说
【百亿级监控场景大数据分位值计算实践】百度官方技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
推荐阅读
- linux 下 自动化跑压测脚本
- #yyds干货盘点# 简单化MySQL优化小妙招
- JAVA SE——对String类的深入理解
- 教大家用 Springboot 集成 slf4j 进行项目日志记录
- (运维服务)高可用keepalived配置实战
- (运维服务)高可用keepalived技术介绍
- Exsi 封装网卡驱动
- 部署LVS-DR群集(DR模式介绍及部署)
- 实验(部署LVS-DR集群)