阅读视图
百度一站式全业务智能结算中台
播放器视频后处理实践(二)氛围模式
01 前言
在日常视频播放中,我们经常会遇到这样的问题:视频的长宽比例与设备屏幕不一致,导致画面上下或左右出现黑边。虽然这并不影响视频的正常播放,但从用户体验的角度来看,这些黑边往往打断了视觉的沉浸感,显得格外突兀。
为了解决这一问题,业界主流播放器(如 YouTube、Netflix)引入了一种被称为氛围模式(Ambient Mode)的视觉增强效果。它的核心思路是:
通过实时识别视频画面的主色调,并动态将其填充到黑边区域,使边缘色彩与视频内容保持一致,提升整体视觉统一性,从而营造出与视频内容相协调的氛围效果,让观众的观看体验更加自然和沉浸。
下面是YouTube的氛围模式效果:
youtube竖屏效果
youtube横屏效果
百度播放内核团队也将氛围模式效果应用到了视频播放场景,用于提升用户观看视频沉浸感,同时在百度App、好看App两款产品完成上线。本文将详细说明视频场景氛围模式技术方案。
02 整体技术方案
氛围模式通过在播放内核视频后处理通道(FilterChain)添加一个AmbientFilter滤镜实现,其核心思路:通过AmbientFilter滤镜先将视频帧数据从GPU下载到CPU,然后将视频帧数据按块进行区域划分,划分完成后再通过颜色量化算法提取每个区域主色调,最后将各个区域主色调传给平台层,平台层拿到主色调进行绘制视频四周氛围效果。整体方案流程大致如下图所示:
氛围模式整体方案
2.1 视频帧采样
为了提取视频的主色调,需要获取视频帧数据。但提取主色调并不要求每帧都下载,太频繁下载会拖垮应用性能,在视觉上也不会带来特别好的体验。因此我们对视频帧进行采样下载:在 25 FPS 的视频下,每隔约 50 帧(约 2 秒)采集一次帧数据。
同时,为了避免将视频帧数据从 GPU 下载到 CPU 时阻塞渲染线程,我们采取了以下优化:
1. FBO 压缩:先将视频帧渲染到较低分辨率的 FBO(例如将 1080p 压缩到 108p),大幅减少待传输的数据量。
2. PBO 异步传输:利用 PBO 异步将帧数据从 GPU 下载到 CPU,从而避免阻塞主渲染线程。
通过这种方式,我们既能保证主色调提取的效率,又不会影响视频的流畅播放。渲染线程和氛围模式工作线程两个线程工作流程如下图:
线程核心职责
2.2 主色调提取
2.2.1 视频帧区域划分
拿到视频帧数据后,我们先将视频帧划分出几个区域。项目中我们是将视频帧画面划分为:TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, BottomRight 六个区域,如下图所示:
视频区域块划分
接下来我们提取出每块区域的主色调。
2.2.2 提取主色调
要提取画面主色调,我们是通过颜色量化技术实现的。颜色量化(Color Quantization) 是一种图像处理技术,目的是减少图像中使用的颜色数量,同时尽量保持原图的视觉效果。代表性的颜色量化算法有:
1. 中值切割法(Median Cut):将颜色空间递归分割成小立方体,取每个立方体的颜色中位数作为调色板颜色。
2. K-means聚类:将颜色按相似性分组,取每组的中心作为调色板颜色。
3. 八叉树算法:通过构建八叉树分层合并颜色,逐层减少叶子节点数量,最终保留高频颜色。
4. 流行色算法(Popularity):统计原图颜色出现的频率,选取高频颜色作为调色板。
这几种算法从各维度对比情况如下:
从算法的速度、精度以及实现复杂度等多维度考虑,氛围模式场景我们选用中值切割法完成视频画面主色调的提取。
2.2.3 中值切割法
中值切割法(Median Cut)是一种用于图像颜色量化的算法,算法核心思想是将颜色空间递归地分割成更小的区域,以减少图像中颜色数量。该算法的目标是在颜色空间中选择一组代表性的颜色,这些颜色可以用于生成调色板,从而减少图像的颜色数量,同时尽量保留图像的视觉效果。算法核心步骤如下:
1. 初始化颜色盒
a. 首先,将所有颜色视为一个大的颜色盒(即整个颜色空间的一个区域)。
b. 颜色盒包含图像中所有像素的颜色。
2. 选择分割轴
a. 在每次迭代中,选择颜色分量(红、绿、蓝)中范围最大的分量作为分割轴。这是为了最大限度地减少颜色空间的不均匀性。
3. 按中值分割
a. 沿着选定的分割轴,根据颜色值的中值,将颜色盒分成两个较小的盒。
b. 这种方法确保每个新盒子中包含的颜色数量尽可能相等。
4. 递归分割
a. 对每个新的颜色盒重复步骤2和3,直到达到所需的颜色盒数量(通常是所需调色板的大小)。
5. 生成调色板
a. 一旦颜色盒的数量达到预期的数量,对每个盒子计算平均颜色或中值颜色,将其作为代表颜色添加到调色板中。
6. 颜色映射
a. 使用生成的调色板,重新映射原始图像中的每个像素到最接近的调色板颜色。
中值切割算法核心流程如下图:
中值切割算法
03 平台渲染氛围效果
当native层提取完视频帧各区域主色调后,将色值传给平台层(Android/iOS)。平台层收到色值后,将色值渲染到视频四周以产生氛围效果。为保证各个区域色值过渡自然,以及前后两帧的色值平滑过渡,需要借助平台层渐变、动画、rgb插值等技术实现。 下面结合Android和iOS两个平台分别介绍具体思路。
3.1 Android平台
Android 使用自定义view技术,完成氛围色值的渲染。我们提供一个自定义view名为AmbientView 来完成这个功能。有了AmbientView之后,布局结构大致如下:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<com.baidu.cyberplayer.sdk.AmbientView
android:id="@+id/left_ambient"
android:layout_width="xxxdp"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/video_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<com.baidu.cyberplayer.sdk.AmbientView
android:id="@+id/right_ambient"
android:layout_width="xxxdp"
android:layout_height="match_parent"/>
</FrameLayout>
上面为视频横屏下布局大致情况,id为video_container的FrameLayout是播放器容器,在播放器容器左右各摆放一个AmbientView渲染氛围模式,AmbientView的宽度会根据播放器的尺寸的变化在代码中动态调整。
AmbientView核心功能:
1. 相邻区域的主色调,使用LinearGradient拉出线形渐变。对于横屏视频,我们渐变方向就是从上至下。所以更新氛围色值的代码如下:
private void updateGradient() {
mLinearGradient = new LinearGradient(0, 0, 0, getHeight(),
mColors, null, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
invalidate();
}
2. 前后两帧氛围色值的切换,为了颜色切换不显得生硬,我们借助Android属性动画以及RGB插值实现色值缓慢渐变效果,核心代码如下:
private void startColorAnimator() {
int[] lastColors = new int[mLastColors.length];
for (int i = 0; i < lastColors.length; i++) {
lastColors[i] = mLastColors[i];
}
mColorAnimator = ValueAnimator.ofFloat(0, 1f);
mColorAnimator.setDuration(1500);
mColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator valueAnimator) {
float progress = (float) valueAnimator.getAnimatedValue();
interpolateColors(progress, lastColors);
updateGradient();
}
});
mColorAnimator.start();
}
/**
* 插值计算color
*/
private void interpolateColors(float progress, int[] lastColors) {
if (mCurColors == null || mCurColors.length <= 0) {
return;
}
ArgbEvaluator evaluator = new ArgbEvaluator();
for (int i = 0; i < mCurColors.length; i++) {
mColors[i] = (int) evaluator.evaluate(progress, lastColors[i], mCurColors[i]);
}
}
mColorAnimator是一个ValueAnimator对象,通过ValueAnimator我们创建一个1500ms的动画,在动画的更新函数里面,我们调用了interpolateColors,这个方法内部就是用ArgbEvaluator完成RGB颜色插值,更新到mColors数组中。最后调用updateGradient方法触发AmbientView重绘。
3. 渐变遮罩:最后我们还要在上面添加一层黑色渐变遮罩,保证氛围区域不要太突兀,以免过度吸引用户眼球,导致用户注意力不在视频内容本身上面。黑色遮罩实现也非常简单,代码如下所示:
float[] mPositions = {0.0f, 1.0f};
int[] mMaskColors = {0x88000000, 0xff000000};
// 从左到右渐变
mMaskLinearGradient = new LinearGradient(0, 0, getWidth(), 0,
mMaskColors, mPositions, Shader.TileMode.CLAMP);
mMaskPaint.setShader(mMaskLinearGradient);
// 绘制黑色渐变蒙层
canvas.drawRect(0, 0, getWidth(), getHeight(), mMaskPaint);
3.2 iOS平台
iOS端同样提供了一个自定义的 AmbientView(氛围视图),为视频播放场景提供动态渐变背景和遮罩效果,增强视觉沉浸感。
1. 双图层架构设计:采用主渐变层与遮罩层分离的架构方案,确保色彩渲染与边缘遮罩效果互不干扰,提升整体渲染效率。
- (void)setupSubLayers {
_gradientLayer = [CAGradientLayer layer];
_gradientLayer.frame = self.bounds;
[self.layer addSublayer:_gradientLayer];
_maskLayer = [CAGradientLayer layer];
_maskLayer.frame = self.bounds;
[self.layer addSublayer:_maskLayer];
}
2. 流畅动画引擎:基于CADisplayLink构建动画循环,通过实时颜色插值计算,实现细腻流畅的色彩过渡效果。
- (void)startAnimation {
// 核心功能代码
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateColors)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)updateColors {
CGFloat progress = MIN(1.0, (CACurrentMediaTime() - self.startTime) / self.animationDuration);
NSMutableArray *interpolated = [NSMutableArray array];
for (NSUInteger i = 0; i < self.endColors.count; i++) {
UIColor *from = i < self.startColors.count ? self.startColors[i] : [UIColor clearColor];
UIColor *to = self.endColors[i];
[interpolated addObject:(__bridge id)[self interpolateFrom:from to:to progress:progress].CGColor];
}
_gradientLayer.colors = interpolated;
}
- (UIColor *)interpolateFrom:(UIColor *)from to:(UIColor *)to progress:(CGFloat)progress {
CGFloat fr, fg, fb, fa, tr, tg, tb, ta;
[from getRed:&fr green:&fg blue:&fb alpha:&fa];
[to getRed:&tr green:&tg blue:&tb alpha:&ta];
return [UIColor colorWithRed:fr + (tr - fr) * progress
green:fg + (tg - fg) * progress
blue:fb + (tb - fb) * progress
alpha:fa + (ta - fa) * progress];
}
3. 渐变遮罩:采用多段式渐变遮罩配合加速曲线算法,打造自然的边缘过渡,有效增强视觉层次感。
- (void)makeMaskColorsAndLocations {
const NSInteger steps = 6;
for (NSInteger i = 0; i < steps; i++) {
CGFloat t = (CGFloat)i / (steps - 1);
CGFloat acceleratedT = t * t;
CGFloat currentAlpha = a + (1.0 - a) * acceleratedT;
UIColor *color = [UIColor colorWithRed:r green:g blue:b alpha:currentAlpha];
[_maskColors addObject:(__bridge id)color.CGColor];
[_maskColorsLocations addObject:@(t)];
}
_maskLayer.colors = _maskColors;
_maskLayer.locations = _maskColorsLocations;
_maskLayer.startPoint = CGPointMake(0, 0);
_maskLayer.endPoint = CGPointMake(1, 0);
}
该实现确保了氛围渲染的高性能和优美视觉效果,为用户提供了沉浸式的观看体验。
04 效果展示
氛围模式已在百度内包括百度App和好看App两款App完成上线,其中百度App主要集中在搜索三方影视场景,好看App所有视频横屏场景(排除广告视频)。同时在视频观看时长、分发、完播率等UBS指标取得了正向收益,说明氛围模式给用户带来了不错的沉浸式观影体验。
下面是百度App和好看App效果展示:
百度App氛围模式
好看App氛围模式
5. 总结
氛围模式是一种视觉增强功能,通过技术手段有效解决了视频比例不匹配导致的黑边问题,显著提升了用户视觉体验,主要表现在如下几个方面:
1. 视觉沉浸:氛围模式通过在视频周围添加柔和的背景颜色,使屏幕的边缘与视频内容更好地融合。这种设计使得用户在观看视频时感觉更加沉浸,减少了视频与周围环境之间的视觉割裂
2. 舒适观看:这种模式可以减少长时间观看视频时的眼睛疲劳。通过在视频周围使用柔和的色彩过渡,可以缓解亮度差异带来的视觉刺激,从而提高观看舒适度。
3. 提升观感:氛围模式通过智能地调整背景色彩,使其与视频中的主要色调相匹配,提升整体观感。这使得视频内容更加突出,同时为观看者提供一种更为和谐的视觉体验。
通过本文介绍的技术方案,开发者可以实现类似主流视频平台的高质量氛围模式效果,为用户带来更加沉浸的观看体验。
百度慧播星数字人技术演进
基于AI的质量风险管控
项目级效能提升一站式交付最佳实践
破局复杂业务场景:百度数据分析平台(TDA)分析增强与性能优化的双轮驱动
百度大数据成本治理实践
大模型在百度电商机审应用的落地实践
大规模微服务系统中的雪崩故障防治
导读
在大规模微服务架构中,雪崩故障是极具破坏力却又难以预防的系统性威胁。本文基于百度搜索架构与运维团队的实战经验,深入解析雪崩从“非稳态”到“自强化崩溃”的微观演化机制,揭示重试风暴、容量退化等正反馈回路的形成过程。文章提出系统化的治理思路,并详细介绍百度落地的多项核心实践,包括重试预算、队列限流、全局TTL控制等自愈机制,以及秒级流量调度与降级预案。通过真实案例与生产数据,为行业提供了一套可借鉴的雪崩预防与治理框架。
说明:本文是由2025 SRECon会议的《Preventing Avalanche Failures in Large-Scale Microservice Systems》的演讲者翻译并整理而成。
01 摘要
大规模微服务系统在享受分布式架构带来的灵活性和扩展性的同时,也面临着雪崩故障的严重威胁。雪崩是一种系统性故障模式,破坏力极大,难以预防,会给企业带来巨大损失。
本文深入分析雪崩的形成机理和演进模式,建立理论模型,从微观层面推导灾变过程,并通过真实生产案例验证了雪崩的快速演化路径。接着,通过系统化的思辨,探讨可能的治理方法。然后,介绍了百度的系统性的雪崩治理框架及一系列的核心实践机制。最后总结了近几年的治理成果,为行业提供了可借鉴的实践经验。
02 背景
大规模微服务系统本质是一张由大量节点和依赖交织而成的网络。请求从入口gateway进入系统,再层层向下形成深度调用链,往往还伴随着交叉依赖与高扇出。这种特点虽然带来了灵活性与扩展性,但天然埋着三类稳定性风险:其一是“未知中的未知”,如冷门链路、冷门代码在突发场景被激活;其二是容量级联风险,任一节点变慢都会把反压沿链路放大回传;其三是重试风暴,自我强化的流量会指数级扩散。在这种架构中,任何局部抖动都会以毫秒级在系统中传播。理解这张网络,就为理解‘雪崩’发生的原因奠定了基础。
大规模微服务系统依靠多种高可用机制来提升性能,表现为:
-
分布式架构避免单点故障并带来弹性扩展能力;
-
缓存抵挡流量激增并降低延迟;
-
重试实现瞬时恢复并提高成功率;
-
请求队列平滑流量尖刺并维持稳定吞吐量。
然而,在雪崩条件下,同样的机制会翻转到另一面,表现为:
-
分布式架构引入复杂依赖和更大的爆炸半径;
-
缓存失效引发流量洪峰和延迟激增;
-
不受控的重试导致指数级流量风暴;
-
后端变慢导致队列超时和入队失败。
03 直观感受雪崩的威力
这里举一个真实的例子给大家直观感受雪崩的威力。在一个IDC中,少量实例出现轻微健康退化,引发中等程度的延迟上升和轻微的可用性下降——没什么令人担忧的,只是典型的小波动。几乎同时,随着上游重试机制启动指数退避策略,流量迅速激增,导致服务在几秒内变得不可用。
然后,根据预定义的SLA执行自动预案,将大量负载从受影响的IDC转移到其他健康的IDC。但这些"健康"的IDC,只能在正常容量范围内运行,此时突然遭受显著的放大了的流量冲击,也变得过载。
紧急扩容马上被触发,扩充并启动更多的实例,但级联过载的蔓延的速度仍然比我们扩容策略的响应速度更快。
紧接着,多项尝试止损的动作被并行执行——流量比例调整、缓存命中率优化、超时时间缩减——然而它们在呈指数级的故障放大效应面前仍然无效。此刻,系统处理的主要是重试流量,其数量远超设计容量,线程池完全耗尽,请求队列被已超过SLA阈值并且注定要失败的请求填满。
这进一步引发了所有IDC的级联故障,最终导致系统级的完全崩溃,影响了大量用户。
在这个案例中,起初只是一个影响了少量实例的小问题,最终演变成了完全的系统崩溃。整个过程在仅仅2分钟内发生——这是大规模微服务系统在雪崩场景下如何自我毁灭的可怕呈现。
04 雪崩的特点
以上,我们直观感受到了雪崩的威力。那么,什么是雪崩?让我们从4个雪崩的特点来介绍雪崩。雪崩发生前,系统都会首先进入“非稳定”状态,这种状态“貌似正常、实则脆弱”。各项指标也许还在阈值内,但可用余量极少,表现为:负载上升,延迟上涨……此时的系统对扰动极为敏感,任何轻微扰动都会使它偏离稳定状态。
第二个特点是需要小的触发源扭转状态。触发源可能是多种多样的,比如,流量尖刺、网络瞬时抖动、硬件故障、软件冷门逻辑被触发……。它们本身并不致命,但在不稳定状态边缘,哪怕微小的触发都足以把系统推过临界点。真正导致系统边界违规的不是触发的强度,而是系统自身余量不足。然而,微小触发仍然是必要条件。
一旦越过临界点,系统会迅速进入自强化阶段,这是雪崩最典型的一个特点。在这种情形下,高可用优化机制统统都站到了反面。系统每增加一点恶化,高可用机制就产生一些正向反馈,从而导致进一步恶化。这条恶化-反馈循环持续加强,复杂且迅速。图中展示了几个例子。回路一:请求失败导致重试,然后导致负载上升,进而导致更多失败,最终形成指数级的重试风暴;回路二:少量实例略不健康导致被摘除,然后导致剩余实例更忙,进而导致更多实例被判不健康,最终引起容量指数级退化;回路三:错误事件导致大量的日志和监控,然后导致I/O用量上升,进而导致更多错误,最终使性能进一步恶化。
随着自强化阶段的持续,系统很快进入到失控和崩溃状态,这种状态最大的特点是无法靠自身恢复。线程池被耗尽,请求队列被已超时或必然失败的请求塞满,后端被无意义工作占满并拖慢,上游开始全面超时。
常见修复措施并不能奏效,甚至适得其反。比如,临时扩容往往慢于正反馈的加速导致资源就绪时系统早已熔毁,调cache难以解决重试反馈带来的流量放大,等等。需要专用的机制才能缓解,就像不能灭火器扑灭一颗正在爆炸中的核弹。唯有改变微观机制上的反馈结构,才能真正抑制雪崩。
让我们简要回顾雪崩的4个特征来理解雪崩的定义:
-
非稳态:系统看似正常但余量极少,对扰动高度敏感;
-
微小触发:流量尖刺或网络抖动等轻微扰动就能推动系统越过临界点;
-
自强化:高可用机制创建正反馈循环,通过重试风暴和容量损失放大恶化;
-
无法自我恢复:系统崩溃,资源耗尽。常见修复措施适得其反,需要专门干预。
05 理论模型
让我们从微观层面看一下雪崩。首先,我们看一个基本的模型。在这个模型中,流量以rps的吞吐流经一个服务的请求队列;然后,该服务的workers以concurrency的并发度处理队列中的请求,并发向后端进一步处理。在正常情况下,发向后端的吞吐也是rps。从请求到达本服务到本服务处理完成,所用的时间是latency。
基于Little's Law,我们有这样的公式:rps <= concurrency / latency。
真实的系统可以看做基础模型的管道似的级联连接。在这套连接系统中,需要保持任意位置的吞吐在实际流量之上,即在任意位置,都要维持这个rps <= concurrency / latency式子满足。
整个系统的吞吐受限于最薄弱的环节。需要精心地维持各环节脆弱的平衡。若任何位置的平衡哪怕短时间被打破,也会迅速触发级联反应,使系统雪崩。
06 微观灾变过程
接下来,让我带大家从微观层面看一下雪崩过程发生了什么。在这个抽象例子中,拓扑结构为 gateway → A → B → C,其中服务C变慢并成为触发点。
这张图使用“状态聚焦”的视角展示雪崩的快速微观演化过程。首先说明图中颜色的含义:红色代表恶化;灰色表示此阶段暂无新增变化。我们仅高亮当前需要重点关注的信号。
-
在健康状态下:系统延迟低,排队最少,吞吐量最优。
-
在触发阶段:C开始变慢,其延迟明显上升。
-
随着问题扩散,B和A的线程利用率和延迟几乎同时激增,而吞吐量和队列长度保持相对不变,显示出典型的’严重积压前的快速传播’行为。
-
随即来到堆积阶段:当并发达到上限后,B 和 A 的队列开始快速堆积,表明排队机制已被触发。
-
很快系统进入重试状态,B的超时触发上游重试。B接收到更多流量,而好吞吐比例下降,显示服务质量正在恶化。
-
接下来,流量开始放大,gateway级重试被触发,在A处造成流量激增,在B处造成第二次流量激增。对B产生的压力导致好吞吐进一步下降。
-
最终迅速坍塌到雪崩阶段:重试的重试形成正反馈循环,线程与队列被大量无效工作占满,系统彻底崩溃。
请注意三个关键点:线程利用率首先增加,队列长度快速跟进,吞吐量表现出两次或更多的快速跃升。
07 真实案例验证
让我们用真实的生产数据来验证前面的模型。由于这个故障发生在几年前,我们现在展示的这些指标是当时能够获取到的最完整的一组,尽管未能把前面模型里提到的所有指标都收集到。左边这个小图把B的总时间拆成了三部分:排队时间、本地处理时间、以及后端时间;右边的三张曲线的时间轴是对齐的。
这次故障仍然是C变慢了。首先,B的后端时间、整体延迟、线程使用量几乎同时抬升,与此同时,A的整体延迟也开始上涨。紧接着,B的排队时间开始上涨,很快,A的排队时间也跟着抬头——这正对应前一页的“扩散到堆积”的阶段转换。
接着我们发现一个有趣的现象:B的本地处理时间不增反降。其实原因很简单:线程更多时间在等后端返回,CPU空出来了,局部被处理到的请求就显得更快,这是常见的“局部变快错觉”。
再看吞吐曲线:A和B的RPS先出现一次阶跃,这是gateway的重试流量被激发;随后B的RPS再次跃升,这是A触发了二次重试。虽然我们没有直接画出“重试率”和“好吞吐占比”,但“排队时间增长”和“RPS两次阶跃”的组合,足以验证雪崩过程中由重试的驱动的故障放大路径。
整个过程大约发生在三分钟之内。
08 系统性思辨
既然我们理解了什么是雪崩以及它们在微观机制层面是如何工作的,让我们采用系统性方法来分析我们的选择。我想通过一个结构化的框架带着你来思考。在这个框架中,我们将检查雪崩的每个特征,并问:“我们能在这里干预吗?”通过这种系统性分析,我们能发现应该在哪些地方进行发力,以及哪些方法是现实的而非理想化的。
首先,是非稳态的避免。对于大规模分布式系统来说,完全避免非稳态是不现实的。系统本质上是面临无限触发源的复杂网络,其中HA机制更是一把双刃剑。它们从根本上是具有内在不稳定性的脆弱管道的级联。
尽管如此,我们仍然可以通过一些措施提升系统进入不稳定状态的阈值,使其更难进入不稳定状态,在面对更多触发源时能从容地运行。
接下来,我们思考能否消除所有触发源。实际上,这是完全不可行的。触发源对应的是一个无限的开放空间,不可枚举——包括网络抖动、流量尖刺、软件缺陷、硬件故障以及无数其他不可预测的因素。
虽然我们可以通过设计冗余机制来减轻干扰对系统稳定性的影响,但这种方法有两个主要局限性:首先是巨大的成本。例如,在网络基础设施层实施冗余链路聚合,在数据中心基础设施层面建设多套制冷系统,等等。而且,建设冗余机制往往涉及跨职能基础设施工程工作,实施困难且超出SRE的职责范畴。
接下来,我们能否消除自强化反馈回路呢?不可行!因为这些回路与HA机制内在相关,迫使我们必须做出权衡。
然而,将缓解机制嵌入到反馈回路内部,通过改变微观层面的反馈结构,可以将指数级放大转化为线性、可控的影响,大大降低其破坏力。就像用控制棒来阻止核反应中失控的中子放大。
最后,我们来探讨能否防止系统崩溃。这是可行的,主要通过两种互补的方法:最大化可用吞吐量与构建弹性恢复机制。即使系统超越临界阈值,我们仍可通过建立内部容错机制与外部干预预案,避免系统全面崩溃,并为系统创造多次恢复机会。
经过以上分析,我们发现可以在这些关键点实施雪崩治理工作,如右图所示。接下来,我们将详细介绍我们的核心实践。
09 我们的实践
前面说过,由于触发源众多,无法逐一验证系统在各种触发源下的行为,因此我们将所有触发源抽象为有限的几类场景,确保系统在这些场景下保持稳定——最大限度保持好吞吐。
这些场景分为两个维度:第一个维度是流量压力:包括流量逐步上升和瞬时激增。第二个维度是系统容量变化:包括绝对容量减少(如服务下线)和相对容量退化(如响应变慢)。
针对这四类场景,我们直接在生产环境中进行系统化验证:对于流量风险,我们在生产环境执行全链路压测,模拟流量的渐进式增长和瞬时尖刺。对于容量问题,我们在生产环境开展混沌工程,在真实环境下模拟服务容量缩减和延迟注入。
通过生产环境验证,我们提前发现潜在风险,确保系统在雪崩场景下保持最大可用吞吐的预期行为,结果可信且能代表实际系统性能。
早期检测对雪崩预防至关重要。我们建立了全面的监控系统,跟踪先导指标:
-
端到端失败数量
-
所有服务的通用指标,包括实例健康状态、实例CPU/内存/磁盘/IO利用率,以及coredump计数,等等
-
核心服务关键指标:包括请求队列长度、线程利用率、分位点延迟,等等
-
分片服务:分片丢失率
所有指标都是以秒级实时进行监控,并建立预警策略,使我们能第一时间感知到异常。
我们对系统自身的改进是建设一系列的"自愈"能力。首先是"重试预算",用于在client侧就地收敛重试风暴。每个请求携带"全链路重试状态"信息,RPC组件据此识别两类重试:直接重试(本服务对下游)与间接重试(上游祖先节点发起)。RPC组件维护一个"预算池",对两类重试流量配置不同的预算阈值,超过预算即快速失败。预算策略会随后端服务容量自适应调整,以充分使用后端容量。该机制把自增负载限制在可控范围内,避免指数级扩散。
在服务器端,我们还构建了一个称为“队列限流”的吞吐保护机制。我们将服务侧的请求队列做成多级优先级队列,并设置限流器。请求入队前先经过优先级过滤,在拥塞时,高优先级的请求允许通过,低优先级的请求被丢弃;限流器则根据实际处理速率自适应放行,同时,对于队列中超时的请求也会及时排出。在server侧容量到达瓶颈时,这套机制让有限的算力用于处理“好吞吐”。
我们也在系统中建设了更精细化的减少无谓的算力浪费的机制,即,全局TTL控制。每个请求在入口获得初始TTL,并随着请求在全链路传递;在各处理阶段结束后,按实际消耗的时间扣减TTL,并判断剩余值。若剩余TTL仍然大于0,则将这个新TTL作为后续阶段的TTL继续传递;否则,立即在此位置结束并返回。
虽然自愈机制已经将雪崩风险降至非常低的水平,但我们仍然为极其罕见的雪崩场景设计了多维度的秒级干预系统。
第一个维度是跨数据中心流量切换。我们实时监控多个敏感指标,当发生异常时,全局流量控制器在一秒内将流量从受影响的IDC迁移到其他IDC。
第二个维度是系统流量降级。流量分为不同等级,非用户流量(缓存刷新、预取等)按层级逐步减少直至关闭,为更高价值的用户请求让路。
第三个维度是架构裁剪。服务被分为不同级别(L2:低价值召回,L1:精确排序,L0:核心召回)。当损失发生时,我们做出秒级决策并按优先级逐步关闭服务级别。
第四个维度是超时套餐切换。多套系统级超时套餐根据损害程度自动适配,在吞吐和质量之间精妙地平衡。
10 治理结果
最后总结一下我们在治理雪崩上的成果。几年前,雪崩给我们的系统带来了比较多的PV损失,经过以上深入分析和系统治理,近几年完全没发生过雪崩,并且也几乎没有雪崩引起的PV损失。
数据平台数据智能化入库
百度APP日志处理框架升级之路
导读
面对百度APP日均数千亿PV、超百PB数据规模带来的巨大挑战,我们完成了数据仓库的系统性升级。本文详细阐述了通过"两步走"策略解决资源压力、处理延迟和架构瓶颈的全过程:第一阶段聚焦日志清洗环节的稳定性与成本优化,第二阶段实现实时离线链路解耦、核心数据隔离及计算框架容错能力提升。此次升级显著提升了数据处理时效性、系统稳定性和成本效益,为业务发展提供了更坚实的数据支撑。
背景
百度APP及其产品矩阵作为百度体量最大的C端业务线,在数据处理全链路面临规模与架构的双重挑战。日志清洗环节因日均几千亿PV、超百PB的庞大数据规模,导致计算资源持续承压、处理延迟频发,加之历史遗留的复杂日志格式,清洗稳定性与时效性逐步下降,存储成本高昂。与此同时上游日志数据仍存在实时与离线链路耦合、核心与边缘数据未有效隔离、计算框架容错能力不足等结构性问题,影响关键数据产出的稳定与时效。整体系统切换与优化面临高额的历史负担和技术重构成本,下游业务的数据可用性、决策及时性及深度运营分析均受到显著制约。
基于以上问题,我们制定了“两步走”的升级策略:第一阶段优先解决日志清洗环节的稳定性和存储成本问题;第二阶段在此基础上,重点推进数仓上层架构优化,包括实时与离线链路解耦、核心数据隔离处理以及计算框架容错能力提升,逐步实现整体数据仓库的高效、稳定与可持续升级。
01 第一阶段:多日志源整合
1. 2023年之前架构
在百度APP及其产品矩阵的数据体系建设过程中,日志清洗作为整个数据流水线的起始环节,其处理稳定性和产出时效性始终处于关键地位,是保障下游业务数据可用性与决策及时性的重中之重。然而,随着业务规模持续扩大和用户体量快速增长,每日产生的日志量急剧上升,由此带来的巨大计算压力使得整个清洗链路频繁面临资源瓶颈与处理延迟,稳定性和时效性均逐步下滑,难以满足下游各业务方对数据交付时间和质量的要求。与此同时,数据入口的分散催生了大量烟囱式的开发与冗余的计算逻辑,不仅推高了运维成本,更在源头形成了数据孤岛。下游基于此类数据构建的数仓架构必然复杂化,多表的 JOIN 与理解成本高昂,使得整个数据建设环节背负着日趋沉重的成本与协作压力。
2. 问题分析
2.1 旧架构分析
2.1.1 数据孤岛化加剧,认知与使用成本高昂
现有架构对每类日志采用独立落表方式,导致数据存储呈现碎片化状态。这种设计造成同一业务实体的相关信息分散在不同表中,形成严重的数据割裂。下游用户在使用数据时,不得不通过多表关联才能获取完整信息,不仅大幅增加了技术实现难度,更带来了沉重的认知负担。用户需要理解多张表的结构和关联关系,极易产生理解偏差,进而影响数据分析的准确性和可靠性。
2.1.2 关联查询性能瓶颈,制约数据价值释放
与此同时,多表关联查询模式给系统带来了巨大的性能压力。随着数据量的持续增长,表连接操作的成本呈指数级上升,查询响应时间显著延长。特别是在需要跨多个表进行关联分析的场景下,系统往往需要耗费大量计算资源和时间,无法满足业务对高效数据分析和快速决策的需求,严重制约了数据价值的及时释放。
此外,原始日志结构中普遍存在的复杂嵌套格式(如多层JSON、数组结构等)大幅增加了数据清洗和解析的复杂度。大量业务自定义字段缺乏统一规范,导致解析逻辑冗余且低效,进一步降低了整体处理性能。这些因素共同加剧了数据处理的延迟与资源消耗,形成系统性瓶颈。
2.1.3 维护复杂度与脆弱性并存,系统稳定性堪忧
独立的数据处理流水线,导致系统维护点分散。任何逻辑变更或schema调整都需要在多处同步实施,极大地增加了维护工作量。这种架构的脆弱性也显著提高了出错风险,单个任务修改的错误可能引发连锁反应,影响整个数据链路的稳定性。
特别需要指出的是,当前采用的UDW数仓及配套ETL框架仍是2012年上线的技术方案,已明显落后于业界主流水平。该框架存在诸多局限性:首先,其兼容性差,难以与现有开源生态工具链高效集成;其次,基于C++的MR计算框架稳定性不足,日常运行中容易出现各种异常;最后,开发调试效率低下,严重制约了数据需求的迭代速度。这些技术债务不仅增加了系统的维护复杂度,更成为制约数据平台发展的关键瓶颈。
2.2 重构思路分析
理想状态:从数据架构的理想设计来看,基于通用宽表数据建模方法论,采用“一步到位”的方式直接产出高度整合、面向主题的Turing宽表,是最为高效和优雅的解决方案。它能够减少中间冗余加工环节,提升数据一致性和复用度。
升级成本:下游业务方因历史原因,数据应用架构高度依赖传统UDW模式的数据组织与服务方式,迁移至Turing宽表体系涉及大量脚本改造、逻辑核对与业务适配工作,技术切换和数据迁移成本极高,导致架构升级短期难以实施。
思考:为实现数据架构的平滑升级,本次重构方案采用渐进式过渡策略,在着力解决现有架构核心痛点的同时,必须充分考虑百度业务数据链路长、历史包袱重的现实情况,审慎平衡技术先进性与落地可行性。方案设计严格遵循"平滑过渡、风险可控、成本最优"三大原则。
需要特别指出的是,由于现有数据体系深度嵌入各业务线的策略计算与离线分析环节,其紧密的耦合关系导致配套升级难度极大、周期长。这不仅涉及底层数据表的更替、依赖路径修改,更要求对依赖原有数据模型的下游业务进行协同改造和全面适配,沟通和推进难度极大。所以在保障业务连续性的前提下,如何有序推进全链路的升级切换是本次升级的重中之重。
建模思路:
(1)降低迁移成本
在数据中间层设计上,方案延续使用刻钟级UDW表作为缓冲层,通过将多个离散的UDW表整合为统一的宽表模型,进一步降低下游的使用和理解成本。同时,对表schema实施精细化改造,包括消除冗余字段、统一数据标准、优化存储格式,并重构字段逻辑以提升数据一致性。这种设计既保持了与现有下游系统的兼容性,又显著降低了数据使用复杂度。
(2)双轨输出机制
为确保迁移过程的平稳性,方案采用双轨输出机制:一方面继续提供优化后的UDW宽表,保障现有作业的无缝运行;另一方面通过聚合加工生成小时级Turing表,作为统一对外输出的日志宽表。这种渐进式迁移路径使下游用户可根据自身情况灵活选择切换时机,最大限度降低升级成本。
(3)兼顾历史和未来
此次架构优化为后续全面升级奠定了坚实基础。通过UDW层的预处理和Turing表的逐步推广,最终将实现架构的完全过渡,在提升系统性能的同时确保业务连续性,达成技术演进与业务稳定之间的最佳平衡。
3. 解决方案
过渡方案设计与实施:稳时效、降成本、提效率的综合治理
面对日志清洗环节日益严峻的稳定性、时效性及成本压力,我们制定并实施了一套详尽的过渡性解决方案。该方案并未激进地推行一步到位的Turing宽表迁移,而是立足于现有技术生态,以快速解决下游业务最迫切的痛点为目标,重点攻坚“产出时效不稳定”、“存储计算成本高”及“明细数据查询效率低下”三大核心问题。
3.1 优化处理粒度与逻辑沉淀,保障时效与复用性
为彻底扭转小时级任务积压与延迟的局面,我们首先对调度周期进行了粒度细化,将日志清洗任务从小时级调度全面提升至刻钟级(15分钟)。这一调整显著降低了单次任务的处理数据量和计算压力,使数据产出的延迟大幅减少,稳定性和时效性得到了根本保障。在技术选型上,我们并未盲目更换计算框架,而是继续沿用成熟稳定的C++/MR框架,确保了迁移过程的平稳性与可靠性。
同时,我们致力于提升数据的易用性与标准化程度。针对下游业务方需要反复从复杂JSON、Map等嵌套字段中解析提取关键信息的痛点,我们进行了大规模的业务通用逻辑下沉工作。将超过100个高频访问的埋点属性进行预解析、扁平化处理,转化为单独的标准化字段。这不仅极大减轻了下游的数据预处理负担,更直接提升了基于这些字段的查询过滤与聚合分析效率,为下游开发节省了大量时间。
3.2 兼顾历史依赖与未来演进,提供平滑迁移路径
我们充分认识到下游业务对原有UDW数仓体系的强依赖性。为保障业务的连续性,我们并未强制要求所有方立即迁移,而是采取了双轨并行的支撑策略。在产出新一代数据模型的同时,我们继续提供UDW中间表,确保那些尚未准备好迁移至Turing宽表的业务方能够无缝对接,无需修改现有代码,极大降低了方案的落地门槛和风险。
3.3 深度优化存储与查询,实现性能跨越式提升
为进一步降低存储成本并提升Turing宽表的查询性能,我们对其存储结构进行了深度优化。
- 合并小文件与高效压缩:海量小文件是制约查询性能的首要元凶。我们通过按设备ID、点位ID、时间戳等关键字段进行精细排序,将数据写入为连续有序的大文件,从而将单天高达800万个小文件合并至60万左右,文件数量减少了近93%。在存储格式上,我们选用Parquet列式存储,并经过充分调研测试,采用了ZSTD压缩算法。ZSTD在压缩比、压缩/解压速度上取得了最佳平衡,且完美支持多线程,最终实现了每天节省超过420TB的巨大存储开销,成本效益极其显著。
4. 新的问题&解决策略
问题1:宽表数据量膨胀导致的查询性能下降
解决策略:为应对宽表数据量激增对查询性能带来的挑战,我们实施了体系化的查询加速方案,显著提升海量数据下的检索效率
-
强制分区限制策略:在查询引擎层上线了强制要求限制分区条件的规则,避免了全表扫描带来的巨额元数据开销,大幅提升元数据检索效率。
-
查询结果缓存:对常见的热点查询结果进行缓存,对于重复性查询实现了秒级响应。
-
智能资源调度:根据查询的计算复杂度,系统自动将其调度到不同配置的资源池中执行,简单查询快速返回,复杂查询获得充足资源,实现了集群资源的高效利用。
问题2:分区数量增多导致点位所在的分区变得困难
解决策略:针对分区维度增加后,数据定位难度加大的问题,我们通过元数据管理与平台化集成提供解决方案:
-
新建分区元数据集,以天为粒度预先计算并存储所有点位与分区的映射关系,形成高效的点位分区定位查询,为点位所在分区快速检索提供基础支撑。
-
与现有点位管理平台深度集成,在其点位查询界面新增【查一查】功能。用户可通过界面化操作直接获取精准的数据分区信息及查询SQL模板,极大提升了用户使用的效率,降低了用户使用成本。
02 第二阶段:全面提速
1. 2023→2024年架构
随着业务发展,该数仓已完成由UDW(统一数据工作台)向Turing(新数据工作台)的改造,并初步建立起体系化的数据模型与分层数据集,显著提升了数据复用性和分析效率。基于这些宽表与数据集,大部分常规分析场景已能够快速响应。然而,在数据加工的最上游,即明细数据宽表的生产环节之前依旧包含缓冲的刻钟级udw表,因此仍存在若干架构性瓶颈。首先,实时数据处理链路与离线批处理链路相互耦合,资源竞争与依赖关系复杂,影响了整体任务的稳定性和时效性;其次,核心业务指标与非核心附属数据未被有效拆分处理,导致关键数据产出易受边缘数据波动或延迟的干扰;此外,当前的计算框架对于数据迟到、重复、异常值等复杂情况的处理灵活度不足,容错与自适应能力有待加强。
为彻底解决这些问题,进一步提升数据产出的时效性、准确性和稳定性,以更好地赋能百度APP及其产品矩阵及各下游业务的数据分析与决策,亟需结合各数据点位的实际使用情况和业务优先级,对最上游的日志ETL(抽取、转换、加载)处理流程进行系统性的优化与重构。
2. 问题分析
当前数据ETL处理流程面临以下几个核心挑战,这些问题不仅影响数据产出的效率与稳定性,也为下游业务数据的准确性和及时性带来风险。
2.1 开发框架灵活性不足,资源协调与弹性扩展能力受限
目前的ETL任务仍沿用原有UDW大表处理框架,通过单机Hadoop Client提交任务,并依赖QE(底层为mapreduce引擎)进行计算。该框架在资源调度和权限管理方面已逐渐暴露出瓶颈。同时udw是2012年提出的数仓建设方案,随着开源计算、存储技术的发展,udw性能逐步落后业界,部分功能不具备继续升级迭代可行性。一旦出现上游数据延迟、队列资源拥塞或系统异常,容易导致任务大规模积压。由于缺乏跨队列或跨资源的调度容灾能力,无法协调其他计算资源执行任务回溯与补偿,最终将直接影响整体数据产出时效,甚至波及下游多条业务线的核心数据应用。
2.2 核心与非核心数据处理耦合,异常影响范围扩散
在日志清洗ETL环节中,核心业务数据点位与非核心业务数据点位、以及实时与离线数据流目前尚未进行有效拆分处理。这种架构层面的耦合导致一旦上游数据源或计算过程中发生异常,其影响面会迅速扩大,不仅关键业务指标受到冲击,非核心业务数据的问题也可能反向干扰核心链路的稳定性。缺乏业务优先级识别和隔离机制,降低了计算链路的整体容错能力和故障隔离水平。
2.3 计算链路冗长复杂,维护困难且稳定性面临挑战
当前处理流程中包含UDW中间缓冲层,导致计算环节增多、链路层级深化。较长的依赖链不仅增加了数据产出的端到端延迟,也显著提高了运维监控和故障定位的复杂度。任何环节出现性能波动或失败都易引起连锁反应,威胁整体任务的稳定性和时效性,同时也带来较高的人力维护成本。
2.4 实时与离线数据源不一致,存在冗余计算与口径偏差
百度APP及其产品矩阵业务当前使用的实时计算链路和离线数据链路在核心指标上并未实现数据源统一,两条链路独立处理且并行存在。这导致相同指标需要在不同流程中重复计算,既造成资源浪费,也增加了数据口径对齐的难度。长期来看,此类架构问题会直接影响关键指标的一致性和可信度,对业务决策准确性构成潜在风险。
2.5 存储无序增长,数据冗余和存储成本与日俱增
随着业务规模的持续扩张和流量快速增长,支撑核心业务的明细数据宽表总量已达到百PB级别,存储与计算成本压力日益凸显。然而,不同业务域对数据的保留周期和使用频率存在显著差异,全部数据长期存储既不经济也无必要。
3. 解决方案
3.1 ETL框架升级
在完成由多张udw表到Turing表的优化工作完成后,数据处理的时效性与稳定性虽然取得了一定改善,但仍存在进一步提升的空间。具体而言,原有的C++ MR计算框架在任务运行过程中逐渐暴露出两类典型问题:一是容易发生计算长尾现象,个别任务实例处理缓慢,拖慢整个作业完成进度;二是基于单机调度的模式存在可靠性瓶颈,整体资源协调和任务容错能力有限。这些问题导致数据产出的延迟风险依然较高,难以完全满足业务对数据时效日益提升的要求。
为解决上述痛点,经过充分的技术调研与架构评估,我们决定将计算框架升级为TM+Spark的组合方案。其中,TM(Task Manager)作为厂内自研的高性能流式处理框架,在多个关键维度上显著优于原有的C++ MR架构。
TM(Task Manager):更高的容错性和更强的稳定性
首先,在容错性方面,TM具备更为智能和敏捷的错误恢复机制。当某个计算实例发生故障或执行缓慢时,TM调度系统能够迅速感知并主动发起抢占操作,将当前Task动态迁移至新的实例继续处理,从而有效避免传统MR框架中由于个别长尾任务导致的整体作业延迟。这一机制极大提升了作业的稳健性和执行效率。
其次,在调度稳定性方面,TM基于Opera调度系统进行资源管理与任务分配,这一调度架构具有高度解耦和资源隔离的特点。每个任务实例独立运行,互不干扰,有效避免了在MR模式下由于同一队列中其他高负载或异常作业所带来的负面冲击,从而保障关键数据处理任务的稳定性和可预期性。
此外,TM框架也在输出存储效率方面做出了重要升级。它原生支持输出Parquet列式存储格式,并集成ZSTD压缩算法,在减少存储空间占用的同时大幅提升了后续查询操作的I/O效率。这一改进使得数据在写入阶段就具备更优的列组织结构和压缩特性,为下游分析提供了高性能的数据基础。
主流开源框架Flink和TM的对比如下:
Spark:通过构建DAG,计算更高效;利用RDD或者DataFrame减少IO耗时;多线程机制,执行速度更快。
Spark对比MR的核心优势:
-
速度:基于内存计算,无需反复做读写操作,更加高效
-
高度集成:spark丰富的API和高级抽象的函数可以轻松实现复杂的逻辑计算和处理,无需和MR一般需要编写复杂的处理逻辑
-
计算模型:内置的RDD数据结构可以提高数据计算的容错性;查询优化和执行优化可以适应复杂数据的处理和查询
结合Spark通用计算引擎强大的分布式内存计算能力和丰富的生态组件,新框架不仅解决了之前C++ MR模式中的长尾与调度瓶颈,还进一步实现了处理链路的统一与优化。Spark的高扩展性和TM的流式稳健性相结合,共同构建出一个容错能力强、资源利用高效、运维负担低的新一代数据处理架构,为业务提供更低延迟、更高可靠性的数据服务。
3.2 日志分类分级
3.2.1 埋点上线不规范,被动兼容推高处理成本
在当前百度APP及其产品矩阵业务高速发展的背景下,日均处理日志量已达3000亿PV的庞大规模,数据流的稳定、高效与成本可控变得至关重要。
原有的埋点分类和校验存在两个突出的问题:
-
上报不规范:存在大量不经过日志中台统一校验而直接上线的业务打点,这些“非规范”打点格式各异、质量参差不齐,极易引发解析异常。
-
处理成本高:下游的日志清洗ETL环节被迫陷入“被动兼容”的循环中,需要频繁地跟进制订适配规则以解析这些非标数据,不仅带来了极高的运维成本,更因计算资源的无效消耗而加剧了整体处理链路的负担,严重制约了数据产出的时效性与稳定性。
3.2.2 通过协同治理实现日志中台全流量覆盖
为从根本上破解这一难题,我们基于对百度APP及其产品矩阵数据全链路的深入洞察,发起了一项跨体系的协同治理工程。联合了日志中台团队、各业务研发团队、QA质量保障团队及PMO项目管理团队,形成了强有力的专项工作组。
第一阶段的核心任务是对所有日志模块进行全域梳理。我们共同制定了统一的《新增业务模块接入日志中台规范》与《日志埋点规范》,明确了从数据采集、上报到校验的完整标准流程,并强力推动百度APP及其产品矩阵(包括主客户端及相关创新业务)的全量需求空间、代码仓库及日志模块,完成向日志中台的标准化接入迁移。这一举措将日志中台的流量覆盖能力从治理前的约80%一举提升至100%****,实现了全流量管控。
更重要的是,我们在日志中台增强了多项主动校验能力:包括日志长度校验、关键公共参数完整性校验、以及精确到需求ID的粒度校验。这使得任何不合规的打点企图在测试和上线阶段就能被即时发现和拦截,实现了“问题早发现、早解决”的闭环管理,从而构筑起覆盖全场景的打点需求上线质量保障体系,从源头上杜绝了异常日志的产生。
3.2.3 打破“只上不下”僵局,建立埋点生命周期管理
在成功建立起“入口”管控机制后,我们将治理重心转向对历史存量埋点的“出口”梳理与优化。长期以来,由于缺乏有效的评估手段,点位数据存在着“只增不减”的痼疾,大量废弃或无效点位持续消耗着巨额的计算和存储资源。为此,我们创新性地从鉴权信息入手,通过对十几类不同下游使用场景(包括内部报表、算法模型、RDC数据转发服务等)的全面调研与信息收集,并对相关日志解析链路进行深度分析,首次精准地绘制出以百度APP及其产品矩阵全量15000多个点位为起点的、覆盖所有下游应用场景的“点位全链路使用地图”。
基于这张价值地图,我们清晰地识别出超过10000个点位已无任何下游业务使用或价值极低。通过严格的评估与协作流程,我们果断对这些埋点进行了下线处理,下线比例高达存量点位的71%。此次大规模治理行动,不仅直接释放了海量的计算和存储资源,有效缓解了系统瓶颈,更打破了长达多年的“埋点只上不敢下”的历史僵局,建立了点位的全生命周期管理模式,为后续数据的精细化管理与成本优化奠定了坚实基础。
3.3 AB实验数据扇出处理
3.3.1 现状与问题
在数据驱动的业务迭代中,A/B实验平台的指标建设和效果评估能力至关重要。然而,随着业务快速扩张和实验复杂度的提升,原有的实验数仓架构逐渐显露出严重瓶颈。平台最初是在通用数仓分层模型的基础上,采用“每个指标单独计算”的模式进行建设。这种设计在初期虽然灵活,但随着实验数量和指标数量的急剧增长,计算链路变得异常复杂、冗余且难以维护。由于缺少与公司数据中台团队的深度协同和标准化约束,每次新增实验指标都需要大量重复开发,导致实验数据需求的交付周期不断延长,严重拖慢了业务迭代速度,引发了业务团队的负反馈。
3.3.2 解决方案
(1)分析过程
理想的解决方案是直接复用百度APP及其产品矩阵已有的标准化大宽表进行实验指标配置。即基于一张集成所有关键维度与指标的大宽表,快速定义和产出实验分析所需的数据集。然而,现实情况却更为复杂:百度APP及其产品矩阵客户端同时线上进行的实验数量极多,平均每个cuid(用户唯一标识)对应的实验ID(sid)字符长度已超过2400字符。这个长度几乎相当于单条日志原始存储容量的40%,如果直接将实验ID维度接入宽表,将导致每条日志存储膨胀近一倍。这不仅会带来极高的存储成本,也会大幅增加下游所有数据应用的数据扫描量和传输开销,严重拖慢查询性能,进而影响整个数据链路的效率。
(2)设计思路
面对这一独特挑战,我们并未选择传统的宽表集成方案,而是从数据生成的源头实施了更根本的架构优化。我们重点对实验ID映射关系进行了拆分和重构:将sid与核心行为数据解耦,设计并建设了独立的sid维表。该维表直接从日志源头统一生成,整合了来自客户端的实验曝光及分组信息,并实现了对业务方、评估方各自独立建设的多套映射关系的全面统一。这一举措不仅从本质上避免了主宽表的存储膨胀,还彻底解决了因数据来源不一致而导致的实验效果评估diff问题,显著提高了实验数据的准确性和可信度。
(3)成果与收益
在此基础上,A/B实验平台的分析查询不再依赖于对超大宽表的直接扫描,而是通过sid维表与核心行为宽表进行动态拼接的方式实现指标计算。
在指标口径对齐方面,已完成实验类指标与OKR指标的口径统一工作,累计对齐上线指标2000余个,覆盖多个主题和维度。实验指标改由数据中心宽表统一生产,显著减少了以往在指标口径沟通与对齐方面的成本;在实验效率提升显著,指标开发环节通过复用宽表及数仓下沉逻辑,并升级计算框架,使常规需求开发周期从原先2周以上缩短至1周内,开发效率提升超50%。同时核心指标计算SLA由T+14小时提升至T+10小时,处理时效明显提高;在计算资源成本方面,通过整体数据流复用和抽样日志整合优化,实现了计算资源成本的有效降低。另外,联动产品及策略团队治理并下线无效实验指标超1800+,释放的资源进一步支撑了新场景的指标建设需求。
4. 分级存储治理
随着业务规模的持续扩张与产品矩阵的不断丰富,百度APP及其产品矩阵业务的日志数据量呈现指数级增长,单张核心Turing数据表的存储量已达到百PB级别,面临巨大的存储与成本压力。传统的统一存储周期策略难以适应当前复杂的使用场景:一方面,大量短期数据被无效保留,占用巨额存储资源;另一方面,部分核心业务场景仍需依赖长周期历史数据进行跨年指标对比、关键数据需求回溯与深度建模分析。
为解决这一矛盾,我们针对Turing表启动了多维度的精细化存储治理工作。通过深入分析业务使用特征与数据访问频率,我们建立了差异化的数据生命周期管理机制,实施**“热->温->冷”**三级数据分层存储策略。对高频访问的近期数据全部保留,对访问频率较低的长期历史数据自动进行转储、压缩或者裁剪等,并配套建立完备的数据取回与回溯流程。
该项治理在充分保障核心业务长周期数据使用需求的前提下,显著压缩了整体存储规模,实现了存储成本的大幅优化,为未来数据的可持续增长与高效管理奠定了坚实基础。
具体实施策略:
03 总结与展望
随着业务规模的持续扩张和产品矩阵的不断丰富,数据量呈现指数级增长,这一趋势持续驱动着数据处理架构与模型的演进与迭代,同时也对数据分析的敏捷性、易用性和可靠性提出了更高要求。在数仓系统全面升级的过程中,我们着力优化数据处理全链路,通过改进调度机制、减少计算环节、强化故障自动恢复能力,显著缩短了整个数据处理流程的时长,有效识别并排除多项潜在稳定性风险。此外,依托于对全端埋点体系的系统化梳理与标准化规范,构建了高质量、可复用的数据资产底座。
本次整体架构的升级为业务提供了坚实的数据支撑,在数据时效性、准确性和使用便捷性方面均实现显著提升。作为百度体系内最核心且数据规模最大的业务板块,百度APP仍面临数据持续激增带来的诸多挑战,包括埋点规范统一难度高、技术栈兼容与选型约束多、日志解析复杂度高、存储结构灵活多变以及成本控制压力增大等问题。
面向未来,我们将持续推进数仓架构的深度优化,重点围绕埋点治理、架构升级、效能提升、存储模型优化和资源精细化管理等方面展开工作。目标是构建一套具备更高时效性、更优数据模型、更低存储与计算成本的全新一代数仓链路,为业务创新与决策提供高效、可靠、低成本的数据服务能力。