普通视图

发现新文章,点击刷新页面。
昨天以前掘金专栏-百度Geek说

基于Flink的配置化实时反作弊系统

作者 百度Geek说
2025年3月4日 10:33

导读

本文详细阐述了基于Flink构建的实时反作弊流式过滤系统,针对大流量场景下的复杂特征计算、高频策略热更新、模拟过滤验证及多场景数仓对接等核心挑战,提出来多项解决方案,实现了秒级特征计算的实时过滤功能,有效支撑高并发场景下的精准风控判定,并通过ClickHouse与图灵双链路数据输出,满足实时监控与离线分析的多样化需求,为互联网业务提供了高吞吐、低延迟、强稳定的实时反作弊解决方案。

01 简介

在互联网业务高速发展的今天,反作弊已成为APP厂商生态稳定运行的重要保障。作弊行为层出不穷,包括恶意点击、刷单、羊毛党等,这些行为不仅会破坏平台公平性,还可能造成巨大的经济损失。因此,构建一个高效、灵活、可扩展的实时反作弊系统变得尤为重要。

反作弊系统根据业务属性和时效性可分为三类:在线反作弊、实时反作弊与离线反作弊。其中,在线反作弊具备最高的时效性,能够即时响应风险;离线反作弊依托最全面的信息,支持深度分析与建模;而实时反作弊则兼具二者优势,提供平衡的时效性与信息丰富度。

在线反作弊系统通过快速处理简单指标进行判断,例如分析当前请求携带的字段信息,并结合基于 Redis 的简单累计值(如访问频率或特定行为计数)来制定策略。这种系统以低延迟为核心,能够在毫秒级别响应反作弊判定结果,适用于拦截时效要求高的风控需求。

离线反作弊系统通过对完整的离线数据进行大规模、长周期的数据挖掘和样本分析,为优化线上策略、构建特征黑产库和训练高精度模型提供支持。然而,由于依赖离线数据的批量处理,其时效性相对较低,通常难以满足实时风控的需求,更适合用于长期策略优化和深度分析场景。

实时反作弊系统能够在秒级别和分钟级别对用户的异常行为做出反馈,及时识别作弊用户并对业务进行止损。虽然其时效性略低于在线反作弊,但得益于对丰富维度和行为序列特征的分析,实时反作弊可以实现更加精准的策略判定,在精准性与时效性之间达到良好的平衡。

图片

本篇文章我们将重点分析实时反作弊流式系统的相关实现。

02 流式系统面临的核心问题

在实际建设过程中,我们需要解决以下关键挑战。

2.1 复杂的特征计算

在实时反作弊场景中,用户行为数据规模庞大且动态变化(如电商大促、搜索点击等),系统需要处理海量的用户行为数据,并需基于时间窗口快速计算多维特征(如用户点击频率、IP集中度、设备关联账户数)。这些特征需覆盖不同窗口粒度(秒级、分钟级、天级)和窗口类型(滑动、滚动、会话窗口),以捕捉异常行为模式。

  • 窗口特征计算的挑战(多维度多窗口多指标聚合):反作弊策略通常需要基于不同时间窗口(如分钟级、小时级、天级),不同维度(用户、设备、IP等)进行特征累积计算。例如,计算某个用户在过去1小时内的点击次数,或者某个IP在过去24小时内的访问频率。这些计算涉及滑动窗口、滚动窗口等多种窗口类型,计算量大且复杂。

  • 数据乱序问题:网络延迟或分区消费不均可能导致事件乱序到达,若未正确处理,会导致特征计算不准确,进而影响反作弊策略的判定。

  • 高并发下的状态存储优化:在高并发场景下,特征累积计算需要频繁访问状态后端(如RocksDB),导致性能瓶颈。例如,当QPS达到数十万甚至上百万时,状态后端的访问压力会显著增加,影响系统的吞吐量和延迟。长周期窗口(如月级)到期时,大量Key需同时清理状态,引发瞬时资源争抢,导致作业卡顿。

详见 3.2 大规模窗口特征计算,通过 内存缓存+微批处理减少状态访问、事件时间排序缓解乱序影响、keyBy和trigger优化降低状态后端压力,最终支撑高吞吐场景下的精准计算。

2.2 高频的策略更新迭代

反作弊策略需要快速响应新型作弊行为。例如,当出现新的刷单手段或恶意点击行为时,风控团队需要迅速调整策略,以应对新的威胁。此外,不同业务场景(如广告点击、电商交易、社交互动)的反作弊策略差异较大,策略的复杂性和多样性增加了系统维护的难度。

  • 高频迭代需求:

  • 反作弊策略需要根据业务需求和作弊手段的变化进行高频更新,传统开发模式(修改代码→测试→发布)无法满足时效性。部分策略需“热生效”,避免作业重启导致数据丢失或计算中断。

  • 策略复杂性升级:

  • 多规则嵌套:单一策略可能需组合字段匹配(如IP黑名单)、模型评分(如行为异常概率>90%)、时间窗口特征(如近5分钟同一设备注册账号数>3)等多层条件,这些策略的复杂性增加了开发和维护的成本。

  • 配置管理风险

  • 人工修改配置文件易出错(如语法错误、字段误配),导致作业崩溃或策略漏判。

详见 3.3 配置化,通过全流程的配置化升级和配置文件托管,将策略规则、特征计算、字段抽取等逻辑抽象为配置文件,支持快速策略调整和上线,减少对底层代码的依赖,提升策略迭代效率。

2.3 模拟过滤的支持

在反作弊策略上线前,风控团队需要对策略进行测试和验证,以确保其有效性和稳定性,这一过程称为模拟过滤。在实时反作弊系统中,模拟过滤是策略上线前的核心验证环节,其必要性体现在以下三个关键维度:

提前规避线上风险,防止“误杀”与“漏杀”:直接在生产环境上线新策略存在风险,可能导致误判或漏判,影响业务正常运行。因此,需要在测试环境中对策略进行模拟过滤,确保其准确性和稳定性。

验证策略性能,避免作业过载:模拟过滤历史峰值流量(如大促期间数据),验证作业在极限负载下的稳定性。

历史回溯与极端场景覆盖:从HDFS读取数月前的全量数据(如黑产攻击事件日志),验证策略对历史攻击的检测能力和进行数据回溯。

图片

详见 3.4 模拟过滤的实现,通过配置化、线上流与测试流隔离、数据Source改造等方式,加速策略效果验证环节。

2.4 多场景数仓对接与平台整合

我们的系统产出的数据需要支持业务方的复杂分析需求。例如,基于反作弊结果进行策略优化,实时监控作弊行为的影响,对历史数据进行深度挖掘。

目前我们支持多种数仓形式(如实时ClickHouse与离线Hive)的数据产出,满足不同业务场景下的需求,包括实时数据看板、策略评估、历史回溯等应用。

  • 数据产出的便利性:反作弊系统需要将计算结果输出到多种存储系统(如ClickHouse、Hive、Redis等),以满足不同业务场景的需求。例如,实时数据需要写入ClickHouse用于实时监控,离线数据需要写入Hive用于历史分析。

  • 自助分析能力:业务方需要对反作弊结果进行多维度的分析,例如按时间、地域、用户群体等维度进行统计分析。传统的固定报表无法满足这种灵活的分析需求。所以支持业务方进行自助分析,能够根据需求灵活查询和分析数据,而不依赖开发团队的支持。

详见 3.5 便捷的数据分析,通过将反作弊结果输出到ClickHouse和Hive,支持实时和离线分析。同时,接入TDA(Turing Data Analysis自助分析平台),业务方可以通过简单的SQL查询或可视化工具,灵活分析反作弊数据,满足复杂的分析需求。

03 反作弊流式框架介绍

3.1 反作弊系统整体框架

整个实时反作弊的生效流程图如下:

图片

上图展示了 Flink 反作弊流式实时过滤系统 的整体架构,包括 风控平台、实时作业、外部存储 三大核心模块,整体流程如下:

  • 风控平台(配置分发):反作弊工程师在平台上编辑策略规则、配置特征计算逻辑,并一键生成配置文件和启动模拟过滤验证策略效果。测试通过后,策略配置通过平台分发至实时作业。

  • 实时作业(配置解析与执行):Flink 作业解析平台下发的配置文件后,构建作业各个模块,包括数据接入、ETL处理、特征计算、规则匹配等,最后提交并执行流式任务。

  • 作业结果存储(结果输出):ClickHouse,存储实时计算结果,支持快速查询与监控。Hive:存储离线数据,用于历史回溯与深度分析。Redis:提供低延迟查询,支持在线服务实时访问反作弊结果。消息队列:将判定结果传输至下游业务系统,供下游实时决策。

Flink作业内部,实时流运行各个模块拆解如下:

图片

流式作业的主要模块可以分为:

  • 数据接入Source:业务事件日志数据(用户行为、支付、点击、搜索等)接入。

  • 数据ETL处理:数据清洗、转换、标准化;简单维度拼接(ip 映射城市等);第三方字段请求(风险评分、黑设备、用户画像等)。

  • 多重窗口特征计算:时间窗口(分钟级、小时级、天级、周级、月级)、滑动、滚动窗口等,多种维度多种聚合函数进行特征累积聚合。

  • Join阶段:负责将特征和原始日志进行join。

  • 规则策略匹配与判定:机器学习模型打分,配置化规则引擎基于之前的所有信息进行最终判定。

  • 下游输出:实时反馈给线上服务、下发给业务方、入数仓表等方式将判定结果进行输出落盘。

3.2 大规模窗口特征计算

对于整个作业而言,主要计算资源就是用于累积基于窗口的特征。对于业务需求而言,不同窗口下的特征聚合结果是提升判定的准确率和召回率最重要的信息。

我们的窗口累积逻辑主要基于 Flink 窗口功能实现,包括TumblingWindows、SlidingWindows和SessionWindows,Session窗口使用较少。我们未使用其原生Aggregate 函数,而是采用了更底层的 WindowProcessFunction实现窗口聚合逻辑。这种方式的优势在于为后续优化提供了更大的灵活性和定制空间。

为了满足业务诉求,我们也对原生的窗口机制进行了多项优化,主要升级点有以下几个:

  • 提前触发:无需等待窗口结束即可实时下发累积结果,满足业务对于数据时效性的要求。

  • 批量更新和抗乱序:采用批量状态更新方式,减少频繁读取与写入,同时在微批更新时进行局部重排序,以降低乱序影响。

  • 键缩减-粗粒度KeyBy:优化keyBy和窗口触发器设计,减少状态访问频次,提高缓存命中率,降低计算开销。

下边将分别进行介绍。

3.2.1 时效性优化-提前触发

默认情况下,Flink 的每个窗口自带一个触发器(Trigger),在窗口结束时触发计算并生成聚合结果。然而,在实时性要求较高的反作弊场景中,如果窗口长度长达一天,等待窗口结束再下发结果显然不符合要求的。因此,我们需要在窗口尚未结束时,通过特定条件提前触发窗口计算,这种机制称为“提前触发”。

Flink 提供了多种现成的窗口触发方式,包括按ProcessTime定时触发、按EventTime定时触发、按固定条数触发等,同时也支持自定义触发方式。针对我们的业务需求,目前采用的是按事件时间的间隔提前触发方式。具体触发间隔依据不同业务场景设定,能够在秒级或分钟级就能得到窗口的聚合结果。

图片

上图: 展示了 Flink 原生窗口的触发机制及其聚合过程。每个绿色矩形表示一个窗口,窗口范围内累积了多个事件,编号为 1、2、3 、4、5。红色圆圈表示触发时下发的特征数据,从上图可以看到,窗口触发是在窗口结束时统一执行的,下发了2、5、3、1四条特征。

下图:改造后 - 提前触发机制**,**展示了优化后的窗口触发机制,通过提前触发减少延迟。每个绿色矩形依旧表示一个窗口,但触发时间提前,避免了窗口结束时的集中计算,红色圆圈同样表示输出结果。提前触发机制在窗口中按事件到达顺序多次输出,窗口中的事件可以更早地被处理,提升了时效性。

3.2.2 乱序和性能优化-批量更新和乱序纠正

在大流量场景下的测试表明,当前吞吐瓶颈主要受限于窗口聚合时RocksDB 状态后端读写。由于一条数据会抽取多条特征,所以特征窗口累积算子会对 Source 输入数据进行爆炸式扩展,例如当输入数据 QPS 达到 10 万时,特征累积算子的 QPS 可能攀升至数十万甚至上百万,导致大量状态读写请求集中在 RocksDB,使其难以支撑高吞吐需求。

Flink 默认的窗口机制会在每条数据到达时更新累积值,并与状态后端交互,进一步加剧了 RocksDB 的负担。为优化性能,我们将窗口触发和累积调整为微批模式,每次批量更新数据,并引入内存缓存层,微批内优先访问内存缓存,有效减少状态的访问次数。

在百度搜索和点击流量场景下的测试结果显示,该优化方案使内存缓存命中率提升至 90% 以上,意味着特征累积阶段减少了约 90% 的状态后端访问。

同时,在微批数据内部,我们会进行排序,还能有效缓解数据乱序问题,提高计算准确性。如下图所示。

图片

上图:Flink 默认窗口累积机制,**绿色矩形代表窗口的时间范围,窗口中的每一条数据(标记为 1、2 等)都会触发累积操作。图示中展示了 5 条pv的状态后端访问,每条pv都需要与 状态后端(图中黄色区域)进行交互,包括查询、更新、写入等操作。红色圆圈是输出的累积结果,红色边框标记的条目表示乱序数据。上图存在两个问题,第一,对状态后端的频繁随机访问会导致性能瓶颈,尤其是在高并发和大流量场景下。第二,输入数据是乱序的情况下,输出数据也是乱序的。

下图:优化后的窗口累积机制,**优化引入了内存缓存和微批模式。数据小批量更新(如标记为2、1、4为一批、3、5为一批)。每次窗口触发时,首先会对本次微批内的数据进行排序(2,1,4被纠正为1,2,4),然后再累积。累积时,窗口内的累积查询会先访问内存缓存,如果内存miss,再访问状态后端。最终图示中仅有 4 次状态后端交互,较优化前的15次减少11次。数据乱序也得到了缓解。

3.2.3 大流量场景优化-键缩减(粗粒度KeyBy)

窗口聚合过程中累积器需要频繁读写状态后端。此前,我们通过引入缓存层和微批模式大幅减少窗口累积器对状态的访问频次,优化效果显著。然而,在实际应用中,我们发现窗口触发器(Trigger) 也会频繁访问状态后端,带来额外的性能开销。

在实际业务场景中,特征累积的窗口划分通常较细粒度,例如基于ip、query、uid进行 keyBy,且随着业务接入的线索和特征增多,key的数量变多,计算压力进一步加大。这导致两个主要问题:

  • Key 数量激增,触发频繁访问状态:keyBy 后的Key量级极大,每个 Key 维护独立的Trigger,这些Trigger需要不断访问状态后端进行触发注册,造成高频状态交互,影响吞吐。

  • 窗口清理(clear)导致计算压力骤增:当水位(watermark)推进到窗口末端时,大量Key需要同时触发Clear操作,瞬时状态访问量暴增,可能导致作业卡顿甚至崩溃,特别是在窗口长度较长、窗口内Key数量庞大的情况下。

针对上述问题,我们探索了更高效的Trigger机制,以降低状态访问开销,提高作业稳定性。

第一,减少 Trigger 数量:

举个例子,我们基于UID进行特征聚合,如果我们在对特征数据执行keyBy操作时,直接按照最细粒度的UID维度进行分区处理。那么每个唯一的key都会绑定一个触发器(trigger),而触发器的数量直接影响状态访问的频次和资源占用。

为了解决这个问题,我们采用了按UID进行取模分区的方式(例如按uid%100 进行keyBy分区)。这种方式显著减少了触发器的数量,从而降低了状态存储和访问的开销。同时我们定制了聚合函数,保证每个分区内进行聚合计算的时候还是会按照原本的UID作为key进行特征累积,保证特征累积的准确性。

第二,状态放入内存:

进一步优化时,我们发现,当按照固定数量(如100个分区)取模后,key的数量和值是确定且有限的。基于此特性,我们将触发器的状态从Flink的状态后端迁移到内存中管理,这样能够进一步提升性能,避免频繁访问状态存储带来的开销。

有人可能担心:触发器状态迁移到内存后,作业一旦发生重启,内存中的数据会丢失,这可能导致窗口数据无法正常触发。例如,若按UID进行keyBy计算,某个UID仅有一条数据,且此时作业重启导致其触发器状态丢失,那么作业恢复后这条数据永远可能无法下发。

但通过固定分区取模(如按 %100 分区)后,我们有效解决了这个问题:

  • 取模分区的 key 数量是有限的(如 100 个),并且这样能保证每个分区会持续接收到新的数据。

  • 当作业重启时,新数据的到来会自动重新注册触发时间,即便原有内存状态丢失,后续的数据流动能够重新触发正常的处理逻辑。因此,即使触发器状态短暂丢失,取模后的分区会很快自愈,确保数据下发的正确性和完整性。

图片

上图最左边-原始设计,数据流按照 uid 进行 keyBy 分组,每个 uid 都对应一个独立的 trigger。每个trigger需要与状态后端 (StateBackend) 频繁交互,包括保存和更新状态。存在问题是:状态后端需要频繁访问,尤其在高并发场景下,性能瓶颈明显。每个 uid 都维持一个独立的窗口触发器,资源消耗较高。

上图中间-第一版优化,将原始 uid 进行取模操作 (uid % 100),将原本细粒度的分组合并为粗粒度的分组。即多个 uid 合并到同一个分组中,减少了窗口触发器的数量。状态后端的访问频率有所减少,降低资源消耗,提升了整体吞吐量。

上图右边-第二版优化,内存的引入,每个trigger相关的信息存储于内存中,而不是直接与状态后端交互。大幅减少状态后端的访问次数。提升了系统性能,确保作业稳定运行。

综上,在 Flink 反作弊系统的窗口特征累积优化中,我们针对高吞吐、低延迟、抗乱序等业务需求,进行了多项改进。

1.提升时效性:反作弊策略依赖实时特征,默认窗口触发方式无法满足业务需求。因此,我们采用提前触发机制,基于事件时间间隔触发计算,使特征聚合结果能够秒级或分钟级输出,避免长窗口带来的数据滞后问题。

2.优化性能瓶颈:在高并发场景下,特征计算涉及海量状态存储访问,容易导致RocksDB负载过高,影响作业稳定性。我们引入批量更新、内存缓存 、trigger优化、分区缩减等方式,大幅提升吞吐量。

综合优化后,该方案使 Flink 反作弊系统具备更快的特征计算能力、更高的吞吐性,有效支撑高并发业务场景下的实时风控需求。

3.3 配置化

为了满足反作弊策略的高频上线和模拟过滤等需求,我们的实时系统实现了高度配置化。并且配置文件全部托管到风控平台。通过配置化驱的架构,无论是字段抽取、特征加工、策略规则定义和数仓产出,均可以通过简单的配置操作快速完成,极大地缩短了开发周期,同时降低了对底层框架代码开发的依赖。只需要在风控平台上编辑好策略,就可以一键分发并启动对应的测试或线上作业。

图片

如上图所示,相关配置文件可以分为两类分别是工程配置(绿色)和策略配置(黄色),策略配置主要用于定义业务过滤规则和逻辑,工程配置侧重于系统的底层运行,比如输入输出、并行度等配置。并且部分配置文件为非必需项,这意味着如果某个计算模块不需要使用,则相应的配置文件可以省略。

3.3.1 工程配置

工程配置是管理流式作业运行的系统层面参数。针对反作弊场景的实时流式任务,与 Flink CDC YAML的设计思路类似,也是通过 YAML 文件对通用工程配置进行抽象和统一管理,确保流式作业能够灵活适配多种业务场景。

为了保证一个 Flink 流式作业的正常运行,完整的工程配置需要包含以下几个关键部分:输入配置、输出配置、并发配置。

  • 输入配置:决定了 Flink 作业如何接收和解析源数据,定义数据源类型(如Kafka、HDFS)、连接参数、消费策略等。

  • 输出配置:定义了 Flink 作业的计算结果如何存储或传输到下游系统,指定结果存储方式(如ClickHouse表、Redis集群、Kafka Topic)。

  • 并发配置:直接影响 Flink 作业的性能、吞吐量以及资源使用情况,设置算子并行度、检查点间隔等,优化作业性能。

3.3.2 策略配置

策略配置是指将反作弊拦截策略的核心逻辑规范化,以配置文件的形式灵活定义和管理。通过策略配置化设计,能够快速调整或部署反作弊策略,无需修改底层代码。

策略的配置主要由字段抽取配置、特征配置、词表配置、模型配置和规则配置等组成。

字段抽取配置:字段是反作弊策略和数仓的最基础的信息,根据抽取方式不同分为:

  • 基础字段:直接从原始数据流中提取的字段,例如设备 ID、用户 ID 等。

  • 二次计算字段:通过基础字段计算生成的派生字段,设备ID是否合法,UID是否为历史黑用户等。

  • 外部服务字段:通过调用外部服务接口动态获取的字段,例如 IP 地址归属地、安全风控标签等。

  • 维表字段:通过查询词表映射关系获得的字段,例如黑名单匹配结果、分类标签等。

我们将字段抽取逻辑进行了配置化抽象,策略开发人员使用类似于写sql的方式即可完成简单字段的etl逻辑的开发,如常见的json字段抽取,字符串处理,反作弊内部的常用UDF等,配置能覆盖大部分字段抽取,对于复杂的字段抽取逻辑仍旧使用Flink的Datastream API开发实现。

特征配置:特征是策略的重要判定依据,特征配置包括以下几个关键方面:

  • 特征类型:数据的聚合方式,如sum、count、distinct等。

  • 窗口信息:设置聚合特征的时间窗口范围和窗口形式,时间范围如:1 分钟、1 小时等,窗口形式如:滑动窗口、滚动窗口等。

  • 特征维度:特征的聚合维度,如用户、设备、IP 地址等。

词表配置:词表通常是离线挖掘得到的黑名单、字段映射(如ip映射城市)等固定维表信息,配置内容需包括以下几个方面:

  • 词表路径:指定词表的存储位置,支持文件路径或分布式存储地址。

  • 词表类型:支持多种形式的词表,包括集合(set)、键值对映射(kv)、正则表达式(regex)等。

模型配置:通过模型实现复杂的行为预测和风险判定,关键配置内容包括:

  • 模型路径:指定模型的存储位置,支持本地或远程加载。

  • 模型类型:支持多种模型形式,例如线性回归、GBDT等,目前模型的加载是通过PMML框架实现的。

  • 模型输入输出:明确模型所需的输入字段和输出字段等。

规则配置:规则配置决定了作弊行为的最终判定规则和处置方式:

  • 策略判定阈值:定义触发策略的条件,例如基础字段匹配、词表匹配、风险评分的阈值、特征累积阈值、模型打分阈值等。

  • 策略判黑等级:设定风险等级,区分低、中、高风险及对应的处置措施。

如下图所示,规则配置能够获取所有字段信息,并基于这些信息进行最后的策略判定。

图片

这张图展示了反作弊规则的判定流程:

1.输入数据:每条PV包含多个字段**,**包括基础字段(如IP、手机号、UID等)、外部抽取字段(如IP归属地、是否异常等)、计算得到的特征(如统计特征fea1、fea2等)以及模型得分(多个模型计算的分值)。

2.策略判定:系统基于预设的反作弊规则,对各字段、特征、模型分数进行综合评估。例如,规则1要求【fea1 > 100 && model2 > 0.95】,规则2要求 【IP like '192.%' && fea2 > 100 && model1 > 0.65】。多个规则都会执行判定逻辑,判断是否命中。

3.结果输出:最终的PV数据会带上反作弊命中结果。例如,在示例中,该PV数据命中了规则2,表明该行为可能存在风险。

以上就是策略配置的所有介绍,通过配置化管理字段、特征、词表、模型和规则,反作弊系统能够快速响应业务需求,灵活调整检测逻辑。同时,配置化设计大幅降低了开发部署成本,提高了策略迭代效率。

3.4 模拟过滤的实现

通过配置化的支持,可以方便地切换数据输入和输出,所以仅需调整测试配置文件,即可启动模拟过滤作业,提升测试效率。同样的模拟过滤功能也接入了风控平台,开发者可以直接一键调启模拟过滤任务。

对于直接接入实时消息队列进行模拟过滤,基本无需修改输入配置,然而,基于实时消息队列的模拟过滤存在一定局限性,首先运行耗时较长,需要1:1的时间来进行测试。如过滤24小时数据需24小时,难以快速验证策略长期效果,其次消息队列仅保存最近几天的数据,历史数据回溯能力有限。一般仅适用于简单工程测试和策略模拟过滤。

为此,我们扩展了 Flink 的 HDFS Source 组件,支持直接读取HDFS上的 Parquet 文件进行模拟过滤。在读取 Parquet 文件时,因为是流式计算,主要挑战在于数据顺序问题,为确保数据时序一致性,我们采取了以下优化策略:

  • 文件级别排序:在读取数据时,按照文件路径和名称进行排序,确保数据按时间顺序加载。

  • 顺序读取文件:严格按照排序后的顺序依次读取 Parquet 文件,避免乱序问题,保证数据的时序性。

通过这一优化方案,我们在策略模拟过滤中与线上对比测试,准确率达到 99% 左右,大幅提升了模拟过滤的可靠性和一致性。

图片

除了Source组件适配了读取离线数据外,其他组件跟线上完全一致,这样就保证了模拟过滤的准确性。极端场景下(如线上作业出错需重新回溯数据),可通过此方式对离线数据再次模拟流式过滤,实现数据修正。

3.5 便捷的数据分析

在实时反作弊系统中,数据分析不仅是风控团队优化策略的核心工具,也是业务方监控风险、评估影响的重要支撑。为了满足不同角色的分析需求,系统提供了离线和实时的数仓产出,帮助进行便捷的数据分析。

图片

上图展示了我们的数仓方案,其中 Flink 负责实时数据流的计算和处理,最终将数据分别存储到 HDFS(Parquet 格式) 和 ClickHouse。

  • 离线Hive表:Flink 将数据以 Parquet 格式 存储到 HDFS,支持按刻钟或小时级别分区产出数据,并挂载到图灵表中,便于后续使用 Hive、Spark 进行批量计算和查询。也可以作为后续回溯和测试模拟过滤的直接输入。

  • 实时ClickHouse表:支持实时数据产出,用于高性能的 OLAP 分析,可用于快速分析策略上线效果、构建实时看板以及告警监控等场景。

这种架构能够同时满足 实时查询 和 离线存储分析 需求,实现高效的数据流式处理与存储。

高效便捷的数据分析主要满足了如下需求:

  • 实时监控与告警:业务方需实时了解反作弊策略的执行效果(如拦截量趋势、高风险用户分布)。

  • 基于 ClickHouse 的秒级查询能力,支持实时生成监控看板(如“近1小时拦截量TOP 10 IP”)。

  • 配置告警规则(如“拦截量突增50%”),通过邮件或消息推送及时通知相关人员。

  • 自助分析与可视化:业务方能够灵活分析作弊行为特征(如羊毛党设备型号分布、异常行为时间规律)。

  • 接入 TDA自助分析平台,支持 SQL 查询与拖拽式可视化分析,无需依赖数据团队。预置常用分析仪表盘,降低使用门槛。

  • 离线挖掘与模型优化:数据同学能够于历史数据挖掘作弊模式,优化策略规则与机器学习模型。

  • 将全量数据存储至 Hive,支持 Spark、Flink 等分布式计算引擎进行进一步的复杂分析。

通过便捷的数据分析能力,系统不仅提升了风控策略的迭代效率,还赋能业务方自主探索数据价值,实现从“被动响应”到“主动洞察”的转变。

04 总结

本文介绍了基于 Flink 的实时反作弊流式过滤系统,围绕架构设计、挑战应对及优化方案展开。通过特征计算和配置化管理,提升了系统的检测效率和稳定性。实践表明,该方案在提升数据处理时效性与反作弊效果方面均取得显著成效。未来,将进一步优化策略检测机制,提升检测精准度,并探索更智能的风险识别手段。

------END-----

推荐阅读

百度智能云xDeepSeek,最具性价比的DeepSeek一体机合集来了!

图引擎在智能体开发场景的应用实践

直播间互动框架性能优化与稳定性实践

百度网盘防雪崩架构实践

如何在百度百舸部署满血版DeepSeek-V3、DeepSeek-R1模型

图引擎在智能体开发场景的应用实践

作者 百度Geek说
2025年2月25日 10:06

导读

随着AGI理论的不断突破,智能体已经成为LLM在企业落地的最重要的形式之一。一个完备的智能体必须能实现:感知、推理、计划、执行等一套完整的功能,从工程的角度来看workflow特别适合这种复杂任务的分析、拆解、重组、执行,  再结合CoT技术, 实现LLM和业务功能完美契合的智能体应用。本文尝试用成熟的图引擎技术驱动workflow探索更多样性的拓展agent能力的方法,以更好应对各类业务场景。

01 简介

1.1 什么是智能体

以大模型(LLM)为核心, 具备以下特性的智能化系统:

  • 交互性: 通过文字,语音,图像等多种交互方式来理解用户的持续性需求  (感知);

  • 适应性: 感知环境的变化持续进化,以更好地完成任务和适应复杂环境      (记忆);

  • 自主性: 能够自主学习,主动思考和决策                                               (推理);

图片

图片

1.2 业务形态、流程

一个智能体生态平台,用户可以在上面体验功能各异的智能体app,同时也能让用户将自己的优秀想法以极低的成本(通过快速组装已有的插件、workflow、知识库、记忆) 快速实现成新的agent。

图片

图片

图片

系统特色:

  • 流程编排能力:支持可视化的数据流加工,通过编辑各个处理节点将原始input加工成output;

  • 功能复用能力:众多的agent库、插件库都可直接复用到自己的智能体里, 可插拔、替换;

  • 低代码能力:无需大量写代码,直接通过拖拽元素就能拼装出想要的功能。

图片

图片

1.3 业务场景的需求难点

1.3.1 能自由组装流程实现人机无缝衔接、数据解耦

  • 能将人的需求表达和agent思考结果的进行完美串联融合,发挥各自优点;

  • 除context外更多样性的数据传递方式,更好满足workflow、cot等流程编排的场景;

  • 细粒度控制数据传递、适配方式,满足特定场景的灵活性和性能的平衡需求。

1.3.2 能更精细规划路径、简化流程设计

  • 支持多种路径控制能力,满足多样性的静态化任务编排;

  • 支持在workflow内部动态编排新的子flow, 满足动态化的场景。

1.3.3 能对流程进行统一的控制、干预

  • 流程运行过程中当出现超时、异常等非预期情况需要框架能提供快速干预、退出机制;

  • 摆脱对executor(执行器的依赖),更低成本支持大量功能异构的流程。

1.3.4 能进行简单的功能注入

  • 支持在模型前后、工具调用前后等地方注入策略逻辑和观测代码,避免对大量节点进行浸入式改造;

  • 支持流程编排时给节点初始化赋值,降低数据传递的成本;

  • 支持任意节点信息的流式输出能力,满足长流程中阶段性结果的sse输出需求。

1.3.5 能支持缺少代码能力的使用场景

  • 将用户生或者LLM产生的cot转化成具体流程配置;

  • 将流程配置转换成可运行的代码。

1.4 为什么自研图引擎

1.4.1 常用智能体开发框架简介

  • LangChain框架:一个开发智能体的框架,定义了prompts,  index, memory, agents, tools, outputParser等一系列功能抽象,通过chains将各个功能串联成应用。

  • 开发模式:

  • Chains:  规划静态任务,  很多抽象都实现了chains的接口,规划好路径就能让各功能有序执行

  • AgentExecutor:  执行动态任务,某些场景无法预知执行路径,需要不同的输入走不同的分支,因此引入代理人(AgentExecutor), 通过多轮循环推理产生最终结果

  • 总结:多轮学习和推理是自主ai系统的基本的能力, Chains不具备循环”能力,  AgentExecutor多轮调度是一个不透明度黑盒。

图片

图片

  • LangGraph:基于LangChain基础上演化的框架,引入条件边,赋予用户对循环的控制能力。

  • 开发模式:用透明化的有向状态图打破LangChain动态任务的循环黑盒 (AgentExecutor)

图片

图片

已有框架比较注重系统的自主性,对业务执行路径的编排能力较弱。

1.4.2 业务需求的挑战

  • 强化的路径控制能力:既能满足llm的多轮循环特性, 又能结合cot模式的功能编排;

  • 传统功能的结合能力:模型存在知识能力边界,业务需要结合之前传统功能来满足多样性、个性化的需求, 在数据的校验、传递、并发、同步,流程控制等这些贯穿整个业务随处可见的基建功能都需要支持。

这些都不是已有智能体开发框架本身所擅长的,从下层视角看需要提供一个更通用、更精细化控制能力的流程驱动框架增强以下特性来满足业务需求。

02 用seda图引擎驱动workflow的模式开发智能体

对于以上共性需求我们引入图引擎驱动workflow的开发模式:将任务拆解成独立的功能节点,不仅可以包含 LLM, prompts, memory, tools, 等智能体(ai)元素还融入了 分支、循环、条件、多路复用、数据传递等传统应用所擅长的路径编排、数据转发、超时控制、错误处理等方面的功能,为智能体应用提供一个更强大、稳定、便于解耦且可黏合性强的基座环境。

图片

图片

2.1 实现更灵活的流程拆分组装、数据解耦

2.1.1 流程拆分

元素:

算子:  若干个函数的集合,组成一个业务逻辑上可简单描述的功能模块;

边:  联接2个算子,具有方向性,代表执行顺序,起始节点作为上游算子,指向节点作为下游算子;

联接:

串联: 一个上游算子联接一个下游算子,上下游顺序执行;

并联: 一个上游算子联接多个下游算子,上下游顺序执行,下游算子之间并行执行;

join:   多个上游算子联接一个下游算子,上游之间并行执行,下游等待所有上游执行完后再执行,实现汇聚、归并。

flow:

将复杂业务功能分解成若干个易实现、解耦、可复用的算子,根据业务执行顺序用边将算子串联起来,形成工作流。

用户请求从起始算子流入,通过flow的算子进行加工,最终从结束算子输出,完成整个业务流程。同时支持 dag 和 dcg(有环图)。

图片

2.1.2 分层组装

  • 子图(sub-flow):把一个flow也当成一个算子(sub-flow),直接挂到另一flow上,当数据推动到这个算子的时候就会触发这个(sub-flow)的内部逻辑,实现flow级别的抽象和复用;

  • 分层脉络地图:功能复杂导致算子数量过多时可以按不同粒度划分层级,把相同抽象粒度的算子放到同一个层级,不同层级的算子通过子图实现串联。通过点击进入子图内部浏览功能细节,通过收拢子图回到上层抽象,以此实现功能导航地图,既方便浏览又方便解耦拆分到多人协作开发。

图片

2.1.3 数据解耦

context共享可以方便的在所有算子间传递数据,算子的插拔替换、顺序调整也无需考虑数据的匹配,但同时也带来了数据加工处理过程黑盒,以及为了尽量避免参数读、写范围放大而需要加强数据访问权限保护这2个问题。

为此我们提供了额外的方式,满足不同场景的需求。

  • 链式推导 -- 更细粒度数据解耦

描述:上游output和下游input类型一致,就能串联并传递数据。

优势:output/intput是数据传播的契约约束,数据加工流程清晰。相同intput, output定义的算子之间能串联且插拔替换;相互之间不匹配的算子能通过定义中间类型的"桥接"算子来实现串联。

短板:需要用户手工串联算子,而且算子间容易产生串联类型不匹配以及并发问题也需要用户自己解决。

  • 自动推导 -- 自动优化使用和运行效率

描述:算子数量越多人工编排效率越低,在链式推导的方式上引入Referential Transparency, Single Assignment等规则约束来实现调用链自动推导。

优势:可以帮助用户自动管理大量算子的联接关系, 省时省力的同时还能在保证数据并发安全性的同时实现最大程度的并行化。

短板:关注的焦点从过程转移到数据,用户需要对流程做设计、优化时需要从数据入手做逆向推导流程(数据是在流程中慢慢产生和迭代的),违背了用户的使用习惯。而且对现有系统来说这种对数据读写的严格拆分、解耦往往需要全部推翻重来。

图片

2.1.4 拷贝与引用

除了以上的数据传递方式外在不同的需求场景下我们同时还需要思考是传递数据的副本拷贝还是传递数据引用(本身)的问题

  • context共享:出于性能考虑,基本都是采用传递引用的方式;

  • 链式推导:可以手工指定,默认通过简单的判断下游算子的数量来自动决定:下游算子个数等于1:传引用,大于1: 第一个算子传引用,剩余算子传副本,尽可能在局部范围内降低用户心智负担;

  • 自动推导:在Referential Transparency, Single Assignment双重数据访问规则保护下无需用户考虑并发安全性问题;

  • 自定义:用户可以在联接2个算子之间的边(edge)上显示指定是副本拷贝还是传递数据引用(本身)来获取更大的灵活度。

图片

2.1.5 类型适配

同一类功往往有多种实现,各自针对不同的使用场景有自己的优化,这些算子的input/output 往往基本结构一致,但又稍稍有一些小差异,为了更方便让这些算子能快速插拔,提高功能复用度,降低复用成本,我们提供了一系列自动适配机制。

  • 类型自动放大:内置多种数据类型及其变体自动转换逻辑:

A  <--->  *A,  interface{}

A  -> []A, []*A, []interface{}

  • 特型自动匹配:上游算子的output类型是被下游算子input类型内部组合的子类型,相互能串联,并完成自动赋值。

  • 自定义适配器:一个单独的适配器完成2个功能类同,但是intput/output参数形式差异较大的算子之间串联,适配器的input是上游算子的output, 其output是下游算子的input。

  • 为什么不直接用过度算子实现:避免大量转换算子干扰业务流程;

  • 为什么不把适配逻辑放算子内:避免侵入式改造和过度引用导致耦合。

图片

2.2 实现精细化的路径规划

2.2.1 多功能边

  • if:                    条件边 (单分支);

  • switch:               多条件边 (多分支);

  • multiplex:           多路复用边 (同时监听多个资源、信号);

  • optional edges:  可选边 (按需放弃对非强依赖上游的等待);

  • while:                 循环边 (条件和循环次数组合实现dcg);

图片

2.2.2 动态规划

编译时构建一个基础的大致性的功能脉络(上层基础逻辑定义),具体实现逻辑由执行期根据代码运行情况及LLM返回结果动态规划做逻辑的实时增删、拼接、构建。

图片

2.3 实现统一的驱动控制和干预

2.3.1 流程驱动

  • executor 执行器:

  • 存算分离设计:执行器读取flow信息并驱动执行,提供路径控制、资源调配,flow仅存储数据;

  • 一次解析多次执行:对flow进行一次解析后可以得到该flow的一个excutor, 后续相同的flow都可以通过该executor进行执行;

  • 适用场景:系统里flow异构类型有限, 都需要大量重复的被执行, 能节省每次构建重复flow的开销。

  • flow自驱动:

  • 无需解析,直接执行:flow被构建出来后,利用flow自驱动的功能直接运行, 无需构建执行器;

  • 适用场景:系统里大量异构flow,或者会动态产生出新构型的flow,能节省复用度低的执行器构建的开销。

图片

2.3.2 错误处理

  • 退出流程

当算子返回值 <0 时表示发生错误,默认直接跳到最后一个算子执行(让系统能有一个给用户回包、打日志的机会) 后并退出当前流程,快速终止无效的执行逻辑,迅速释放资源。

  • 逐层退出

当有多层(子图)时候,每一层有算子返回值 <0都会直接跳转到本层子图的最后一个算子执行,随即返回回上一层。用户可以按需给上层指定返回值,当给上层的返回值 >=0时候上游正常往后面算子执行,否则继续跳到本层的最后一个算子执行,以此类推直到整个flow结束。

图片

2.3.3 超时控制

  • 图超时

我们可以给整个flow指定一个执行的超时时间,触发后直接跳入到本层最后一个算子(让系统能有一个给用户回包、打日志的机会),这样快速终止无效的执行逻辑,迅速释放资源。

  • 算子超时

一般没有特殊需求我们建议根据业务接口要求通过flow控制整体超时即可,由系统自行判断可以尽量避免一些超时设置不合理的问题,同时我们也保留单独指定算子超时的能力,算子发生超时但flow整体没有超时时会跳过超时算子继续往下执行;,尽可能保持必要的灵活性。

图片

2.4 实现通用注入

2.4.1 事件监听

  • 监听通用事件

提供以aop接口的形式统一支持算子 on_enter, on_leave, on_timeout, on_error等关键事件的钩子机制,以全局函数或者算子成员函数的形式进行改写,以便用户能统一加入自定义的日志记录、监控、通知、错误处理等应对机制。

  • 监听自定义事件

除了上面的通用事件,我们还提供统一的机制,提供用户在算子内部任意地方加入自定义事件的能力,帮用户简便地完成应用层框架监听机制的建设需求。

图片

2.4.2 附加属性

除了context共享,链式推导的传递机制外我们还提供了额外第三种"附加属性"的方式来给算子传递数据,方便用户在编辑算子时就能给算子指定一些固定属性值,算子运行时能快速被读取,降低初始化成本。

图片

2.4.3 流式输出

放置一个内置的流式输出的算子到workflow里,通过向这个算子的channel里写入数据即可实现在任意算子里流式输出信息来满足sse需求,同时将流式输出算子和最后一个算子相连,即可实现优雅退出。

图片

2.5 实现低代码

  • 提供可视化编辑器,让用户拖拽设计流程并产生对应的配置文件;

  • 后端提供算子仓库作为用户功能实现的基础素材;

  • 图引擎的generate负责将配置翻译成流程代码, builder动态构建流程,driver负责驱动流程运行并返回结果。

图片

2.6 seda相较其他图引擎实现的优势

2.6.1 图引擎实现方案一:多线程并发(thread-base-on-request)

本质:线程数和请求数N:M的模型,基于请求数量规划线程的设计, 由操作系统保证线程调度、资源分配的均衡。

优势:实现简单、数据局部性好,负载在系统处理能力阈值内性能及佳,适用于对耗时要求苛刻的场景。

短板:

  • 流程黑盒:线程粒度太粗(请求粒度),不利于功能迭代、优化。

  • 扩展性差:请求数受系统线程数制约, 负载超出系统处理能力阈值会使系统陷入“调度内耗” (上下文切换,锁竞争),处理能力指数级下降。

图片

2.6.2 图引擎实现方案二:事件驱动并发(thread-base-on-resource)

本质:资源数和线程数N:M的模型, scheduler根据系统资源初始化若干线程,将请求拆解成由若干个non-blocking节点组成的有限状态机(FSM),节点执行后将状态回传给scheduler, 由其根据当时资源使用情况分配下一个节点的处理线程,直到整个有限状态机结束。

优势:

  • 流程可视:通过有线状态机实现各个功能节点之间互相解耦;

  • 扩展性佳:线程数不受请求数影响,能始终控制在系统资源可高效运行的阈值范围内;

  • 吞吐量大:事件驱动极大程度避免了多线程之间的"资源内耗",能有效提升系统并发和吞图。

短板:

  • 时延增大:一次请求处理过程跨多个线程执行增加了数据传递消耗的同时也降低了硬件缓存命中率导致请求延迟增大;

  • 实现困难:中心化的scheduler调度器既要驱动业务状态流转又要管理资源调度往往会顾此失彼。

图片

2.6.3 图引擎实现方案三:基于seda的图引擎驱动(thread-base-on-stage)

seda:staged event driven architecture

本质:将上面中心化的scheduler模块给拆分成多个子部件实现。

  • 用多个事件队列将每个状态节点(stage), 组成一张数据流动网络 (Directed Graph);

  • 每个stage都由事件队列(接收数据)、控制器(分配资源,驱动stage流转)、线程池(调节线程数)、事件处理器(业务处理handler)组成;

优势:

  • 资源按流程stage拆分,粒度适当 (按request粒度过小,容易陷入内耗;按resource粒度过大,容易浪费);

  • 去除中心化模块,通过对事件队列的流速控制使得每个stage可以单独进行负载调节。

短板:

相比运行状态良好情况下的单线程处理一个请求的设计, 时延上会有增大 。

图片

03 通用图引擎在智能体场景的实际应用

3.1 功能场景应用

3.1.1 根据大模型COT结果动态生成子workflow

  • query:请预测下明年下周的天气情况

  • 大模型将问题拆解成具备先后依赖关系的多个小步骤:

  1. 计算下一周对应的时间范围

  2. 查询本周天气情况

  3. 查询历史上前n年对应时间范围的的天气情况,

  4. 根据历史查询结合当前情况推测明年对应的时间范围的天气情况(结果保存到短期记忆)

  5. 如样本范围太小或结果单一则重复前面过程3-4,直到给出预测结果的具体概率分布

  • 大模型根据当前执行到的具体步骤将工作内容动态分解到子图并执行。

图片

3.1.2 复杂场景的功能拆分解耦和精细化路径控制能力

  • 用"多路复用"同时监听多值,支持任意数量路径分发,将 "路由和子功能调用" 算子进行拆分解耦;

  • 用"可选边"将多处可能会触发到的公共逻辑"润色模型"模块拆分成独立算子;

  • 用过"融合边"将各种不同类型的边融合汇聚到一个算子, 便于整体控制流程结束逻辑;

  • 通过以上多种精细化路径控制功能,减少大量胶水代码的同时方便对流程图做快速修改,让用户专注于业务逻辑自身。

图片

3.1.3 通用注入和循环增强

  • 由侵入式改造转变成通用事件注入来统一控制算子内部的共性行为;

  • 个性化功能增强也可以通过非侵入式方式注入算子内部;

  • 在之前纯代码逻辑控制循环结束条件的同时增加了框架保护机制,避免响应不及时和资源长时间侵占。

图片

3.2 小结

通过图引擎驱动workflow的开发模式提供了一个强大的基座,用户可以快速在其上通过插拔替换、顺序调整、串联汇聚、编辑出任意自己想要的流程,其强大的解耦和精细化路径控制能力从根本上解决传统ai开发模式带来的黑盒问题和相关不确定性问题,同时还能获得极佳的运行时效率(天然并发);其自带的低代码、分层导航等特性减少了大量胶水代码,还有助于多人同时入场并行开发,降低开发、维护成本。

目前系统已经接入80w开发者,15w合作企业,超过10w个智能体。

————END————

推荐阅读

直播间互动框架性能优化与稳定性实践

百度网盘防雪崩架构实践

如何在百度百舸部署满血版DeepSeek-V3、DeepSeek-R1模型

首日调用客户破1.5万!DeepSeek-V3/R1上线背后的超低推理成本技术揭秘

唤醒 AI 算力,专有云 ABC Stack 面向企业级智算平台的 GPU 提效实践

直播间互动框架性能优化与稳定性实践

作者 百度Geek说
2025年2月20日 10:29

导读

直播间互动体验框架技术实践,揭秘性能与稳定性优化之道,快来探索吧!在百度直播间歌会红包等活动中,我们创新性地将红包互动与高质内容深度融合,通过技术架构升级与系统性优化,打造了"音乐+红包"(边听歌边抢红包)的沉浸式体验。本次实践显著提升了直播间的并发承载能力、实时互动响应速度和用户参与满意度,同时沉淀出可复用的技术方案,为后续大型直播活动奠定坚实基础。

01 百度直播间歌会红包运营活动介绍

为提升直播内容质量和用户粘性,需注入多元化内容,增强直播间多样性和观赏性。同时,通过活动裂变扩大影响力,吸引特定用户群体,保持用户新鲜感和期待感,为平台长期发展奠定基础。

为落实直播歌会目标要求,需加快直播间互动体验框架建设,探索新型混合模式和沉淀通用能力,着力适配重点业务场景,打造"音乐+红包"的互动体验,提升直播间品质:

一是通用基础。主要包括组件复用、大图压缩等减少产物体积,页面异常、性能、白屏监控,BFF服务编排扩缩、稳定性监控等。

二是访问保障。主要包括页面多域名容灾、开启强缓存;字体、图片、CSS、JS等静态文件单独CDN强缓存域名,开启多级缓存等。

三是红包性能。主要包括页面预静态化、数据预加载、文档预取、资源预取、视图预渲染、动效降级等。

四是开发体验。主要基于直播前端一站式,建强队伍,确保项目开发流程规范统一,搭建增质增效的研发环境等。

图片

02 体积

2.1 页面划分

在大型产品需求中,通过合理的页面划分策略,实现高效开发与维护。面对产品需求中罗列的多样玩法功能和19种以上的红包状态,研发团队面临的首要挑战是如何将这些功能合理的拆解成多个页面承载。合理的页面划分不仅关乎用户体验的流畅性,更是减小产物体积、提升跨页面资源缓存利用率的关键。通过深入分析业务逻辑与用户行为路径,我们精心设计了页面边界,确保每个页面和组件都承载着唯一元素,同时避免了冗余代码的产生。此外,这一策略还极大地促进了开发团队的协作效率,明确的页面划分减少了代码冲突的可能性,使得团队成员可以高效并行集成,从而加速了开发迭代周期。在直播间端能力的规范化构建上,同样遵循了通用化这一原则。

在页面划分时,我们非常注重跨页面资源的最优利用,通过策略性地缓存HTML、CSS和JavaScript等资源,确保一旦用户在任意时刻首次触发了红包弹出事件,这些资源即可被全面缓存,使用户在后续的页面切换过程中无需再次加载这些核心资源。

通过一系列设计举措,划分多页应用(MPA)10+个、单页应用(SPA)20+个、红包组件状态(Component)19+个、规范化直播间端能力(Scheme)30+个,每一项都经过精心设计,共同作用于提升应用的整体性能,为用户带来更加轻盈、快速且协同良好的使用体验。

2.2 性能优化

在直播歌会抢红包运营活动中,Web页占据了80%的比重,对于每一个依赖较多网络资源的玩法页面,在直播间中实现即时加载和快速展现确实面临较大挑战,尤其是在高并发、低延迟的场景下。

图片

△页面展现过程

为了有效应对这些挑战,通过深入分析页面展现过程中的各个环节,直播间互动框架提炼出七种通用的优化方案。旨在提升用户交互体验、增强系统的整体性能。并确保直播间玩法在高并发场景下依然能够流畅运行。这些优化方案覆盖了从页面加载、资源获取到实时交互的各个方面,形成了一个全方位的性能提升样板,具体方案如下:

图片

2.3 页面预静态化(SSG)

在直播歌会抢红包的场景中,所有不变的内容(如活动规则、活动主页框架等)使用SSG能够显著提高页面通用静态内容的加载速度,同时通过集成CSR可以实现部分动态内容的及时更新。

图片

△框架原生SSG Webpack插件

图片

△图1:活动规则 △图2:攒百元半屏页 △图3:支线攒碎片

2.4 页面静态化(SSR)

在直播歌会抢红包的场景中,节目单页作为用户获取歌曲节目信息的第一入口,其快速加载至关重要。SSR提供快速的节目单页初始加载,后续通过客户端的JavaScript动态增强功能(如进度提醒、节目回放等)获得更丰富的交互体验。

图片

2.5 增量静态渲染(ISR)

在直播歌会抢红包的场景中,对于实时性要求极高的红包抢夺场景,ISR的动态更新和实时交互特性为活动的各个环节提供了实时回显的用户体验:

  • 首先,在全屏红包弹窗页(如初始红包、任务红包和裂变红包)中,ISR使得页面无需刷新即可实时更新用户的红包状态。当用户参与活动或完成任务时,ISR的快速响应确保用户能即时获得任务状态和奖励领取情况,增强了用户的参与感与互动性。

  • 对于实时轮次切换功能,ISR保障用户迅速在游戏阶段间切换,提升了同页面不同状态的连续性。当活动结束时,系统能够快速通知用户并更新活动状态为下线,避免误导用户继续参与。

  • 在内容分享与社交互动方面,ISR处理高效的页面加载与实时显示,如微信邀请和海报分享,保证用户能快速刷新助力进度。在邀请分享页,主态用户能立即看到受邀好友的参与情况和贡献,进一步增强社交互动体验。

图片

2.6 数据预取(Prefetch Data)

在直播歌会抢红包的场景中,通过NA与H5之间的有效数据预取和缓存衔接,实现了端数据的复用,有效减少与服务器的交互频率,消除了数据加载的等待时间,确保了在直播环境中用户能够高效参与活动:

  • 预取皮肤元素配置,进入直播间后,NA会预取皮肤元素配置,预加载与活动相关的皮肤素材,并将这些信息进行缓存,包括页面主题色和红包动画等。同时,前端JavaScript能够在页面弹出时,通过端能力或全局变量直接获取相关数据,用户不需要等待皮肤配置加载,界面视觉能够立即呈现,从而实现在操作上的流畅体验。

  • 攒百元红包的进度更新,在活动进行中,用户需要实时查看攒百元红包的进度,通过数据预取的方式,能够迅速更新至用户界面。在启动WebView的同时,NA实现数据的并行获取。这意味着在用户点击挂件后,相关的数据请求会立即开始,前端JavaScript则能够在执行时通过端能力直接获取这些已经预取的数据,降低了数据延迟加载等待时间,增强了用户参与活动的效率。

图片

2.7 文档预取(Prefetch HTML)

在互动性较强的直播歌会抢红包的场景中,用户不仅可以观看演出,还能参与抢红包活动。为提供最佳的用户体验,确保用户在首次点击不同功能时能够快速上屏相关内容,采用文档预取能力在后台主动下载歌会相关HTML内容,如攒百元半屏页、节目单页等。当用户最终点击某个链接时,直接从内存中读取HTML文档内容,无需网络请求,从而显著提高页面加载速度,确保用户在直播间里的互动预期。

通过数据结果来看,文档预取的效果非常显著。在优化了节目单页的性能后,Android用户的首屏加载时间从3秒级减少到500毫秒级,iOS用户的首屏加载时间从2.5秒级减少到500毫秒级。这样的性能提升显然改善了用户体验,使得用户能够快速获取所需信息,进而积极参与到活动中,营造出活跃的直播间氛围。

图片

△Prefetch SSR/CSR HTML

2.8 资源预取(Prefetch Resource)

在直播歌会的场景中,用户参与抢红包是一个关键的互动环节。在此过程中,确保红包弹出的多层动画和红包图能够迅速、完整地展示对于增强用户体验至关重要。为此,资源预取在这一场景中得到了有效应用,在正式直播活动开始前期,后台服务主动下载、缓存、更新关键资源,包括红包的动画文件和高质量的红包皮图像。以确保当红包正式弹出时,最新的文件已被准备妥当,用户能够立即看到完整的红包图和流畅的动画效果,避免了图片逐块加载造成的卡顿和不完整展示。

通过数据结果来看,资源预取的效果非常显著。Android用户资源加载耗时提升幅度约46.7%,iOS用户资源加载耗时提升幅度达86.1%,大幅提升了整体互动体验,使用户在关键时刻享受到快速且流畅的操作体验。

为了确保资源预取的有效性,需要注意以下几点:

  • 预取的资源应以用户行为的合理预测为基础,避免过度预取,从而造成带宽浪费。

  • 采用分模块的离线包设计,将每个模块的资源单独管理。

  • 在活动结束后,应及时下线不再需要的资源,释放带宽和用户手机空间。

图片

2.9 视图预渲染(Prerender WebView)

在直播歌会的场景中,观众们期待快速响应的抢红包互动体验,此时视图预渲染能力发挥了重要作用。当用户进入直播间后,提前在后台加载并渲染抢红包页面内容,并注册页面可见性监听器。即使用户专注于观看直播,红包页面也已准备妥当。用户点击按钮,抢红包页面便迅速显示,无需等待加载和渲染时间,同时触发监听器实时更新数据。这样的即时反馈使得用户几乎可以瞬间查看抢红包的结果,极大提升了参与的积极性和体验感,进一步增强了直播的互动乐趣。

在预渲染过程中,仅对用户频繁访问的页面进行预渲染,避免资源浪费,确保当前视图性能不受影响。由于预渲染占用内存资源,因此需要控制WebView的数量,防止内存泄漏。在实施时应关注内存管理、时机选择、兼容性和安全性,以灵活适应具体应用场景。

图片

图片

03 稳定性

3.1 弹窗稳定性

保障直播间红包弹层的进退场稳定性,防止透明弹层卡住以致用户无法互动,是一项关键挑战。在直播间中,红包弹层通过覆盖全屏透明WebView实现,且与动画控制密切相关,用户在拆红包动画播放过程中无法进行任何交互,关闭按钮在动画结束后才会显示。这要求我们确保红包动画的持续时间和效果稳定,以便在合适的时机正确显示关闭按钮。为确保红包弹窗正常退出,尤其是在H5页面渲染异常或网络不稳定的情况下,用户也能得到一个状态友好的反馈。保障直播间抢红包互动的稳定性,我们设计了“一次握手”和“双重兜底”策略:

  • 一次握手,即Web内容健康检查:

  • JavaScript通过WebContentLoaded端能力,表示H5成功接管用户交互,并通知Native端取消WebView的超时销毁策略,以确保全屏红包弹窗能够稳定展示。

  • 如果H5接管未在规定时间内完成,Native端将销毁上层全屏透明的WebView。这一措施确保用户不会因弹窗问题而中断观看体验,从而能够持续与直播间进行交互。

  • 双重兜底,即NA兜底页和H5兜底页:

  • NA兜底页,当HTML入口文件请求异常时,展示Native兜底页面,确保用户有可见的替代内容。

  • H5兜底页,在JS业务组件发生异常(例如接口请求异常、端能力调用失败、组件内部异常、重要资源缺失)时,展示H5兜底内容,为用户提供实质反馈。

图片

图片   

△图1:NA兜底页 △图2:H5__兜底页 △图3:请求______兜底

3.2 动效降级

炫酷的动效的表现直接影响用户的体验,在直播歌会场景中,红包动效由复杂的元素组成,包括Lottie动画、AFX透明视频和CSS动画。炫酷的动效虽然可以增强视觉吸引力,但在低端手机上可能导致卡顿。为确保所有用户可以顺畅参与活动,我们实施了分级动效降级策略:

  • 高性能设备(High):在高性能设备上,展示完整的动画和丰富的动态效果,享受到丰富的视觉效果。

  • 低性能设备(Low):对于低端手机,复杂的动效将被简化为静态图像或低复杂度的CSS动画。例如,红包拆开时只展示基本的静态图形,替代激烈的动态效果,确保用户能够正常阅读红包金额,而不至于因动效卡顿而影响体验。

分级动效降级策略能够根据当前手机的实时性能情况,在用户点击拆红包时动态调整展示的动效级别,确保以最佳效果参与活。这种适应性有效地解决了不同设备用户在参与红包活动时可能遇到的性能问题,从而提升整体用户体验的品质。

图片

3.3 组件响应

随着用户体验的不断优化,直播歌会抢红包活动中页面组件的运行环境日益复杂。特别是在复杂组件的开发中,组件开发者必须意识到各项适配工作的必要性,以确保用户体验与开发体验之间的平衡。为了有效满足用户需求并提升开发效率,我们需要综合考虑多个环境及其不同状态。至此,在一个组件的设计和实现过程中,需要针对以下五种状态进行响应和适配:

  1. SSG环境(编译线环境):组件在编译过程中,通过Node.js将公共的信息(如活动规则)提前生成静态内容,以提供快速响应时间。

  2. SSR环境(服务端环境):组件服务器集群上,通过Node.js根据用户请求动态生成相应的内容(如歌会节目单),减去客户端JavaScript加载执行时间,加快页面首屏展示速度。

  3. ISR环境(客户端环境):组件在浏览器中,通过JavaScript进行动态渲染、响应用户点击、滑动等操作,通过异步接口获取最新数据(如红包金额、助力信息)并即时更新界面,保证用户体验的实时性和互动性。

  4. 页面可见(Visibility):组件在浏览器中,通过JavaScript控制组件的渲染时机,仅在内容需要展示时才进行渲染(如播放红包动画),减少不必要的DOM操作,提升性能并降低资源消耗。

  5. 动效级别(High / Low):组件在浏览器中,通过Native端能力获取用户设备的性能,动态调整组件中的动效,在高性能设备上展示更炫酷的动效,在低性能设备上则展示更简单的动效,确保流畅体验。

04 总结

  • 沉淀直播框架能力:通过优化直播间视图容器组件,并形成标准化的组合能力样板,拉升直播间活动页面的性能水准,这些方案具备良好复用性,适用于未来各种直播活动。

  • 系统稳定性保障:组件复用、性能监控和容错机制,减少重复开发和维护成本,进行压力测试与优化,提升系统可靠性和用户体验,确保高峰流量下的稳定性。

  • 强化互动性体验:在直播歌会中建立综合能力框架,特别是在抢红包等互动性强的活动中,确保用户在享受歌会演出的同时体验流畅的互动,鼓励积极参与

————END————

推荐阅读

百度网盘防雪崩架构实践

如何在百度百舸部署满血版DeepSeek-V3、DeepSeek-R1模型

首日调用客户破1.5万!DeepSeek-V3/R1上线背后的超低推理成本技术揭秘

唤醒 AI 算力,专有云 ABC Stack 面向企业级智算平台的 GPU 提效实践

百度APP iOS端磁盘优化实践(上)

百度网盘防雪崩架构实践

作者 百度Geek说
2025年2月18日 10:33

导读

大模型在研发效能领域代码生成方面发挥了越来越大的作用

而大模型的预训练依赖大量的精标代码,这些精标数据必须是比较好的工程实践代码

这些比较好的工程实践代码,需要大量的技术沉淀,包括工程架构,代码架构等多纬度,涉及性能、可用性、扩展性、安全等方向

百度网盘有不少比较好的工程实践,本文主要是介绍百度网盘工程架构中的防雪崩架构

抛砖引玉,与大家一起探讨什么才是优秀的工程实践,为大模型的落地提供坚实的数据基础

01 背景

1.1 百度网盘业务背景介绍

简要介绍一下百度网盘的业务背景

  • 外部视角:用户规模超过10亿,单纯外部用户请求日pv过千亿;

  • 内部视角:实例数超过60w+个,模块数规模过千,单个功能最多涉及100+个模块节点。

在复杂的链路+高并发情况下,短暂的异常就会使得整个系统出现雪崩,即使异常消失也无法自愈,对用户产生不好的用户体验。

于是百度网盘服务端工程方向设计了一套防雪崩的机制,本文先介绍雪崩的技术背景,再介绍相关的解决方案。

1.2 雪崩发生的背景介绍

雪崩是如何发生的?简单的说,某种场景下,触发一个服务的处理能力(容量)不足而拒绝导致请求失败,而它的上游因为请求失败而重试,加剧服务持续处理无效请求,无法处理有效请求,从而形成雪崩死循环,服务无法恢复。

如下图所示,某个变动(根因) –> 服务异常(诱因) –> 链路异常(局部雪崩) –> 全局异常(全局雪崩)

简单来说,雪崩分成两个阶段:

  • 初始阶段:各种根因 + 诱因 –> 服务过载;

  • 循环阶段:上游重试 —> 服务持续处理无效请求 —> 上游重试 ……

图片

下面将按系统视角介绍:雪崩是咋发生的?

先从服务接收&处理请求来说,根据网络TCP三次握手准则,客户端的请求会被放入半连接/全连接队列中,而服务端从队列中取出请求进行处理。由于队列是先进先出的,处理的请求都是之前的请求(即使client已经断开了连接,但是accept队列也不会剔除这些断开连接的请求)。如下图,client 向serve发了5个请求,r0/r1/r2/r3/r4,这些请求会被放入server的accept队列里,但是由于server处理逻辑比较慢,导致client请求超时,断开了连接r0/r1/r2的连接,但是server这边还会从accept队列里取出r0/r1/r2的连接,进行请求request数据的读取(即使client已经断开连接,server还是能够读到之前client发送的请求数据),进行无效的处理,于是client不停的重试,而server不停的在处理失效的请求。

图片

△server读到已经断开连接的socket连接

图片

△ server读到已经断开连接的socket请求数据

从上下游处理视角来说,雪崩并不是局部行为,它会随着链路漫延到整个系统链路上。如下图,client以300ms的超时时间访问server,server在访问A和B之后,已经用掉了300ms,这个时候client已经断开了和server的连接,但是server却继续访问C和D,进行无效的请求。当这种无效请求在整个链路蔓延开,client又在大量的重试的时候,就是整个系统崩溃的时候。

图片

02 传统解决方案

从预防/阻止/止损雪崩三个子方向介绍相关实战经验,以及分析它们的优缺点。

2.1 预防

通过各种手段,规避雪崩初始阶段的出现,例如热点治理、长尾治理、分级操作、容量保障等。

2.2 阻止

处于雪崩初始阶段,有发生雪崩的可能性,需要阻止雪崩进入循环阶段。

【重试率控制】

  • 基本思路:当重试请求数占当前请求数的X%, 就不会再进行重试;

  • 优点:可以避免大量重试导致雪崩死循环;

  • 不足:设置重试率多少才是合理的?另外,这个还需要持续更新,因为下游的容量是在变化的,可能连正常的流量都无法处理(比如实例被缩容了)。

【队列控制】

  • 基本思路:不再基于系统层面的内核队列,应用层自己维护队列(比如用一个线程从系统读取连接数据到队列中),记录请求在队列中等待的时间。如下图,队列中有8个请求,代表他们在队列中等待的时间(单位ms),假设上游的执行是200ms,处理一个请求需要100ms,那么第1个到第4个请求明显是失效请求(最大等待时间( = 上游执行超时 - 服务处理时间) < 当前等待时间),等处理完上游都已经断开连接了,就不需要处理了,可以直接从第5个请求进行处理;

图片

  • 优点:通过维护应用层的队列,加上等待时间,快速抛弃无效请求,避免雪崩的发生;

  • 不足:依赖假设上游的访问超时时间以及自己的执行时间,才能准确判断出请求是否已经失效。但是有可能不同的上游的超时时间不一样,自身的执行时间也不稳定性。同在服务在极端负载下,包括读取系统队列的线程也会受限资源,无法及时从系统队列中读取请求,导致读到了无效请求。

【限流】

  • 基本思路:在服务接入层设置一个静态阈值,表示后端服务最大的请求qps,超过这个qps的流量就丢弃请求;

  • 优点:可以避免瞬间突增的流量打垮服务,导致服务不可恢复,可以解决一部分场景问题;

  • 不足:提前设置限流一个静态阈值,和重试率控制一样,不是所有场景都有效果。另外也有运维维护成本,具体如下:

  • 阈值合理性:需要频繁压测才能知道当前系统的瓶颈qps阈值是多少,设置大了,达不到阻止过多流量的效果,设置小了,又有误伤;

  • 故障期间服务处理能力退化:当花费了很多时间压测出来一个系统的qps阈值是100,但是故障期间服务承载的qps可能会退化成80。会有很多原因会导致,比如一部分实例oom导致不可用,上线导致实例启动失败、长尾流量导致部分实例处理能力不足(比如某些视频转码需要更长的时间,同时还是热门视频,负载均衡重试到不同实例上),最常见的是下游请求执行时间变长了。这样导致每秒都需要承载多余的qps请求,累计下来,这个系统迟早被打垮。本质问题还是静态阈值导致的。

2.3 止损

通过各种手段,加速雪崩止损恢复,例如:

  • 限流:通过多次人工调整限流阈值,使后端请求减少,逐渐恢复;

  • 重启服务:通过重启服务,快速丢弃系统队列中的无效请求。

属于兜底的机制,缺点是整个恢复周期会比较长。

03 解决方案

传统解决方案里,包括预防、阻止、止损三个子方向。

一旦处于雪崩状态,是不可逆的,需要比较长的时间去恢复。

所以重点在于预防和阻止,因为预防会有遗漏,更多的精力在于阻止,即处于雪崩初始阶段,有发生雪崩的可能性,需要阻止雪崩进入循环阶段。

业界的做法,其实可以分成两种做法:

  • 减少过载流量:比如限流和重试率控制,剔除服务不能够承载的流量;

  • 减少无效请求:比如队列控制,使得服务处理有效的流量。

这两种做法需要结合,单个做法是会有问题的。

比如减少过载流量,由于下游的处理能力是动态变化的,实际上还是会出现过载,下游会长期处于处理无效请求,无法处理有效的请求。

减少无效请求,大部分场景下是能搞定的,但是如果瞬间的请求量特别大,请求都是属于有效的,内存可能会先达到瓶颈,导致oom了。

下面介绍一下百度网盘的防雪崩架构实践。

3.1 减少过载流量-基于动态熔断

【基本思路】

业界对减少过载流量,除了上面说的静态限流之外,还有一种熔断做法,具体流程如下:

流程说明
1 开始请求: 系统接收到一个外部请求。
2 熔断器状态判断:
- 闭合状态(Closed): 熔断器允许请求通过,继续执行。
- 打开状态(Open): 熔断器阻止请求,直接失败或返回预定义的响应。
- 半开状态(Half-Open): 熔断器允许部分请求通过,以测试服务是否恢复。
3 执行请求:
- 请求成功:
    - 如果处于闭合状态,则重置失败计数。
    - 如果处于半开状态,则关闭熔断器,恢复正常。
- 请求失败:
    - 增加失败计数。
    - 如果失败计数超过预设阈值,则打开熔断器,跳闸。
4 冷却时间: 熔断器在打开状态后,会等待一段冷却时间,然后进入半开状态。
5 半开状态测试:
- 请求成功: 关闭熔断器,恢复正常。
- 请求失败: 重新打开熔断器,继续等待冷却时间。



这个熔断机制和限流一样的,都是存在静态的缺陷问题。

举个例子,当前流量的qps为100,后端只能承载10的qps。

当后端失败率超过阈值之后,触发打开状态,然后等待一段时间之后,进行半开状态,用一部分流量进行测试。

本质上是不能动态根据后端的处理能力进行流量限制转发,所以需要实现动态熔断限流。

【具体实践】

其核心思路是根据下游的请求成功率动态限制转发到下游的请求数。具体如下图所示,先随机丢弃X%比例的请求,然后进行检测,判断服务是否恢复,如果还未恢复,说明需要继续降低转发到下游的请求数,设置X = X + Step,增大丢弃请求的比例,继续熔断限流。如果已经恢复了,则判断丢弃请求的比例是否已经降低到0(即X是否为0),如果X不为0,则还需要继续减少丢弃请求的比例,设置X = X – Step,继续熔断限流,如果X为0则说明整体已经恢复,则结束动态熔断限流。

动态熔断的思想是借鉴了网络,当雪崩过载的时候,相当于发生了请求的拥塞,和网络拥塞是一样的特征行为,网络链路都带宽相当于服务的容量。

图片

图片

这里面其实还有一种问题没法解决,即ddos攻击。

这种一般需要有一个统一的接入层来解决,设置一个相对大的限流阈值,然后通过动态熔断来转给后端的业务。

3.2 减少过载流量-基于流量隔离

动态熔断策略相对复杂,还有一种简单粗暴的方式,即流量隔离。

适用于流量来源存在不同级别的,而且高优流量常态下比较稳定,低优流量有突增的情况。

【基本思路】

将流量进行分级,通过部署隔离解决高低优流量相互影响的问题,即使低优流量再怎么增加也不影响高优流量。

  • 高优流量:比如用户感知明显流量等;

  • 低优流量:比如后台流量、离线计算流量等。

【具体实践】

首先是client需要打上流量标签,其次是gateway or service mesh基于这些流量标签进行相关的流量转发。

3.3 减少无效请求-基于请求有效性

【基本思路】

上游服务A访问下游服务B的请求时间可以由以下部分组成:

请求耗时 = 上游发送请求 + 网络传输 + 下游tcp建连队列等待时间 + 下游处理时间

  • 上游服务A发送请求:基本可以忽略,机器负载问题不再这里考虑;

  • 请求在网络中的耗时:基本可以忽略,网络故障问题不再这里考虑;

  • 请求在下游服务B系统队列中的等待时间:堆积太多请求会导致请求失效;

  • 下游服务B的处理耗时:处理的慢导致请求长期阻塞在系统队列中。

不再基于单独维护一个队列的思想去解决问题。单独队列一方面是需要支持不同语言,以及准确性不足,在虚拟化的环境里,资源受限之后,队列的等待时间就不准确了,还有上游不同的超时时间,没法简单判断是否已经超时了。

这里面的关键点是上游传递一个截止时间给下游,但是如何表示该请求截止时间呢,具体有绝对时间和相关时间两种方式,相关缺点如下:

  • 绝对时间:用绝对时间戳表示(比如1577681783),但是不同设备上的时钟不一致,如下图A和B两台机器的时钟差了2分钟,A访问B的超时时间是5s,于是请求发到机器B上就直接超时失败了,不符合预期;

  • 相对时间:用相对时间表示(比如5s),但是这个时间是从业务接收到请求开始计时的,没有包括在系统队列中的时间,有可能已经等待了很久的时间了,上游已经断开了连接,不符合预期;

图片

△绝对时间的问题

图片

△相对时间的问题

【具体实践】

从上文可知有这么两个问题,绝对时间和相对时间解决其中之一:

  • 机器之间的系统时钟不一致

  • 需要关注在系统队列中的等待时间

于是可以将绝对时间和相对时间结合在一起,借助UFC(百度网盘的Service Mesh,详见之前的一些分享Service Mesh在百度网盘数万后端的实践落地)来解决问题,以下图流程为例子说明:

  • Host1机器上的服务A访问Host2机器上的服务B;

  • 服务A先访问本地的UFC-agent,传递相对超时时间为5S;

  • Host1机器上UFC-agent 访问Host2机器上的UFC-agent,传递相对超时时间为5S;

  • Host2机器上的UFC-agent 把相对超时时间转成绝对超时时间;

  • Host2机器上的UFC-agent 访问本地的服务B。

图片

通过双端的Agent实现了相对时间到绝对时间的转换,对业务解决了上面的那2个问题,对业务也是透明的。

图片

目前网盘的核心链路都接入了这套请求执行时间过期丢失的机制。

不过这个也是存在一些缺陷场景的,比如网络故障/ufc agent资源打满等,使用其它的解决方案来解决这些缺陷。

3.4 减少无效请求-基于socket有效性

上面是基于deadline来判断请求是否有效的,是百度网盘19年的技术方案。

当时百度网盘的技术栈还是比较多样的,编程语言包括c/c++/php/golang/ngx_lua等多种,需要从Service Mesh这种中间件来解决问题。

另外,这个依赖Service Mesh这种中间件的落地覆盖,并不是所有的业务都具备这种的前提条件。

这里提供另外一种基于socket有效性的方案来判断请求是否有效。

【基本思路】

需要有一个认知:

比如以下场景:

  • tcp三次握手之后,server 未accept请求,client直接调用close关闭连接;

  • tcp三次握手之后,server 未accept请求,client先写request请求,然后调用close关闭连接;

  • server accept后进行请求处理时,client调用close关闭连接。

从c语言编程视角来看,非阻塞情况下read函数返回-1表示读数据失败,返回0表示读到fin包,即client关闭了socket。

ssize_t read(int fd, void *buf, size_t count);

所以可以基于socket读到fin包事件来判断client是否已经断开连接了,如果已经断开,server则不需要处理接下来的逻辑了(异常场景收尾逻辑可能还需要)。

【具体实践】

底层socket编程这块,一般都是被编程语言/编程框架屏蔽了,这里主要是介绍一下一些常见语言/编程框架怎么判断client已经断开连接了。

先说一下brpc框架,如下图所示,主要是调用IsCanceled函数来判断client是否已经断开了(不适用http 2.0等这样的场景)。

验证效果的话,可以通过在brpc框架里面的accept 逻辑前面加sleep,构造server backlog 堆积的场景进去验证。

图片

图片

再说一下golang语言,主要是通过http server的回调函数的参数r *http.Request进行判断的,具体是判断 r.Context().Done()。

验证效果的话,可以通过netutil.LimitListener来设置最大处理的连接数。

图片

图片

不过golang和brpc的内部实现还是有点不一样的,下面将举例说明:

当一个client 与server通过tcp三次握手建立连接后,进入了server的全连接队列中,client调用close关闭连接,1秒之后,server 调用accept取出该连接。

brpc通过IsCanceled是可以判断出client已经断开连接了,而golang通过r.Context().Done()却判定client还未断开连接,需要sleep若干时间之后才能判断client已经断开连接。

原因是golang在源码里是起了一个单独的协程去读socket,所以导致这个判断会出现延迟。

图片

解决方案是通过ConnContext回调函数,把连接存储在context里,然后在http server的回调函数里取出连接,先读一次,这样就可以判断是否已经断开了。

图片

图片

04 总结

百度网盘业务形态众多,业务的高速迭代发展需要建立在可靠的架构基础之上。

在整个架构演进过程,可用性是非常重要的事情,于是设计了一套防雪崩架构,具体包括两部分:

  • 流量限制:可以分成两部分,一个是流量接入层,解决ddos连接数攻击,另外一部分是流量转发层,通过动态熔断策略将后端能处理的请求数转发给后端;

  • 流量处理:业务基于流量有效性进行处理,避免处理无效请求。

图片

最终对雪崩的治理也取得了不错的效果,单个季度可以规避若干次的雪崩故障发生,保障了网盘业务的可用性。

本文只是抛砖引玉,更多的是希望与大家一起探讨什么才是优秀的工程实践,欢迎大家留言反馈,多谢!!!

————END————

推荐阅读

如何在百度百舸部署满血版DeepSeek-V3、DeepSeek-R1模型

首日调用客户破1.5万!DeepSeek-V3/R1上线背后的超低推理成本技术揭秘

唤醒 AI 算力,专有云 ABC Stack 面向企业级智算平台的 GPU 提效实践

百度APP iOS端磁盘优化实践(上)

对话AI原生|比帮你写代码更爽的是:让Agent来打工

如何在百度百舸部署满血版DeepSeek-V3、DeepSeek-R1模型

作者 百度Geek说
2025年2月13日 11:09

百度百舸·AI异构计算平台已支持快速部署DeepSeek-V3、DeepSeek-R1及其蒸馏的Llama、Qwen等小规模dense模型。您可以登录百度百舸平台快速部署DeepSeek系列模型体验模型效果。

01 开通轻量计算实例

开通一台H20(ebc.lgn7t.c208m2048.8h20.4d)规格的计算实例并添加到百度百舸·AI异构计算平台。

图片

02 部署vLLM

在百度百舸平台的左侧导航中选择「工具市场」页面,部署工具vLLM。

图片

03 模型推理

vLLM部署成功,登录实例下载模型并启动vLLM服务,安装WebUl客户端。

图片

发送请求开始对话。

图片

04 各系列模型的推荐配置清单

图片

在完成满血版DeepSeek模型的快速部署后,百度百舸·AI异构计算平台还能为这些在线服务提供全生命周期管理、自研框架推理加速、推理资源碎片整理等能力。在保障服务稳定性的同时,有效降低推理成本并提升推理性能。

访问百度百舸页面cloud.baidu.com/product/aih…

————END————

推荐阅读

首日调用客户破1.5万!DeepSeek-V3/R1上线背后的超低推理成本技术揭秘

唤醒 AI 算力,专有云 ABC Stack 面向企业级智算平台的 GPU 提效实践

百度APP iOS端磁盘优化实践(上)

对话AI原生|比帮你写代码更爽的是:让Agent来打工

0 Token 间间隔 100% GPU 利用率,百度百舸 AIAK 大模型推理引擎极限优化 TPS

首日调用客户破1.5万!DeepSeek-V3/R1上线背后的超低推理成本技术揭秘

作者 百度Geek说
2025年2月11日 10:18

2月3日,百度智能云千帆大模型平台正式上线了DeepSeek-R1与DeepSeek-V3模型,模型上线首日,已有超1.5万家客户通过千帆平台进行模型调用。百度智能云针对此次模型上线提供了行业领先的超低推理价格,低至DeepSeek官方刊例价3-5折,当前还可享受限时免费服务。这一突破性进展的背后,是百度智能云在推理引擎性能优化技术、推理服务工程架构创新以及推理服务全链路安全保障上的深度融合。本文将深度解析其技术实现路径。

图片

已上架至千帆ModelBuilder「模型广场」

图片

可在千帆ModelBuilder「体验中心」立即体验

01 推理引擎性能优化技术

基于百度智能云在大模型推理性能优化方向的技术积累,针对DeepSeek模型MLA结构的计算进行了极致的性能优化,并通过计算、通信、内存不同资源类型算子的有效重叠及高效的Prefill/Decode分离式推理架构等,在核心延迟指标TTFT/TPOT满足SLA的条件下,实现模型吞吐的大幅度提升,进而显著降低模型推理成本。

02 推理服务的工程架构创新

在推理服务层面,进行了深入的优化与创新。针对推理架构,做了严格的推/拉模式的性能对比。经验证拉模式在请求处理的成功率、响应延时以及吞吐量等关键指标上均展现出更为卓越的性能。为了进一步提升系统的稳定性和用户体验,巧妙地设计了一种请求失败的续推机制,这显著增强了系统的容错能力和服务SLA达标率。同时针对多轮对话和system设定等场景中存在重复Prompt前缀的情况,实现了主流的KV-Cache复用技术,并辅以全局Cache感知的流量调度策略。这一举措有效避免了Token KV的重复计算,从而大幅降低推理延迟,提高了推理吞吐。

03 推理服务的稳定安全保障

千帆平台基于百度自身长期的大模型安全技术积累,集成独家内容安全算子,实现模型安全增强与企业级高可用保障,基于大模型全生命周期数据安全与模型保护机制,在千帆平台上的模型均拥有使用安全的安全保障;基于在安全方面的专项优化,确保DeepSeek-R1&DeepSeek-V3模型,企业用户在使用过程也具有更高的安全性。

百度智能云千帆ModelBuilder始终致力于为用户提供全流程、一站式的 AI 服务,除了强大的模型资源,还匹配了完善的一站式模型效果调优工具链,包含数据加工、模型精调、模型评估、模型量化等关键环节,助力企业根据自身业务需求深度优化模型性能。同时,百度智能云千帆ModelBuilder具备卓越的模型推理托管能力,支持vLLM、LMDeploy、TensorRT-LLM、SGLang等各类主流推理框架,还支持模型的自定义导入与部署,为开发者提供了高度灵活的开发环境。

值得一提的是,百度智能云近日成功点亮了昆仑芯P800万卡集群,这也是国内首个正式点亮的自研万卡集群,百度智能云将进一步点亮3万卡集群。

未来,我们将持续解锁更多技术文档,共享行业最佳实践案例,助力每一位创新者更快突破技术边界。我们期待与更多企业用户、开发者一起,共同探索无限可能,携手共创AI新篇章。

————END————

推荐阅读

唤醒 AI 算力,专有云 ABC Stack 面向企业级智算平台的 GPU 提效实践

对话AI原生|比帮你写代码更爽的是:让Agent来打工

0 Token 间间隔 100% GPU 利用率,百度百舸 AIAK 大模型推理引擎极限优化 TPS

百度视频搜索架构演进

网页结构建模在低质采集站上的识别应用

唤醒 AI 算力,专有云 ABC Stack 面向企业级智算平台的 GPU 提效实践

作者 百度Geek说
2025年2月6日 10:30

从「建好」到「用好」,企业级智算平台借助专有云 ABC Stack 的 GPU 提效服务,应对大模型业务挑战,唤醒 AI 算力,加速 AI 原生业务的落地。

01 难以一步到位的GPU效能

当企业的私有化智算平台项目上线一段时间后,用户普遍会反馈 GPU 效能相关的问题:

  • 将全部资源分配给各个业务部门后,集群全部 GPU 资源的平均利用率在 30% 左右。这个指标处于什么水平,是否充分发挥 GPU 效能?

  • 大模型训练的时候,我们会请技术专家排查 GPU 集群故障问题,有时居然要 2~3 个小时才能搞定,这个时间这么长,还能缩短吗?

  • 新平台按照最高的硬件进行配置,但是常有业务线反馈,相比过去的老集群,智算平台上的任务速度并没有明显提升,这是为什么呢?

那么,企业遇上这些问题的原因是什么呢, GPU 效能可以一步到位吗?

先说结论。根据对不同的企业级智算平台类项目实践的总结:在平台落地后就处于 GPU 最佳效能的状态,这是几乎不可能的。

这些问题的出现和解决,正好体现了企业级智算平台和客户大模型业务落地磨合的过程。

这些问题的原因,有一部分来自于智算平台从无到有,再到大规模 AI 业务落地过程中,智算平台管理部门在不同阶段,关注的目标和业务运行环境的变化所致:

  • 在 POC 阶段,通常是用若干个典型任务做功能、性能和稳定性测试。这些测试可以提前规划,可控性更大。整个过程关注的是平台自身能力的评估。

  • 在大规模生产落地阶段平台开始承载所有部门的业务,需要考量的维度更加复杂,比如资源如何分配满足不同业务需要,平台如何正确使用确保业务能够高效运行等。

另外一部分原因,可能占更大比例,则是因为企业级客户,在过去已经习惯「小模型」和「老平台」后,面对「新平台」运行「大模型」中,需要有一段学习和适应的时间。

02 从资源管理到任务设置,唤醒AI算力

基于百度在大规模集群的技术积累和工程实践,在向企业交付智算平台后,专有云 ABC Stack 还为客户提供了一套面向整体 GPU 算力平均利用率、训推任务加速和稳定性等场景的 GPU 提效服务。

2.1 调整资源分配策略,提升GPU平均利用率

每个业务部门都期望能够获得/储备更多的 GPU 资源加速自己的 AI 任务速度,也可以免去申请更多资源的时间。不过,智算平台管理部门的目标稍有不同,会更多聚焦于在全局资源有限的情况下,能够实时按需分配资源,覆盖全部业务,使得资源利用效率最大化。

为了在「各个部门的业务效率」和「集团整体资源利用率」之间达到平衡,智算平台管理部门需要深入分析不同部门的业务模型,统计过往的任务类型和 GPU 资源使用量等情况,以便找到合适的资源分配策略。

比如,将过去统一把全部资源分发给业务部门的模式,变成把其中一部分资源作为保底资源分发给业务部门,剩余资源作为所有部门的共享按需调度的模式。其中,本周期内各个部门的保底资源额度,可以按照「上一个周期的统计数据」进行预测,适当进行缩减或者扩大。当通过监控数据发现资源总量不足时,及时进行扩容。

2.1.1 实践

传统车企 A 的自动驾驶平台,将智算平台的全部 GPU 资源固定划分给车辆视觉、雷达感知、数据处理、BEV 等 9 类业务。全部业务上线运行 2 个月后,整体 GPU 平均利用率在 30% 附近波动。

为探索 GPU 利用率是否有提升空间,车企 A 联合专有云 ABC Stack 共同对各个业务在过去 2 个月的使用情况进行了详尽的调研,发现全部的节点中:

  • 20% 节点的 GPU 利用率长期不足 1%,这说明这些 GPU 资源几乎被浪费了;

  • 20% 节点的 GPU 利用率较高,且多次超过 80%。这说明在未来这些资源有一定的超负荷风险;

  • 另有 30% 的节点的 GPU 利用率大幅波动。这说明这些 GPU 存在一定的弹性调度空间。

因此,智算平台管理部门将预设的「整体资源按业务部门固定分配」管理方式,调整为「整体资源按调度方式灵活分配:保底 + 共享」的管理方式。

针对各个业务设置保底 GPU 资源,然后将未划分的 GPU 算力纳入集团公共资源池中,供各个业务方按需调用。同时,为了能够更好地管理资源,适应业务变化,车企 A 成立了 GPU 资源管理专委会,每两周对资源使用情况进行汇总分析,动态调整保底 GPU 资源,监控整体 GPU 资源水位。

通过以上资源管理措施的调整,车企 A 的 GPU  整体平均利用率从 30% 提升到了 45%。

2.2 系统性建设容错&稳定性能力,提升GPU有效训练时长

在小规模 GPU 场景下,通常只需要关注硬件异常引发的任务中断,快速替换故障节点并重新拉起训练任务进行故障恢复,就能解决大部分的问题。在千卡的大模型场景中,有很多问题并不会直接反映出硬件异常,例如训练进程 hang 死、训练降速、loss 跑飞(loss 值为 NaN/Inf)等等,这类问题可能跟用户代码、训练框架、硬件故障、操作系统、网络、存储都有关系。

此时,仅仅依赖专家经验人工处理故障,时长和结果都将是一件不可控的事情。

**我们需要更系统的方法,来实现感知异常、诊断定位及故障恢复。通过对训练进程、节点状态、网络流量和计算负载等多维度数据的监控与分析,快速识别异常行为,然后进行自动恢复,最终生成详细的故障报告,**缩短「感知–定位–重启–恢复」整个流程时间,提升有效训练时长。

2.2.1 实践

互联网企业 Z 经历了从小模型升级到大模型业务的转变。在小模型场景已经积累了足够的专家经验处理各类故障问题。在切换至大模型场景后,没有第一时间进行平台稳定性的建设,在故障感知、定位和恢复中投入了大量的人力成本,造成了资源的严重浪费。

借助百度百舸平台的稳定性&容错能力, 互联网企业 Z 在大模型训练任务中实现了显性故障和隐形故障的及时感知、精准定位、故障隔离和自动恢复,平均故障恢复时间从 3 小时缩短到 20 分钟,任务有效训练时长大幅提升,确保了大规模训练任务的持续稳定运行。

2.3 正确配置系统参数,释放GPU性能加速训练任务

大模型训练任务的效率,不仅仅和集群中 GPU 的性能和数量相关,还需要将计算、网络、存储各类资源进行合理配置,使得他们能够将任务各个环节进行无缝衔接,充分发挥整个平台的能力。

一个完整的业务流程,从数据采集开始,再到将预处理好的数据送入 GPU 进行训练,经过多轮迭代后,将最终结果写入存储完成训练。整个业务流程步骤非常多,每个环节的提速都能缩短大模型训练任务的时间。

尤其各类面向大规模 GPU 集群的全新高性能组件(并行文件存储 PFS、RDMA、模型训推加速库等)的引入,对于习惯了小模型业务场景,刚接触大模型和 GPU 集群的企业用户来说,如何才能用好这些能力加速模型任务呢?

为了全面地提升任务运行效率,需要对大模型训练过程中的各个环节进行梳理,给出理想的系统配置和关键指标,然后与智算平台的硬件配置和期望指标对比,以便找到潜在的优化点。

2.3.1 实践 1

传统能源企业 H 使用并行文件存储 PFS 提速从对象存储 BOS 到 GPU 的数据加载流程。运行一段时间后,业务部门发现模型训练速度虽有提升,但似乎离预期还有不小距离。

专有云 ABC Stack 对客户的整个数据流转过程和相应配置参数进行了梳理和分析,发现客户将 PFS 的工作模式设置为「仅加载元数据」,即仅将对象存储 BOS 的元数据进行了加速,导致在任务中未能充分发挥 PFS 的性能。

传统能源企业 H 的业务团队在将 PFS 的工作模式从「仅加载元数据」修改为「加载完整数据」后,任务训练速度提升了近 40 倍。

2.3.2 实践 2

互金企业 Y 将多机多卡的 AI 模型训练任务部署至拥有 RDMA 网卡的新 GPU 集群进行训练。但经过一段时间,发现新集群的训练速度,与未配置 RDMA 的老平台相比,并没有预期成比例提升。

专有云 ABC Stack 与客户深入沟通后发现,性能未达预期的原因在于 RDMA 未被正确配置。互金企业 Y 此前主要运行小模型训练任务,并不需要使用 RDMA。所以在大规模 GPU 集群的使用过程中,直接将老平台的使用经验复制过来,没有将 NCCL 中 RDMA 相关的环境变量配置到容器中。

互金企业 Y 在使能了 RDMA 网卡节点后,数据加载性能和 GPU 多卡训练性能明显提升,任务训练效率对比提高约 2 倍。

2.3.3 实践 3

在自动驾驶业务场景中,会经历「模型选型 - 模型训练 - 模型上车」等几个步骤,研发团队需要在不同模型中做实验选出最合适的模型,并完成模型训练,最后部署在量产车上。所以模型训练的速度越快,量产车获得最新 AI 能力的速度就越快,客户的体验就越好。

在与专有云 ABC Stack 的交流中,传统车企 C 了解到百度百舸的模型算法团队针对各类主流的自动驾驶模型都进行了极致优化,相比开源版本性能有大幅度提升,均已在 AI 加速套件 AIAK 中上线。

车企 C 的智算平台升级了最新的 AIAK 加速库,使得工程团队可以从 AIAK 中直接调用经过优化的模型,吞吐量最高提升 400% ,缩短 80% 的训练时间。

03 从「建好」到「用好」,加速 AI 原生业务的落地

当然,不止于上文提到的方法,GPU 效能的提高涉及到方方面面,比如合理划分故障域、为新的 AI 加速芯片开发监控指标、部署合适的任务资源调度策略、编写适用于大模型平台的管理手册等。

从「过去几块 GPU 跑小模型,业务逐步智能化」到「现在 GPU 集群跑大模型,业务全面智能化」业务场景的转变,这给企业的智算平台高效能运行带来了挑战。同时,由于 AI 原生应用、大模型、基础设施平台等相关技术正在快速演进,AI 算力提效将是一个长期存在的课题。

凭借着百度百舸 4.0 在大模型基础设施方向的领先技术,以及在不同企业级智算平台项目中积累的丰富经验,专有云 ABC Stack 将帮助企业成功应对最新的大模型业务挑战,「建好」和「用好」智算平台,加速 AI 原生业务的落地。

————END————

推荐阅读

百度APP iOS端磁盘优化实践(上)

对话AI原生|比帮你写代码更爽的是:让Agent来打工

0 Token 间间隔 100% GPU 利用率,百度百舸 AIAK 大模型推理引擎极限优化 TPS

百度视频搜索架构演进

网页结构建模在低质采集站上的识别应用

百度APP iOS端磁盘优化实践(上)

作者 百度Geek说
2025年1月23日 10:37

01 概览

在APP的开发中,磁盘管理已成为不可忽视的部分。随着功能的复杂化和数据量的快速增长,如何高效管理磁盘空间直接关系到用户体验和APP性能。本文将结合磁盘管理的实践经验,详细介绍iOS沙盒环境下的文件存储规范,探讨业务缓存、用户资产及系统缓存的清理策略。同时,分享自动清理与手动清理相结合的机制,展示如何在不同触发条件下合理执行磁盘清理。文章使用文心一言辅助编写。

02 磁盘系统介绍

2.1 ios沙盒系统

沙盒机制是iOS系统中的一种安全体系。每个iOS程序都有一个独立的文件系统,而且只能在对应的文件系统中进行操作,此区域被称之为沙盒(SandBox)。APP中所有文件都保存在此,如文本文件、图片、图标、媒体资源、Mach-O等。主要包含4个目录 MyApp.app、Documents、Library、tmp。

MyApp.app目录包含应用程序及其所有资源,即.ipa安装包解压后的.app内容,仅支持只读访问。Documents目录用于存储用户生成的内容,可以通过文件共享提供给用户,并由iCloud备份。Library目录则用于存放非用户数据文件的顶级目录,通常包含几个标准子目录,如Application Support和Caches,并由iCloud(Caches除外)备份。而tmp目录用于写入临时文件,这些文件不需要在应用程序启动之间保留,当不再需要时应由应用程序删除,且不被iCloud备份。 图片

2.2 获取目录API

NSSearchPathForDirectoriesInDomains()函数用于查找目录,返回指定范围内的指定名称的目录的路径集合。

// 获取沙盒主目录路径
NSString *homeDir = NSHomeDirectory();
// 获取Documents目录路径
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 获取Library的目录路径
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
// 获取Caches目录路径
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 获取tmp目录路径
NSString *tmpDir =  NSTemporaryDirectory();
//应用程序程序包的路径
[[NSBundle mainBundle] bundlePath];


2.3 文件存储规范

iOS项目开发中,文件管理至关重要。我们结合Apple官方指导与项目实践,形成了一套详细的文件使用规范。这些规范不仅遵循了iCloud备份策略,还充分考虑了文件类型特性和业务需求。结合业务使用场景,能够更准确地判断哪些文件是临时数据、哪些是用户生成的重要文件,为后续进行磁盘监控和清理提供了便利。

MyApp.app:

  • 用途:应用程序的包,此目录包含应用程序Mach-O可执行文件及其所有资源,iCloud默认不备份此目录。

  • 使用规范:

  • 开发者不能写入此目录。写入此目录会影响签名导致APP无法启动。

  • 开发者可以获得对应用程序包中存储的任何资源的只读访问权限。

Documents:

  • 用途:存储用户生成的内容的顶级目录,iCloud默认备份此目录。

  • 使用规范:

  • 该目录应仅包含开发者可能希望向用户公开的文件。

Documents/Inbox:

  • 用途:存储通过外部共享方式导入到APP的文件,iCloud默认备份此目录。

  • 使用规范:

  • 开发者不应自行在此目录下创建文件。

  • 开发者可读可删,将此目录中文件移出后再编辑。

Library:

  • 用途:存储非用户数据文件的顶级目录,iCloud默认备份此目录(Caches除外)。

  • 使用规范:

  • 可以创建自定义子目录,用于保存不向用户公开的任何文件。

  • 各业务方需要创建业务相关的子目录存储数据。

Library/Caches:

  • 用途:用于存放再次下载或重新生成的数据,iCloud不会备份此目录。

  • 使用规范:

  • 存放可以重新下载或生成的数据,如图片缓存文件、临时文件等。

  • 系统在系统储存不足时可能会删除该目录以释放磁盘空间。

  • 各业务数据缓存文件应放在/Library/Caches的子目录下,方便管理。

Library/Application Support:

  • 用途:用于存储APP配置和数据文件,iCloud默认备份此目录。

  • 使用规范:

  • 存放程序配置和数据文件,如配置文件、模板等,不应存放缓存文件。

  • 避免在此目录下创建不必要的文件,以减少备份时间和存储空间占用。

Library/Preferences:

  • 用途:用于存储应用程序的首选项值,iCloud默认备份此目录。

  • 存放建议:

  • 开发者不应自行在此目录下创建文件。

  • NSUserDefaults或CFPreferences来获取和设置APP的首选项值。

tmp:

  • 用途:用于存放临时数据,iCloud不会备份此目录。

  • 存放建议:

  • 仅存放临时数据,开发者应在使用完这些文件后及时删除。

  • 系统会清除目录下文件,应用程序终止后可能不存在。

  • 不推荐业务方使用此目录存放重要数据。

2.3.1 文件命名规则

  • 文件夹命名推荐Pascal命名法,第一个字母大写。

  • 一个业务最多3个二级路径:

  • 业务数据放在Library/下的一个目录下。

  • 数据缓存文件放在Library/Caches的一个目录下。

  • 用户数据放Documents/下的一个目录下。

2.3.2 iCloud备份与恢复

iCloud会默认同步Documents、Library 目录下的文件(Caches子目录除外),用户在设置中开启iCloud同步功能,同步APP数据时会上传所有沙盒数据。如果没有按照规范创建目录,会导致大量缓存文件被上传到iCloud,严重占用用户iCloud 空间,严重时甚至会被Apple官方警告。如果将数据写入这些目录,但是又不希望同步到iCloud,iOS提供了API可以排除默认同步的目录,需要开发者在创建文件时主动设置。

由于无法查看iCloud备份的沙盒文件内容,使用Mac本地备份的方式模拟iCloud备份行为。iPhone连接Mac,打开访达,选择将数据备份到Mac,恢复备份。测试发现Document文件夹下的子目录和子文件,属性被设置为NSURLIsExcludedFromBackupKey,恢复备份后不在,符合预期。不过用电脑备份和iCloud有个差异,Library/Caches下的文件也会被备份。

注意:此方法对于在App Store的应用,即使在恢复备份前将APP删除,也会在恢复备份后自动下载APP,并且恢复数据。对于不在App Store的应用,比如我们的测试DemoAPP,在恢复备份前不要删除DemoAPP,否则无法查看效果。因为无法下载不在Appstore的APP安装包,所以备份内容也无法还原。测试APP在恢复备份后重新安装就可以查看完整的沙盒文件。

  • API设置对于性能的损耗:
    iPhone12 :总计 14780 个文件,总计耗时 1124.180794 ms,平均每次耗时0.076ms
    iPhone6p:总计 11650 个文件,总计耗时 3730.319023 ms,平均每次耗时0.32ms

  • NSURLIsExcludedFromBackupKey不需要每次使用目录或文件时都调用,只需要设置一次即可。设置属性后,该目录下所有子目录和文件都不会同步到iCloud,但是子目录和文件的属性NSURLIsExcludedFromBackupKey依旧为NO。

  • 创建文件URL时需要调用[NSURL fileURLWithPath:] ,然后获取和设置 NSURLIsExcludedFromBackupKey。

NSURL *pathurl = [NSURL fileURLWithPath:path];
BOOL success = NO;
success = [pathurl setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];


2.4 磁盘大小计算方式

在系统中可以查看一个APP占用磁盘大小,iPhone 存储空间通常由APP 大小文稿数据大小组成。getResourceValue:forKey:error: 或者attributesOfItemAtPath:error: 直接获取的是“文件大小”,不是文件占用的磁盘物理空间。计算文件的磁盘占用大小需要通过磁盘块的方式。标准头文件stat.h的st_blocks可以获取到块的数量,st_blocks * 512计算出文件的磁盘占用大小。 扇区(block)是磁盘读写的最小物理单位。之所以扇区大小是 512 字节,这种做法源自较早的磁盘扇区大小标准。这种设计是基于早期磁盘技术的物理限制,尽管现代硬盘的物理扇区大小可能已经增加到了 4096 字节,但 512 字节的逻辑块大小在许多文件系统和操作系统的接口中仍然被保留。iOS系统存储单位为十进制,且保留有效小数机制为四舍五入。

图片

+ (unsigned long long)fileSizeOnDisk:(nonnull NSString *)filePath {
struct stat fileStat;
int res = stat([filePath cStringUsingEncoding:NSUTF8StringEncoding], &fileStat);
if (-1 == res) {
return 0;
    }
long long fileSize = fileStat.st_blocks / 8 * fileStat.st_blksize;
return fileSize ;
}



2.5 APP包大小

APP包大小是手机沙盒的重要组成部分,包体积直接影响用户转化率。用户可以在iPhone存储空间设置中查看设备上各APP大小文稿与数据大小。

百度APP在iOS17系统的iPhone存储空间设置中,系统不显示APP大小,只有文稿与数据大小。经过调研,发现在iOS17系统APP使用ODR功能后,会导致APP大小被计算到了iOS系统数据。推测是iOS17系统BUG,iOS18系统已经恢复正常。

03 磁盘清理

在综合性 APP 的开发中,多个业务方共同使用沙盒空间,磁盘缓存的管理面临诸多挑战。每个业务方对磁盘使用的整体感知较弱,难以准确评估自身的空间占用大小是否合理,磁盘占用很容易出现无限增长的问题。同时,不同业务方可能独立设计了缓存清理策略,缺乏统一规范,难以满足统一管理的需求。

为解决这些问题,从全局出发设计一套高效的磁盘清理方案。这套方案结合自动清理手动清理的机制,对磁盘使用进行统一管理。自动清理负责监测磁盘占用情况,通过设置清理阈值和判断条件,在空间不足时通知或触发业务方执行清理操作。清理范围以业务缓存为主,支持按业务个性化配置清理规则,确保管理灵活高效。手动清理则面向用户,提供简单直观的操作界面,允许用户自主选择清理范围,管理下载文件等数据,从而平衡自动化与用户自主权。

此外,在iOS平台上,系统本身具有定期清理临时文件和部分缓存内容的机制。设计磁盘清理方案时,合理利用这些系统行为,同时避免依赖其不可控性。通过以上策略,可以在确保磁盘空间高效管理的同时,最大限度地保障用户体验,为综合性APP提供可靠的存储管理支持。

图片

3.1 自助清理

3.1.1 业务限额

为了管理和规范业务方对磁盘的使用,根据业务的使用场景和必要性,评估每个业务方需要使用的磁盘空间,每一个业务占用的缓存大小会被分配一个限额,业务需要保证自身缓存占用限制在限额以下。业务方从磁盘组件获取业务自身限额值,保证自身缓存占用在限额以内;而磁盘组件会从磁盘剩余大小、APP已使用大小、业务使用频率等条件实时分配合适的限额给各个业务方。

3.1.2 磁盘等级

根据设备的剩余磁盘空间与应用程序所占用的磁盘空间大小,我们可以将磁盘状态细分 为Normal、Warning、以及Critical三个等级,每个等级都反映了用户磁盘空间的使用状况。当处于Normal时,表明用户的手机尚拥有充裕的剩余空间,且APP所占用的磁盘空间相对较小。在此情境下,我们可以适度增加分配给各个业务方的限额,使得业务方有机会缓存更多内容,从而实现以空间换取时间的性能优化,进一步提升用户体验。

然而,一旦进入Critical等级,意味着用户的手机剩余空间已所剩无几,或是 APP占用了过多的空间。这种情况下,用户的手机很容易出现卡顿现象,甚至面临存储空间告急的困境。更为严重的是,这可能会迫使用户做出卸载APP的极端选择。因此,在Critical等级下,我们必须对业务方使用磁盘空间的行为进行严格限制,并强制要求业务方彻底清理任何不必要的缓存,以确保用户的手机能够维持基本的运行需求。

3.1.3 状态检测

磁盘状态检测是磁盘组件的核心功能,检测 APP 运行期间设备磁盘的使用情况,判断是否需要触发自动清理操作。APP 启动后周期性的执行磁盘使用状态检查,自动触发相应的清理机制。通过不同条件触发磁盘清理,以优化应用性能并提升用户体验。为此设计了多种触发清理机制的机制,确保不同情况下都能触发清理操作:

等级:根据磁盘使用等级的变化来决定是否清理;

版本:如果应用版本发生升级,会强制业务方执行一次清理。 时间:根据磁盘等级设定兜底的定期清理间隔。

3.1.4 触发清理

业务方在各自的组件中注册磁盘清理回调protocol,不同的业务模块能够响应磁盘等级变化并进行相应的处理,实现了磁盘清理服务与业务模块之间的解耦合。业务在收到磁盘状态等级变化时,重新查询业务限额,并调整自身策略,管控和优化自身占用缓存,保证保证自身缓存占用在限额以内。

磁盘组件会逐一调用各业务模块注册的清理回调,通过protocol抽象各模块的清理逻辑,实现了对多业务模块的统一管理和扩展性支持。结合信号量机制实现同步等待,确保每个模块的清理任务按顺序完成。所有清理任务完成后,磁盘组件会触发全局回调进行状态更新。

3.2 手动清理

手动清理沿用自动清理的设计思路,业务方实现清理协议,注册到磁盘组件,然后由磁盘组件统一管控。和自动清理不同的是,手动清理提供了一个用户可视化页面,并且详细列出可清理的内容供用户选择。支持深度清理,在大多数情况下,业务逻辑要求清除所有缓存数据。除此之外,还另外添加了用户资产管理功能。

3.2.1 用户资产管理

用户资产定义为用户自主下载并保存的各类文件,包括但不限于视频、图片、安装包、PDF 文档、Excel 表格以及 Word 文件等。百度APP是一个综合性 APP ,兼具浏览器的功能。用户在浏览网页的过程中,会下载各种内容。特别是在 iOS 设备上,用户还可能会下载一些无法安装的安装包。尽管这些文件是用户主动下载的,但实际上它们可能并无实际用途,且长期占用用户的磁盘空间。用户资产管理模块可以帮助用户轻松地识别并清理那些不再需要或无效的用户资产,从而有效释放存储空间,提升设备的整体性能。

3.3 系统缓存清理

3.3.1 tmp目录

iOS的tmp目录是系统缓存目录,Apple官方文档的说明是使用此目录写入不需要在APP启动之间保留的临时文件。当不再需要文件时,APP应从该目录中删除这些文件,系统也可能会在APP未使用时清除此目录。实际使用过程中发现,依赖系统清理该文件夹具有不可控性,因此需要对该文件夹进行清理,tmp目录主要包含以下文件:

图片

1. 下载缓存文件

NSURLSessionDownloadTask进行文件下载的过程中,系统为了确保数据的完整性和安全性,会在文件下载完成前,将其暂时保存在以.tmp为扩展名的临时文件中。这些临时文件的命名遵循CFNetworkDownload_xxxx.tmp的格式,其中CFNetworkDownload_是固定的前缀,而后续的xxxx部分则根据每个下载任务的不同而有所区别。

NSURLSessionConfiguration配置会导致这些临时文件被存储在沙盒的不同位置。defaultSessionConfiguration创建的下载任务,其临时文件会被保存在沙盒的tmp文件夹内。backgroundSessionConfiguration,临时文件的存储位置则有所不同。在后台下载模式下,为确保即使应用进入后台,下载任务也能继续进行。这些文件会存放在Library/Caches/com.apple.nsurlsessiond/Downloads/<bundle_id>。

然而,需要注意的是,沙盒中的tmp文件夹主要用于存放临时文件,这些文件在磁盘空间不足时可能会被系统自动清理。因此,如果下载任务被取消,且临时文件没有被及时移动到其他安全的存储位置,它们可能会因为 tmp 文件夹的清理机制而被删除。这将导致用户无法从上次中断的位置继续下载,即无法实现断点续传。

2. tmp目录清理

统计tmp目录下可以被清理的文件,可以选择全部清理,也可以按照业务实际应用场景,选择过期清理等方案。

/// 统计 /tmp 目录下需要清理的缓存文件
+ (NSArray<NSURL *> *)calculateTmpSysCache {
// 匹配文件名的模式
NSArray *regexPatterns = @[@"CFNetworkDownload", @"WKWebFileUpload", @"NSIRD_", @"正在存储文稿"];
NSString *tmpPath = NSTemporaryDirectory();
if (!tmpPath) return @[];
NSMutableArray *tmpNeedCleanCache = [NSMutableArray array];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString *fileName in [fileManager enumeratorAtPath:tmpPath]) {
NSString *fullPath = [tmpPath stringByAppendingPathComponent:fileName];
NSURL *fileURL = [NSURL fileURLWithPath:fullPath];
// 检查是否符合清理条件
if ([self shouldCleanFile:fullPath patterns:regexPatterns]) {
            [tmpNeedCleanCache addObject:fileURL];
        }
    }
return tmpNeedCleanCache;
}

3.3.2 WKWebView清理

WKWebView是iOS和MacOS上用于加载和展示网页内容的控件,它利用了多种缓存机制来提升加载速度和用户体验。以下是WKWebView缓存产生的几个主要原因:

  • 资源缓存(HTTP 缓存):WKWebView会缓存通过网络请求加载的网页资源(如 HTML、CSS、JS、图片等),以减少重复下载,提升网页的加载速度。这些资源通常存储在本地磁盘上。

  • Cookie 缓存:网站使用的Cookie会被WKWebView缓存,用于维护会话状态、用户登录信息等。

  • Session Storage和Local Storage:WKWebView支持HTML5的sessionStorage和localStorage,用于本地存储网站数据,以便后续访问时能够直接从缓存中读取,而无需重新下载。

  • WebKit Cache(WebKit内部缓存):WebKit内部有一套复杂的缓存系统,用于管理各种网页资源、脚本、图像等的缓存。WKWebView依赖这些机制来加速网页内容加载。

  • Service Workers和离线缓存:一些网页使用Service Workers来实现离线功能或加速加载特定资源,WKWebView会缓存这些资源,以便在后续使用时能够从本地获取。

1. WKWebView缓存清理方法

清理指定数据类型的缓存:通过WKWebsiteDataStore可以清理特定类型的缓存,比如Cookies、缓存、localStorage等。该方法可以清理指定类型的缓存数据,并支持自定义时间范围。以下代码展示了如何清理WKWebView的缓存数据:

// 获取所有类型的缓存
NSSet *websiteDataTypes = [NSSet setWithArray:@[
WKWebsiteDataTypeCookies,                 // Cookie
WKWebsiteDataTypeLocalStorage,            // localStorage
WKWebsiteDataTypeIndexedDBDatabases,      // IndexedDB
WKWebsiteDataTypeWebSQLDatabases,         // WebSQL
WKWebsiteDataTypeFetchCache,              // Fetch API
WKWebsiteDataTypeDiskCache,               // 磁盘缓存
WKWebsiteDataTypeMemoryCache,             // 内存缓存
WKWebsiteDataTypeOfflineWebApplicationCache // 离线应用缓存
]];
// 获取过去时间的日期,比如一个月前
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
// 清理特定类型的数据
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes
                                           modifiedSince:dateFrom
                                       completionHandler:^{
NSLog(@"清理缓存完成");
}];


清理指定域名的数据如果只想清理某个特定网站的缓存,可以通过查询 WKWebsiteDataRecord 来实现:

// 获取指定域名的数据
[[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
                                             completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
for (WKWebsiteDataRecord *record in records) {
if ([record.displayName containsString:@"example.com"]) { // 指定域名
            [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes
                                                       forDataRecords:@[record]
                                                    completionHandler:^{
NSLog(@"清理 %@ 的缓存完成", record.displayName);
            }];
        }
    }
}];


2. WKWebView清理实践

在WKWebView的清理实践中,先计算NetworkCache文件夹的大小,一旦超过设定的限制即会触发清理流程。在清理过程中,为了保证缓存带来的性能优化和磁盘空间占用达到平衡,我们并没有选择全部删WKWebView的所有缓存,而是按照文件的修改日期进行排序,确保最老的文件在后续的清理中会被优先处理。

+ (void)cleanUpWKWebViewCacheWithLimit {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC * SOME_DELAY)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 设置缓存上限为10MB
unsigned long long cacheLimit = SOME_CACHE_LIMIT;
// 获取WebKit网络缓存目录
NSString *networkCachePath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Caches/WebKit/NetworkCache"];
// 计算缓存总大小
unsigned long long totalSize = [self calculateDirectorySize:networkCachePath];
if (totalSize < cacheDiskLimit) {
return;
        }
// 获取Records子目录路径
NSString *networkCacheRecordsPath = [networkCachePath stringByAppendingPathComponent:@"/Version 16/Records"];
// 获取所有文件列表,安时间排序判断需要清理的文件
NSArray *filelist = [self listFilesInDirectory:networkCacheRecordsPath];
if (filelist.count == 0) {
return;
        }
if (@available(iOS 9.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^{
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
NSSet *dataTypes = [NSSet setWithArray:@[WKWebsiteDataTypeDiskCache]];
                [dataStore removeDataOfTypes:dataTypes modifiedSince:SOME_DAY_AGO completionHandler:^{
// 清理完成
                }];
            });
        }
    });
}

3.3.3 dyld缓存清理

dyld是iOS和MacOS系统中的一个关键组件,负责在程序启动时加载和链接动态库(如框架和共享库)。在iOS 13及更早版本中,dyld可能会在tmp目录下创建一些临时缓存文件,用于加速后续的程序启动过程。这些缓存文件包含了动态库的加载信息,使得系统在下次启动相同程序时能够更快地找到并加载所需的库。

然而,iOS13系统之前会在每次APP升级后的首次启动生成一个新的dyld缓存,保存在tmp/com.apple.dyld,并且之前的APP版本的dyld缓存也不会自动删除。该部分缓存会随着版本升级不断累加,需要管理这部分缓存,实际上还有部分用户从 iOS13 系统升级到更高系统,这部分缓存就会一直遗留在用户手机中,高达上百 MB。iOS14 系统则是迁移到了Library/Caches/com.apple.dyld,且系统会自动清理。

/// 清理iOS13系统生成的tmp/com.apple.dyld缓存文件
+ (void)cleanTmpDyld {
NSString *tmpDyldPath = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp/com.apple.dyld"];
if ([[NSFileManager defaultManager] fileExistsAtPath:tmpDyldPath]) {
// 系统版本大于iOS14
if (@available(iOS 14.0, *)) {
            [PFMDiskSizeUtils cleanupDirectoryAtPath:tmpDyldPath];
        } else {
// 保留计数限制为 1,保留最新的
            [PFMDiskSizeUtils cleanDiskCaches:tmpDyldPath reservedCountLimit:1];
        }
    }
}

3.3.4 其他系统缓存清理

  • Documents/Inbox是iOS应用接收文件的默认目录,直接清理即可;

  • Library/Preferences目录下属于应用的临时 .plist 文件。这些文件通常由 NSUserDefaults自动生成,有时在写入或更新过程中会生成临时文件。

/// 清除 Documents/Inbox 目录
+ (void)cleanUpInboxPath {
NSString *inboxPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Documents/Inbox"];
if (inboxPath) {
        [PFMDiskSizeUtils cleanupDirectoryAtPath:inboxPath];
    }
}

/// 清除 Library/Preferences/bundleId.plist.xxxx文件
+ (void)cleanUpUserDefaultsTempPlistFiles {
NSString *preferencesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Preferences"];
NSDirectoryEnumerator *dirEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:preferencesPath];
NSString *deleteFilePrefix = [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".plist."];
NSString *file;
while (file = [dirEnumerator nextObject]) {
if ([file hasPrefix:deleteFilePrefix]) {
NSString *deleteFilePath = [preferencesPath stringByAppendingPathComponent:file];
NSDate *fileModifyDate =  [[[NSFileManager defaultManager] attributesOfItemAtPath:deleteFilePath error:nil] objectForKey:NSFileModificationDate ];
            [PFMDiskSizeUtils cleanupDirectoryAtPath:deleteFilePath];
        }
    }
}

04 总结

本篇文章围绕百度APP的磁盘清理问题,从iOS沙盒文件存储机制出发,系统性地阐述了磁盘管理的重要性和技术实现。文章探讨了自动清理与手动清理的结合策略,通过多维度触发机制和灵活的用户交互设计,平衡了系统性能与用户体验。后续我们还会分享磁盘监控和磁盘异常问题治理相关的文章。

————END————

推荐阅读

读友好的缓存淘汰算法

如何定量分析 Llama 3,大模型系统工程师视角的 Transformer 架构

微服务架构革新:百度Jarvis2.0与云原生技术的力量

技术路线速通!用飞桨让京剧人物照片动起来

无需业务改造,一套数据库满足 OLTP 和 OLAP,GaiaDB 发布并行查询能力

0 Token 间间隔 100% GPU 利用率,百度百舸 AIAK 大模型推理引擎极限优化 TPS

作者 百度Geek说
2025年1月16日 10:14

01 什么是大模型推理引擎

大模型推理引擎是生成式语言模型运转的发动机,是接受客户输入 prompt 和生成返回 response 的枢纽,也是拉起异构硬件,将物理电能转换为人类知识的变形金刚。

大模型推理引擎的基本工作模式可以概括为,接收包括输入 prompt 和采样参数的并发请求,分词并且组装成 batch 输入给引擎,调度 GPU 执行前向推理,处理计算结果并转为词元返回给用户。

  • 和人类大脑处理语言的机制类似,大模型首先会把输入的 prompt 进行统一理解,形成具有记忆能力的上下文。这个阶段通常称为 Prefill 阶段。

  • 在结束 Prefill 阶段之后,大模型引擎会根据生成的上下文不停地推断下一个可能出现的词语,如此往复循环,直到遇到停止符或者满足采样参数中的停止条件。这是一个自回归过程,通常称为 Decoder 阶段。

由于 Prefill 阶段和 Decoder 阶段所完成的任务不同,通常来讲,会从用户视角出发使用 SLO(Service Level Object): TTFT(Time To First Token)和TPOT(Time Per Output Token)去评测引擎。

  • TTFT 就是首 token 延迟,用于衡量 Prefill 阶段的性能。也就是用户发出请求之后,收到第一个词元返回的间隔,也就是系统的反应时间。对于客户来说,这个指标越低越好。

  • TPOT 就是出字间隔,用于衡量 Decoder 阶段的性能。也就是每生成两个词元之间的间隔。通常需要比人眼阅读文字的速度要快,这个指标同样也是越低越好。

当然,只用这些 SLO 并不能完全评测推理引擎对资源的使用状态,所以,和其他使用异构资源的系统一样,会使用吞吐来评测引擎对资源的使用效率,常用的指标就是极限出字率。

极限出字率 TPS(Tokens Per Second )就是系统在满载的情况下,使用所有可用的资源在 1s 内可以生成的词元的最大数量。这个指标越高,代表硬件的效率越高,可以支持的用户规模就越多。

目前市面上流行的推理引擎有很多,比如说 vLLM、SGLang、LMDeploy、TRT-LLM 等。其中 vLLM 是业界第一个完美解决了大模型不定长特性来各种问题的推理引擎,也是市面上使用最多,社区最活跃的推理引擎。

vLLM 的原创性高效显存管理、高吞吐、极易用、易拓展、模式众多、新特性支持快,社区活跃等特性是其受欢迎的原因。但是,vLLM 对复杂调度逻辑的处理没有做到极致,引入了大量的 CPU 操作,拉长了 TPOT。TPOT 的拉长会降低用户体验,降低了出字率,造成了 GPU 资源浪费。

02 影响 TPOT 的罪魁祸首 —— Token 间间隔

区别于小模型推理以 batch 为最小推理单位,大模型推理的最小单位是 step。这也是由大模型推理中自回归的特点所决定的。

每一次 step 会给 batch 内部的每个请求生成一个词元,如果有请求生成了结束符,那么这个请求将会提前结束,并且从下个 step 的 batch 中剔除,空余出来的资源将会被引擎动态的分配给其余正在排队的请求。用户可以感知到的观测指标 TPOT,就是每次 step 的执行时间。

每个 step 的执行逻辑可以简单的概括为一下两部分:前向推理和 Token 间间隔。

  • 前向推理是调用 GPU 计算资源对 Transfomer 结构进行运算的过程,是一个典型的 GPU 密集计算型任务。

  • Token 间间隔,则负责做词元拼接、结束检测、用户响应、请求调度、输入准备等工作,是典型的 CPU 逻辑密集型任务。

优化推理引擎的终极目标其实就是,极限提升前向推理的吞吐,同时极限压缩 Token 间间隔,最终提高极限出字率。****

然而,vLLM 的实现中,这两者天然存在着矛盾。极限提升前向推理的吞吐,(即充分发挥 GPU 算力)要求在适当范围内尽可能增加 batch 内的请求数。然而更多的请求数却拉长了 Token 间间隔,这样不仅会使 TPOT 拉长,还会导致 GPU 断流,出现空闲。在最差的情况下(比如 batch 为 256),Token 间间隔和前向推理时间几乎相同,GPU 的利用率只有 50%-60%。

为了提升极限出字率,同时确保高 GPU 利用率,优化 Token 间间隔成为了提升推理速度的关键。

03 百度百舸 AIAK 优化 Token 间间隔的方案

百度百舸的 AI 加速套件 AIAK 基于 vLLM ,在优化 TPOT 持续发力,并且始终保持着对社区在同周期的技术领先。

3.1 标解决方案1:多进程架构

这个方案的目标是尽可能缩短 Token 间间隔,将 detokenizer 所耗费的时间从 TPOT 中拿去。

我们发现在处理输入请求和生成返回的过程中,tokenize/detokenize 过程(token id 和字符串的转换)是完全可以独立于 GPU 推理运算的逻辑操作。

所以,我们借助 NVIDIA Triton 框架,将 tokenize/detokenize 的过程从推理流程中抽象出来作为单独的 Triton 模型部署,借助 Triton 的 ensemble 机制,把串行过程转变为 3 阶段( 3 进程)流水,实现了 tokenize/detokenize 和 GPU 推理 overlap,有效缩短了 Token 间隔时间。尽管这个优化只把 Token 间间隔中一部分 CPU 操作消除了,但是依然有将近 10% 的收益。

图片

3.2 解决方案 2:静态Slot方案

这个方案主要改造了 vLLM 的调度逻辑,全方位优化了词元拼接、结束检测、用户响应、请求调度、输入准备,提高了各个模块的并行效率,实现了对上一个方案中的「剩余部分」耗时的压缩**。**

我们发现 vLLM 的调度逻辑是面向全局视角的。也就是说每个 step 的调度都会从全局中进行重新筛选,相当于当前 step 结束之后,调度器会把当前 batch 中的句子「放回」全局请求池子中,然后在下一个 step 开始前,从这个全局池子中「取回」适当请求进行运算,这一放一取引入了额外的 overhead。

为了实现全局调度,vLLM 在词元拼接等其他环节引入了大量的 for 循环去串行的处理每个请求,由于这些操作都在发生在 CPU 上,导致在输入打包过程中,必须要引入耗时较长的 host to device 操作。

事实上,step 之间的很多信息是可以复用的(每次放回去的请求和取回来的请求很大一部分是重复的)。也正是基于这个洞见,百度百舸的 AIAK 把 GPU 每次可以迭代的 batch 当成一批固定的 slot,一旦某个请求被调度到某个 slot 后,在完成请求所有推理迭代之前,都不会被唤出,也正是有了这些固定 slot 抽象,AIAK 实现了:

  • 将全局调度改造为局部调度。也就是在下一个 step 调度时,最大程度复用上一个 step 的信息,避免全局搜索,只做增量调度。

  • 串行转并行。也正是有了 slot 的引入,词元拼接、结束检测等这些原本串行的操作可以用 CUDA Kernel 做并发处理,耗时从 ms 级别降低到 us 级别。

  • 避开 host to device 操作。输入打包的工作得以复用前序的显存,有效避开了 host to device 操作。

图片

3.3 方案 3:异步化执行

多进程架构将逻辑上容易独立的部分解耦到其他进程做流水并行,静态 Slot 方案则直面 token 间耗时问题,优化调度模式压榨各个环节的耗时。有了这两个方案,Token 间间隔已经从 35ms 降低到 14ms,GPU 的利用率已经从 50% 提升到了 75%,但是距离 100% 的 GPU 利用率和零耗时 Token 间间隔的目标还有不少距离。

百度百舸 AIAK 通过异步调度模式,将前一个方案中的「剩余部分」全部取出,最终实现了上述极限目标。

简单来讲,就是将 CPU 操作密集的 Token 间间隔和 GPU 计算密集的前向推理完全分开到两条流水线上做二级流水并行。

  • 从逻辑上来讲,核心调度逻辑摆脱了对前向推理的同步依赖,实现异步化调度。

  • 从效果上来说,GPU 避免 token 间同步导致的断流问题,处于一直繁忙状态,实现了推理过程中 100% 利用率和 0 Token 间间隔。

为了简化实现,我们将操作相对简单的前向推理当做一个任务放在后台线程中进行运行,主线程则运行核心的复杂的调度逻辑。两个线程通过一个队列进行交互,分别冲当生产者和消费者,通过线程信号量和 GPU 流上的事件进行信号同步,实现二级流互相 overlap。

图片

和其他任何使用 GPU 类似的硬件作为加速器的系统一样,追求 100% 的利用率一直是所有工程师的终极目标。百度百舸的 AI 加速套件 AIAK 在优化 TPOT,同时打满 GPU 利用率这一目标上经历漫长而又艰辛的探索,最终才彻底实现了 0 Token 间间隔和 100% 利用率这一目标。

当然,除去在这个过程中使用的诸多巧妙的优化手段外,百度百舸的 AIAK 还在量化、投机式、服务化、分离式、多芯适配等领域做了大量工作,致力于实现一个适用于全场景、多芯片、高性能的推理引擎,助力用户在「降低推理成本,优化用户体验上」更上一层楼。

————END————

推荐阅读

百度视频搜索架构演进

网页结构建模在低质采集站上的识别应用

海量存储的批量计算框架

网页多模态建模思考

百度垂搜一站式研发平台演进实践

百度视频搜索架构演进

作者 百度Geek说
2025年1月9日 10:26

导读

随着信息技术的迅猛发展,搜索引擎作为人们获取信息的主要途径,其背后的技术架构也在不断演进。本文详细阐述了近年来视频搜索排序框架的重大变革,特别是在大模型技术需求驱动下,如何从传统的多阶段级联框架逐步演变为更加高效、灵活的端到端排序框架。

01 背景

过去近十年,搜索引擎的主流框架为多阶段级联框架,分为召回,粗排,精排几个阶段。在每个阶段中,系统会基于相关性、质量、时效性和点击率等维度独立建模,然后通过模型融合这些信号进行排序和截断,最终产出检索结果。随着以BERT、ERNIE和GPT为代表的预训练大模型技术的逐渐成熟,利用一套端到端框架解决信息检索问题变得越来越可行。同时,用户差异化,多样化,深层次信息需求越来越强烈, 为了满足这些需求,系统的算力需求也在不断增加。在这种技术及需求趋势的引导下,传统视频搜索排序架构如何演变,已经成为视频搜索最重要课题,同时也对排序架构提出了重大的挑战。

02 目标

以大模型技术为主线,打造高性能,扩展灵活的视频搜索排序框架,同时完成存量排序系统的熵减治理,从而来大幅度提升排序系统的系统能力,降级系统长期运营治理成本。

03 问题与挑战

  • 架构功能如何解耦:视频搜索排序架构经历了多年的积累和发展,已经形成了策略、架构和产品逻辑高度耦合的局面。这种耦合导致排序模块承担了过多且复杂的功能,直接影响了研发效率,并频繁引发稳定性问题。此外,模块功能定位模糊,严重制约了新产品和业务的快速落地与迭代。面对这些挑战,我们亟需打破现有的陈旧框架,从更底层进行架构优化,以实现理想的业务和架构收益。

  • 系统效能如何提升: 目前核心排序模块缺少灵活高效的并行计算框架,制约系统资源使用率的提升。与此同时,系统流量低峰时段会存在大量空闲资源,没有得到充分使用,如何充分,高效挖掘这部分空闲资源资源,来满足业务对资源大量需求。

  • 端到端架构如何演进:在端到端大模型技术的引导下,排序策略的复杂性将逐步被模型内部化,现有策略实现可以得到极大的简化。传统多阶段级联排序架构如何演进升级,以适应这种新的排序模式,也是一个需要深入研究和探索的重要课题。

04 整体思路

对上述问题和挑战,我们采取了一系列综合措施来加以解决。首先,为了解决架构耦合与复杂性问题,我们对核心排序模块进行了深度重构,将原本集成在其中的召回处理与摘要计算功能独立出来,从而实现系统分层的合理化。其次,采用支持串行、并行和数据并行的灵活框架,提升视频排序流程的可视化管理和并行计算能力,并基于弹性算力分配控制中心,高效利用系统空闲资源,最大化搜索视频业务收益。最后,在大模型端到端排序模式下,推动多阶段级联框架向单阶段端到端框架转变升级。下面详细介绍以上解决方案的设计思想:

  • 核心排序功能解耦:

  • 视频核心排序模块是在线检索核心模块之一,之前承接排序和部分召回功能。累积了大量的视频独有的策略和业务逻辑,支持了视频搜索业务的不断发展。随着越来越多的策略、架构功能迭代,核心排序模块也越来越臃肿,接手、开发、维护等成本不断攀升。同时也面临例如不支持云原生、整体框架设计老旧、功能耦合严重等问题。

  1. 将排序模块中召回处理阶段独立分拆,整体功能迁移至新的视频召回模块。

  2. 利用图引擎将多Query串行执行升级至Query全并行执行,包含请求构建,Cache读取,结果解析。

  3. 常用架构,策略功能组件化,插件化,易于理解、开发和维护。

图片

△新召回模块

  • 为满足用户差异化,多样化查询需求,每次请求都需要重新进行召回,排序计算,摘要处理等阶段。如果全量穿透系统缓存,会带来巨大的资源,耗时增长,系统成本无法承担,所以需要考虑目前视频搜系统分层设计是否合理,是否需要重新设计。为解决视频个性化带来的资源,速度问题,我们对视频搜索核心排序功能进行重新分层设计:
  1. 核心排序系统结果返回和摘要获取解耦,视频排序系统有能力提供更多量结果集,弥补之前机制能力缺失的短板。

  2. 新增个性化排序模块,优化传输协议,在核心排序模块返回更多结果基础上,同时穿透更多基础排序,供个性化排序使用。

  3. 根据最终个性化排序结果集合,对Top N进行摘要处理计算,最后返回给上游模块。

    图片

△视频个性化排序演进

  • 系统效能提升:

  • 当前的视频搜索排序框架采用单线多策略管理器的串行执行模式。这种单线程串行处理方式在吞吐量和延迟方面表现不佳。此外,框架缺乏灵活的并行化配置能力,依靠人工经验引入各种omp,bthread等并行组件,并且存在历史遗留的冗余计算逻辑,架构组件较为陈旧。为了设计出能实际解决业务需求的现代引擎框架,我们对主流图引擎的特性进行了调研总结:

  1. 驱动方式:排序层当有大量算子,上千维特征时,无论数据驱动,还是人工编排,可读性都很差。这种复杂性不仅增加了理解整个排序层架构的难度,还进一步影响了项目的研发效率。

  2. 并行方式:目前主流job/processer算子并行方式,没有办法很好去支撑算子job内部并行,排序列队list/item-wise并行。排序数据通常含有多list, list内包含成百上千个item数据,这样数据处理模式需要job内部灵活的并行计算方案。

图片

△驱动&并行方式

  • 事实上,我们发现没有一套图引擎能够完全满足排序业务场景的需求。因此,我们提出了一种图框架引擎主张,灵活的支持搜索排序各个场景。
  1. 除了支持serial,paralle模式,常见的job 间的串,并行模式,框架还支持data_parallel模式。召回返回数据通常包含多list队列,list队列间要做排序,list内有成百上千个item,同样需要排序,常见并行模式不能很好解决这种排序需求,所以我们在框架层做了data_paralllel模式设计,让它契我们当前排序模式,支持list+item的混合排序模式,同时能满足各种并行场景使用需求。

  2. 对业务阶段进行清晰的stage,sub_stage抽象,相对传统图引擎算子推导,缺少很好可读的效果,我们做了stage抽象,配置可读性更好,配置即可读,排序全流程可视化管理易读易接手,这也就是我们做编排配置及推导的主要目的。

    图片

△Rankflow框架

  • 我们不仅要提升现有系统的并行计算能力,还优化资源的分配和使用方式,因为搜索系统的输入流量、资源消耗、响应时间等系统状态存在着周期性的波峰-波谷变动,而系统资源已经预先分配好。在波谷期,由于用户输入流量的减少,系统资源不会得到充分利用;而波峰期,随着用户输入流量的增多,系统往往面临着资源紧缺甚至不足的情况。于此同时,搜索系统的业务链路复杂,时常还会遭受某一中间节点的故障甚至是外部流量徒增等稳定性问题。

  • 架构方案:

  • 构建全局视角的弹性算力分配控制中心。

  • 通过对集群各种维度指标的获取、策略分析及周期性执行最适合当前机器负载状态的策略组合参数,实现其核心弹性算力分配决策。

  • 业务应用:

  • 目前支持视频搜索短小视频扩触发,高峰减载,系统异常处置等功能。

图片

△智能弹性算力系统

  • 端到端排序架构升级:

  • 视频核心排序模块主要分为粗排,精排级联两阶段,排序策略是依据这两阶段排序模式进行迭代升级,如粗排阶段完成初步相关性计算用于初步筛选,减少精排阶段系统计算量,精排阶段少量优质结果进行复杂计算。以大模型排序为核心的排序框架打破了原来多阶段级联模式,端到端排序框架需要对计算和数据方案进行重新设计。

  1. 精简精排前调权和挖掘队列策略,优化索引召回和模型计算选送逻辑,粗排和精排阶段统一为粗精排一体化排序阶段。

  2. 由于缺少粗排模型提前初筛作用,端到端模型需要计算数量更多的候选结果集,计算候选集合从原来精排阶段的几十条增加到几百条。

  3. 升级精排模块,利用Rankflow框架,高并发处理候选结果集数量增加带来的耗时问题。

图片

△端到端排序架构

05 总结与展望

视频搜索排序框架通过系统分层优化、Rankflow框架引入及弹性资源复用等架构演进,显著提升了排序系统的性能与灵活性,提高研发效率,降低了长期运营成本。

  • 在大模型技术趋势下,视频搜索系统如何更好提供RAG搜索增强功能。

  • 如何使视频与通搜端到端融合,达到搜索端到端理想态,都是我们后续探索研究的方向。

————END————

推荐阅读

网页结构建模在低质采集站上的识别应用

如何定量分析 Llama 3,大模型系统工程师视角的 Transformer 架构

微服务架构革新:百度Jarvis2.0与云原生技术的力量

技术路线速通!用飞桨让京剧人物照片动起来

无需业务改造,一套数据库满足 OLTP 和 OLAP,GaiaDB 发布并行查询能力

网页结构建模在低质采集站上的识别应用

作者 百度Geek说
2025年1月7日 10:41
导读 百度搜索是全球最大的中文搜索引擎,拥有着几十亿级的流量,作弊团伙通过各种各样的手段妄想从巨大的流量中不劳而获。搜索反作弊团队维护百度搜索生态安全和质量,经过不断探索并利用前沿技术过滤低质作弊网页

海量存储的批量计算框架

作者 百度Geek说
2024年12月31日 10:25

导读

本文介绍了百度针对海量存储数据计算需求研发的HTAP表格存储系统及计算调度架构。项目背景源于原有存储系统难以满足日益增长的OLAP业务需求,因此构建了集OLTP与OLAP于一体的HTAP系统,通过存算分离、Serverless设计等创新点提升IO访问能力和资源利用率。同时,自研的计算与调度系统实现了任务开发的SQL化和数据处理的FaaS化,简化了业务使用成本,提高了开发效率。整体方案在存储成本、IO能力、IO放大率等方面取得显著成果,为海量存储数据的计算提供了高效、灵活的解决方案。

01 项目背景及目标

1.1 项目背景

搜索内容存储团队主要负责各类数据,如网页、图片、网页关系等,的在线存储读写(OLTP)、离线高吞吐计算(OLAP)等工作。

原有架构底层存储系统普通采用百度自研表格存储(Table)来完成数据的读、写、存工作,此存储系统更偏向于OLTP业务场景。随着近几年大数据计算、AI模型训练的演进,对存储系统OLAP业务场景的依赖越来越重,如数据关系分析、全网数据分析、AI样本数据管理筛选。在OLTP存储场景的架构下,支持OLAP存储需求对资源成本、系统吞吐、业务时效带来了巨大挑战。为此我们在百度自研表格存储之外,结合业务实际workflow针对性优化,增加构建了一套符合业务需求的HTAP表格存储系统以及相应的计算框架,共同组成面向海量存储数据的大批量计算架构系统。

1.2 项目目标

  • 提供海量存储数据计算的超高IO访问能力。当前内容存储数据达几十P+,访问频率按照每周一轮估算,平均IO能力需要达到34G/s,峰值IO能力需要达到200G/s。面对如此庞大的IO访问能力,需要从文件系统、存储引擎、分布式存储系统、访问模型等全方位进行深度优化来满足需求;

  • 提供海量存储数据计算的快速开发&部署能力。在提供海量访问能力的同时,也需要为业务提供访问配套的基础设施,来满足业务开发&部署计算任务的需求。

02 现有研发条件和工作基础

搜索内容架构存储团负责各类数据,如网页、图片、网页关系等,的在线存储读写(OLTP)、离线高吞吐计算(OLAP)等工作。面对当前海量存储数据的计算需求有清晰的技术和业务认知,第一视角明确清楚地知道系统瓶颈、技术难点、业务需求。

  • 系统瓶颈——当前存储系统能提供的IO能力与业务计算需求之间的矛盾。随着大数据、机器学习、大语言模型等新技术的兴起,业务对数据的计算访问需求越来越强烈,然而存储系统的IO能力却一直止步不前。为此,迫切需要一款面向数据计算的存储系统;

  • 技术难点——数据表格存储系统的数据访问模型与计算模型之间的矛盾。当前架构底层存储普遍采用百度自研表格存储(Table)来完成数据的读写存工作,此存储系统更偏向于OLTP业务场景。但随着近几年大数据计算、AI模型训练的演进,对存储系统OLAP业务场景的依赖越来越重,如数据关系分析、全网数据分析、AI样本数据管理筛选。在OLTP存储场景的架构下,支持OLAP存储需求对资源成本、系统吞吐、业务时效带来了巨大技术挑战;

  • 业务需求——方便高效快速的任务开发&部署能力的需要。在大量搜索内容OLAP workflow中,从表格存储系统中提取筛选数据只占全部任务的一小部分,大量任务需要对数据进行加工处理得到需要的结果。常规的做法是多任务串联,这样做的缺陷是大量中间临时数据存储开销。为此我们为HTAP表格存储系统构建了一套计算与调度系统。

03 整体方案

3.1 概览

本项目拟研发面向海量存储数据的大批量计算架构,主要分为两大系统,HTAP表格存储系统、计算&调度架构。

3.1.1 HTAP表格存储系统

图片 △图2.3

  • 架构采用业界HTAP主流设计思想,将OLTP和OLAP workflow拆分到两套存储系统中,如F1 Lightning、ByteHTAP,在SDK层根据任务类型分发到不同的存储系统中;

  • OLTP存储系统——Neptune,采用Multi-Raft分布式协议组建存储集群,采用本地磁盘(SSD/HDD等) + 百度分布式文件系统AFS组成存储介质;

  • OLAP存储系统——Saturn,Serverless设计模式,无常驻Server,即用即加载,贴合OLAP workflow的不确定性和间歇性;

  • OLTP与OLAP存储系统间,采用数据文件硬链的方式进行数据同步,全版本替换,成本低、速度快,充分贴合Saturn Serverless设计模式。

如上架构设计图,可将OLTP与OLAP workflow拆分到两套独立的系统中,解决上述提到的存算耦合问题。

  • 解决存储空间放大问题。空间放大主要带来的问题是存储节点成本,Workflow分离的架构将OLAP需要的数据文件采用AFS低成本存储,减少了对存储节点存储空间的压力。

图片 △图2.4

OLAP存储系统的数据写入并没有使用常见的log redo或raft learner模式,最主要还是在保证OLAP存储系统的Serverless特性的同时,又能实时感知到OLTP系统的最新写入结果。

  • 解决存储节点资源冗余问题。拆分后,分布式存储节点将大量重型OLAP workflow转移到OLAP存储——Saturn中,将极大减少存储节点的计算压力。同时,OLAP存储的Serverless设计模式又可贴合workflow的不确定性和间歇性。

图片

△图2.5 Saturn Serverless模型

计算节点可以部署在任意计算集群中,如Map-Reduce、自研计算节点Pioneer等,在SDK中直接初始化存储引擎,从AFS中访问对应分片的数据文件。计算节点可充分利用云原生系统(PaaS)的弹性资源,解决资源常驻冗余问题。

3.1.2 一次开发,多端部署

图片

  • 任务生成。自研KQL数据查询语言。在任务生成阶段将KQL语句解析优化成相关的调度任务,一个Job包含多个Task。

  • 任务调度。

  • 任务调度的计算节点可以是Map-Reduce,也可以是自研计算集群Pioneer,负责不同计算场景。

  • 任务运行容器负责数据依赖部署和运行计算框架。

  • 计算框架采用插件化设计思想,依托KQL语言进行差异化描述。计算框架的最大特点是,可在数据处理节点执行用户自定义FaaS函数。

3.2 详细介绍

3.2.1 HTAP表格存储系统

3.2.1.1 OLTP存储系统——Neptune

图片

Neptune引擎主要支持四类操作:写、删、读、Scan。每一类操作都通过RegionMapper进行映射,对外隔离分区概念。

Neptune存在两类分区:索引分区、数据分区。

  • 索引分区。索引分区用于减少因为数据分区导致Key所在数据分区不明确导致的随机访问IO放大问题,提升随机查性能。

  • 数据分区。Neptune可配置多个数据分区,每个数据分区内包含多个Locality-Group。分区间的数据理论上是互斥的。

Neptune各类操作的流程:

  • 写操作:

  • 根据RowWriter中设置的Region信息找到需要写入的Region的Handle,按照列语义将数据序列化成RawData。

  • 同时根据Region信息生成当前Key的Region索引信息。

  • 将RawData与RegionIndex作为一条操作记录Commit到引擎中,整个操作为原子操作。

  • 删操作:

  • 由于存在Region的概念,删除某个Key是需要明确当前Key所在的分区。目前的做法是查询一遍分区索引获取分区信息,再准确删除对应分区的数据。这样带来一个问题,删除操作会增加一次分区查询操作,我们可以考虑将分区信息全部加载到内存提升性能。

  • 读操作:

  • 读操作类似删除操作,会首先查询分区索引表,如果在分区索引中查询不到则表明当前Key不存在,直接返回NotFound。否则,根据分区索引查询对应的分区即可。

  • Scan操作:

  • Scan时业务可以指定对应的分区以及CF信息,RegionMapper根据这些信息Select出合适的物理存储Handle,然后对这些物理存储进行Scan。

3.2.1.2 OLAP存储系统——Saturn

图片

Saturn主要分三层:文件系统(File-System)、Table(表级别的抽象,非TG的Table)、访问层(SDK),Meta-Server为每一层提供全局Meta信息支持。

  • 文件系统。Saturn既可以支持AFS,也支持本地文件系统,同时后续可以支持其他类型的文件系统。文件系统的类型对于Saturn来说是插件化可插拔的。使用AFS作为文件系统相比于Table在成本层面有巨大优势。

  • Table。一个抽象的Table包含多个Slice,理论上每个Slice间的数据是互斥的,这里引入数据模型的概念。当前支持两种数据模型:哈希序(hash order)、全局序(global order),两种模型与Table完全对等。

  • SDK。SDK目前支持Seek和Scan功能,使用方式跟通用的列存储系统保持一致,SDK直接与文件系统(AFS)连接,对外提供存储Serverless的访问能力。

同时,Table数据的更新和构建包含两种模式:全量构建、增量合并

  • 全量构建。全量构建通过完整Dump Table数据的方式对表中的每个分片进行逐步替换,替换过程中采用多版本机制保证访问的稳定性。

  • 增量合并。增量合并通过控制TG Table做Major Compaction的时机,保证每次获取增量数据前不会发生Major Compaction。增量数据通过Snapshot的形式对外提供所有的操作记录,这些记录保存在Table SST文件中,Saturn把这些SST文件Transform成自身协议的SST,再发起Ingest操作即可。

3.2.1.3 存储引擎优化——数据行分区

数据行分区思想在很多OLAP存储系统中很常见,如当前比较流行的一些数据湖架构,ClickHouse、IceBerg等。在表格存储中,数据行分区的好处是可以极大减少在数据行筛选过程中IO放大率。以下是我们在存储引擎中支持数据行分区的设计思路:

图片

△图2.6

数据行分区的思想在OLTP和OLAP存储引擎中都有使用,OLTP存储引擎以数据行分区构建的数据文件可直接被OLAP存储引擎加载,减少了OLAP存储的数据构建工作。

数据行分区在Write、Read、Scan场景下的处理流程分别为:

  • Write操作。Write时会根据请求中的特殊Region描述,如分区键,找到需要写入的Region-Index和Region上下文,前者保存Key的分区索引信息,后者中保存实际数据,操作记录由WAL中保存。

  • Read操作。Read操作相比通常直接访问数据,需要多进行一次分区索引访问,为减少多一次访问带来的性能折损,我们将分区索引信息全内存化。由于索引数据非常小,因此全内存化是可接受的。

  • Scan操作。Scan操作相比之下没有任何变更,但在Scan特殊分区场景下可大量减少IO放大。因为相比之前的行过滤模式,可直接跳过大量不需要的数据。

在业务存储支持时,合理设置数据行分区,可极大减少数据行筛选过程中的IO放大率。

3.2.1.4 存储引擎优化——增量数据筛选

在实际业务中,有很大一个场景是获取近期(如近几个小时、近一天)有值变化的数据,常规的做法是Scan全量数据,以时间区间作为过滤条件,筛选出符合条件的结果。但如此的筛选逻辑会带来严重的IO放大,因为满足条件的结果只占全量结果的一小部分。为此,我们在引擎层调整优化Compaction时机以及调整筛选流程,减少增量数据筛选过程中需要访问的数据文件集合,降低IO放大,业务提速。

图片

△图2.7 LSMT

3.2.1.5 存储引擎优化——动态列结构

在OLAP存储引擎中,还存在一类访问场景会带来IO放大问题,数据列筛选。在表格存储系统中,一个Key可以包含多个列族(Column Family),一个列族中可以包含任何多个数据字段,这些字段以行结构存储在同一物理存储(Locality Group)中,当筛选特定数据列时,需要进行整行读取,然后过滤出需要的字段,这也将带来IO放大问题。

同时,OLAP workflow的访问不确定性导致存储层无法及时调整数据在物理存储中的结构。为此,我们引入动态列结构的概念,在逻辑层对业务透明,在物理层根据近期OLAP workflow特性及时调整物理结构。

图片

△图2.8

如上图,在逻辑存储中,分为两个LG,根据workflow特性,把业务常用的访问字段在Compaction阶段存放在同一物理存储结构中,反之,这样可以减少字段筛选阶段的IO放大率。

动态列结构只在OLAP存储引擎中生效,我们在原有OLAP存储中引入workflow收集以及compaction任务,将从OLTP存储中同步的数据构建成更适合OLAP场景的存储结构。

3.2.2 计算与调度架构

在本节,我们将介绍在此HTAP表格存储系统基础上,如何设计实现任务计算和调度系统,简化业务使用成本,提升业务效率。

在大量搜索内容OLAP workflow中,从表格存储系统中提取筛选数据只占全部任务的一小部分,大量任务需要对数据进行加工处理得到需要的结果。常规的做法是多任务串联,这样做的缺陷是大量中间临时数据存储开销。

为此我们为HTAP表格存储系统构建了一套计算与调度系统,系统两大特点:任务开发SQL化、数据处理FaaS化。

3.2.2.1 SQL化与FaaS化

我们充分贴合上述存储系统特性,自研了一套数据查询语言——KQL,KQL类似于SQL Server语法。同时,又结合存储系统特性以及计算框架,支持一些特殊语言能力,最主要的是能支持原生FaaS函数定义,当然也支持外部FaaS函数包依赖。

如下是一段KQL语句例子以及说明:

function classify = { #定义一个Python FaaS函数
def classify(cbytes, ids):
    unique_ids=set(ids)
    classify=int.from_bytes(cbytes, byteorder='little', signed=False)
    while classify != 0:
        tmp = classify & 0xFF
        if tmp in unique_ids:
            return True
        classify = classify >> 8
    return False
}

declare ids = [2, 8];
declare ts_end = function@gettimeofday_us();      # 调用Native Function获取时间
declare ts_beg = @ts_end - 24 * 3600 * 1000000;   # 四则运算

select * from my_table region in timeliness       # 利用存储分区特性,从my_table中的timeliness分区获取数据
where timestamp between @ts_beg and @ts_end       # 利用存储增量区间特性,筛选增量数据
    filter by function@classify(@cf0:types, @ids) # 在Filter阶段调用自定义FaaS函数
    convert by json outlet by row;
desc:                                             # 对计算框架进行特殊描述
    --multi_output=true;

3.2.2.2 任务生成与调度

图片

任务生成与调度主要分为三层,任务解析层、任务调度执行层、任务执行容器。

  • 任务解析层。负责将KQL表达式解析成实际的任务执行计划,并保存在任务存储容器中。

  • 任务调度执行层。负责将任务计划分发到任务执行容器,并轮训检测任务状态,执行探活、重试等操作。

  • 任务执行容器。提供两种任务执行容器,Pioneer、EMR。前者为自研任务执行容器,后者为公司Map-Reduce执行平台。

3.3 技术经济指标

通过上述的架构设计以及优化手段,我们在IO能力、访问成本、开发效率等方面取得显著成果。

04 主要创新点

4.1 自研HTAP表格存储系统

结合业务特性以及实际需求,构建符合业务场景的HTAP存储系统。架构采用业界HTAP主流设计思想,将OLTP和OLAP workflow拆分到两套存储系统中,如F1 Lightning、ByteHTAP,在SDK层根据任务类型分发到不同的存储系统中。系统创新点如下:

  • 存算分离架构。解决OLTP存储系统的空间放大问题,将OLAP Workflow从OLTP存储中分离,分离的架构将OLAP需要的数据文件采用AFS低成本存储,减少了对存储节点存储空间的压力。

  • OLAP Serverless设计。分布式存储节点将大量重型OLAP workflow转移到OLAP存储——Saturn中,将极大减少存储节点的计算压力。同时,OLAP存储的Serverless设计模式又可贴合workflow的不确定性和间歇性。计算节点可以部署在任意计算集群中,如Map-Reduce、自研计算节点Pioneer等,在SDK中直接初始化存储引擎,从AFS中访问对应分片的数据文件。计算节点可充分利用云原生系统(PaaS)的弹性资源,解决资源常驻冗余问题。

  • 表格数据行分区。数据行分区思想在很多OLAP存储系统中很常见,如当前比较流行的一些数据湖架构,ClickHouse、IceBerg等。在表格存储中,数据行分区的好处是可以极大减少在数据行筛选过程中IO放大率。

  • 增量数据筛选支持。在实际业务中,有很大一个场景是获取近期(如近几个小时、近一天)有值变化的数据,常规的做法是Scan全量数据,以时间区间作为过滤条件,筛选出符合条件的结果。但如此的筛选逻辑会带来严重的IO放大,因为满足条件的结果只占全量结果的一小部分。为此,我们在引擎层调整优化Compaction时机以及调整筛选流程,减少增量数据筛选过程中需要访问的数据文件集合,降低IO放大,业务提速。

  • 表格数据动态列结构。根据workflow特性,把业务常用的访问字段在Compaction阶段存放在同一物理存储结构中,反之,这样可以减少字段筛选阶段的IO放大率。动态列结构只在OLAP存储引擎中生效,我们在原有OLAP存储中引入workflow收集以及compaction任务,将从OLTP存储中同步的数据构建成更适合OLAP场景的存储结构。

4.2 自研任务生成与调度系统

在大量搜索内容OLAP workflow中,从表格存储系统中提取筛选数据只占全部任务的一小部分,大量任务需要对数据进行加工处理得到需要的结果。常规的做法是多任务串联,这样做的缺陷是大量中间临时数据存储开销。

为此我们为HTAP表格存储系统构建了一套计算与调度系统,系统两大特点:任务开发SQL化、数据处理FaaS化。

  • SQL化。我们充分贴合上述存储系统特性,自研了一套数据查询语言——KQL,KQL类似于SQL Server语法。

  • FaaS化。在SQL化的基础上,同时结合存储系统特性以及计算框架,支持原生FaaS函数定义能力,当然也支持外部FaaS函数包依赖。

————END————

推荐阅读

网页多模态建模思考

百度垂搜一站式研发平台演进实践

初探图谱Embedding用于异常检测(一)

AIAPI - 转向AI原生检索

学校新来了一位AI作文老师:能看、会评、还教改写

百度垂搜一站式研发平台演进实践

作者 百度Geek说
2024年12月24日 10:36
导读 百度垂搜架构历经多年发展,内部沉淀了多个开发者平台\工具,涉及覆盖了搜索系统的多个阶段模块,如何高效地串联系统全流程,为业务提效提质,可靠的工程化基建和更上层的抽象设计是关键。本文阐述了百度垂搜

AIAPI - 转向AI原生检索

作者 百度Geek说
2024年12月17日 11:04
导读 大型语言模型(LLMs)展示了非常强大能力,但在实际应用中仍旧有一些问题需要解决,比如幻觉现象、在垂类细分场景下的知识更新较慢,以及在回答中缺乏透明度(模型黑盒问题)等。检索增强生成(RAG)是

学校新来了一位AI作文老师:能看、会评、还教改写

作者 百度Geek说
2024年12月12日 10:31

“学生怕写作文,老师怕教作文。”一句教育行业中盛行的调侃道出了语文教学的“难题”。教了34年语文课的特级教师唐晴晓对此不无理解,因为在她的教学生涯中,熬夜给学生批改作文几乎是家常便饭。

作文教学是发展学生观察力、思维力、想象力的重要途径,由于作文的内容形式特殊,需要教师花费成倍的时间精力,对全班作文进行针对性批改和整体性分析备课。但紧张的教学安排下,现实情况往往是语文老师作文批改工作量积压,学生也难以得到充分指导。即便在学校普遍推行机器阅卷的当下,作文也犹如数字化的“漏网之鱼”,未能获得技术提效。但如今借助 AI,“难题”有了新解法。

在人工智能与教育领域相结合的多年技术探索中,厦门笔杆子教育科技有限公司(下称“厦门笔杆子”)逐渐注意到,语文作文是教师们工作量最大、个性化教学需求最迫切的方向。基于百度飞桨和文心大模型的助力,厦门笔杆子进一步优化其产品“智能作文教学平台”,实现作文的个性化打分、点评、指导和系统化分析等能力。

图片

△智能作文教学平台实现从作文批改、讲评到归档的全流程数字化管理

01 作文批改之困:耗费精力多,学生进步慢

“按照教学计划,每1-2周要布置一篇作文,每次只是完成批改就至少需要花2天时间。”执教34年的时间里,厦门市海沧区育才小学特级教师唐晴晓在作文教学上消耗的精力,始终处于各项教学任务中的前列。作文批改不仅是看卷面整洁与否,更需要在标点符号、错别字、遣词造句、语法应用、段落结构、主题立意等多方面进行综合考量。老师会在阅卷后给出分数、圈出错字、写下评语,一份完整的作文批改才算完成。粗略统计下,每篇作文花5分钟左右的时间批阅,每个班四五十名学生,老师一般很难做到连续批改,即便挤出时间,至少也要六七个小时以上才能全部批改完毕。

而且实际操作中,作文的批阅质量难以保证篇篇精细。唐老师解释道,作文评改高度依赖教师个人经验和感觉,评价难以保持完全的客观和科学。在时间精力有限的情况下,很难做到面面俱到、细致入微地评改。很多时候,学生最终拿到的只有一个分数及简单评语。

对于学生来说,本周完成的作文练习,等到下次上课讲解已经过去一两周时间,写作思路印象早已不深。唐老师表示,学生获得批改反馈慢,课堂讲解也往往无法针对学生个人情况,因此在作文改进效果和积极性上都会受到影响。

而对于教师来说,传统的人工批改作文后,只能对班级作文情况形成主观上的印象,对整体情况的把握缺乏数据支持,更无法在具体的问题上进行例证和教学上的延展。

图片

△厦门市海沧区育才小学学生再作文课上进行习作练习

如今,即便数字化阅卷已普及大多数学校,语文教师们仍然困在作文批改的难题中。“大模型技术发展之前,智能作文教学平台其实已经可以实现作文打分和好词好句的标注,但在学校中间的推广使用一直较为缓慢,我们调研后发现主要问题出在批改评语的质量上。”厦门笔杆子 CEO 成硕介绍,作文没有标准答案可以直接对照判对错,阅卷时需要识别好词好句、并针对具体内容写出针对性评语和改进建议,这都需要对文本具备深度理解能力。起初,厦门笔杆子在产品中使用了一套自己开发的评语模板,但老师在使用中反馈评语机械化模板化,使用感受和效果欠佳,“而这也正是生成式 AI 与判别式 AI 的差异体现”。

02 飞桨+文心“双管齐下”作文批改提速6倍

去年,百度飞桨(厦门)人工智能产业赋能中心入驻厦门,致力于为企业提供 AI 原生应用孵化、AI 人才培养服务,厦门笔杆子与百度的合作也因此进一步加深。依托飞桨文字识别套件 PaddleOCR 和文心大模型优秀的理解、生成、逻辑、记忆能力,厦门笔杆子全新升级了智能作文教学平台功能。

PaddleOCR 支持多种 OCR 任务,包括文字检测、文字方向检测、多语种 OCR(支持约80种语言)、手写体 OCR 等,可以在各种复杂场景下实现高精度的 OCR 识别,具有高精度、高效率、易用性和开源免费的特点。开发者可通过简单易用的 API 接口和开发文档,进行二次开发和定制化部署。基于这一技术,即便是学生们不易辨认的手写体,也可被精准识别用于后续数据分析。

图片

△通过手机拍照或机器扫描后进行作文识别

基于文心大模型优秀的自然语言理解和生成能力,智能作文教学平台可以对作文内容进行快速摘要,并进行整体优缺点评价。判断字词句段的质量后,结合自有数据库生成针对性评语或优化建议,甚至连示范内容也一并给出,辅助老师高效批改。“文心大模是国内较早成熟的一批大模型,对中文的支持和理解能力都很优异,尤其老师使用后反馈生成内容十分人性化,甚至难以辨别出人工与大模型生成内容的差异。”成硕介绍,随着文心大模型的技术迭代,智能作文教学平台始终同步进行优化,也正是由于百度文心大模型技术团队持续、及时的支持,使得双方合作始终顺畅进行。

图片 △作文批改情况

大模型技术刚开始接入时,不少老师持谨慎态度。唐老师回忆,有同事担心适应 AI 要花费更多学习成本;也有同时怀疑大模型批改后效果不佳。但不到3个月试用后,老师们纷纷改观。原来6小时左右的工作量,现在有了 AI 辅助,不到1小时就能完成,作文批改提速至少6倍,当天上交的作文当天就能拿到成绩反馈,老师也有更多时间进行针对性、个性化的讲解和辅导。

成硕介绍,智能作文教学平台不仅是给老师们阅卷端使用,学生也能利用这个平台提升自己的作文水平。“很多孩子会催促家长使用手机在平台上传作文,看到打分和点评后,自己修改完、再拍照、再上传,不断去优化作文来拿到更高的分数。”唐老师介绍,以前除非老师要求,否则学生很少有动力再写一遍。“但现在很短时间就给出反馈,学生会更积极,作文成绩也在反复打磨中进步得很快。”

图片 △学生在家长辅助下可通过手机端查看作文批改情况

能实现这样的效果源于厦门笔杆子扎实的数据积累和技术研发路径选择:

  • **构建作文写作知识库:**厦门笔杆子搜集了40余万篇学生原稿作文、100多万条教师的写作评语,依托260余个中文语料库,结合54个写作技法检测规则库,经数据脱敏后通过文心大模型“语义向量”能力对数据进行向量化处理,构建了作文写作向量知识库。

  • **PaddleOCR 精准识别:**作文通过拍照或扫描上传后,依托飞桨的文字识别开发套件 PaddleOCR,对照片中的手写文字进行精准识别,并转换为数据等待读取。

  • 拆解文章结构并评分:基于自然语言处理(NLP)将识别后的数据进行段、句、词到字的拆解,并依次与对应向量数据库进行比对。评分系统基于文章结构、方法技巧、立意构思、语句修辞等50个以上的评分维度给出评分。教师可在后台自主调节每次作文的评分维度及权重。

  • **文心大模型生成评语:**基于文心大模型“对话补全”能力,根据文章水平和具体内容,给出针对性的作文评语,指出文章优点,给出修改建议和示范。

图片

△厦门笔杆子研发团队

目前,该产品已覆盖福建、江苏、浙江、湖北等省份500多所中小学,越来越多学校拥有了这样一位“能看、会评,还教改写”的 AI 作文老师,作文批改提效达6倍以上。语文教师得以从大量的批改作文工作中脱身,在作文教学内容和方法上做更多探索,孩子们也因此获得快乐学习的动力。

03 AI赋能教育:避免千篇一律,真正因材施教

人工智能的介入会不会让学生作文变得千篇一律?

成硕毫不犹豫地给予了否定的答案。人工智能可以更好地照顾学生的心理需求,一视同仁地在每一篇作文里发掘亮点和优点,给予学生肯定。让每个孩子在自己的作文中能获得独一无二的认可和针对性的优化建议。

对于这个问题,已经将 AI 融入日常教学的老师们心态更加积极。对他们来说,AI 更像是小助理,个性化评阅报告可以为每个学生生成作文专属的详细反馈建议,他们也能够在 AI 评阅报告的基础上,对每个学生的状况进行更精准地把握,进一步进行剖析,帮助学生成长。

在智能作文教学平台的辅助下,老师在写作教学中可以不再重点讲解“记叙文六要素”“借景抒情”等基础性知识和技能,这些从文心大模型给出的评语示范中就能获得。教师可把重点放在引导学生进行个性化选材,分析比较、评价和鉴别智能作文的优缺点上,并在使用 AI 过程中,促进高阶思维能力形成,增进具有个人风格的言语表达。

图片

△语文老师在作文课上展示全班作文的AI分析情况

今年9月26日,教育部副部长王光彦在国务院新闻办发布会上强调,要充分发挥人工智能这把“金钥匙”的作用,深入实施人工智能赋能教育行动,建立基于大数据和人工智能支持的教育评价和科学决策制度,增强师生数字素养,助力教育变革。百度也将持续发挥文心大模型的技术优势,为以厦门笔杆子为代表深耕教育领域的企业提供全面支持,推动企业产品优化迭代,用人工智能的力量真正把“因材施教”照进现实,让教育更加个性化,让更多学生感受到语言文字之美,收获学习的快乐和成长的满足。

————END————

推荐阅读

AI Agent重塑微服务治理

百度智能云千帆大模型平台引领企业创新增长

轻松搞定平稳运行,数据库平台 DBStack 帮助 DBA 运维不同基础设施上的各类数据库

基于PP-ShiTuv2新增PaddleX图像识别模型产线,显著提升商品识别等细粒度开放域产业场景检索性能

❌
❌