B站用户行为分析架构演进与ClickHouse优化实践
1. B站用户行为分析的技术挑战与架构演进
B站作为国内领先的视频社区平台,每天产生的用户行为数据量达到数千亿行级别。这些数据包括视频观看、弹幕发送、点赞收藏、分享评论等丰富的行为类型,构成了一个极具价值的用户行为分析宝库。面对如此庞大的数据规模,传统的数据分析架构面临着三大核心挑战:
首先是存储压力。半年以上的历史行为数据需要存储PB级别的压缩数据,这对存储系统的扩展性和成本控制提出了极高要求。其次是查询性能。用户行为分析需要支持多维度实时查询,包括事件分析、漏斗分析、路径分析等复杂场景,传统方案往往需要分钟级响应。最后是资源效率。如何在保证分析能力的同时降低计算资源消耗,成为技术团队必须解决的难题。
B站的北极星用户行为分析平台经历了三个主要的技术架构演进阶段:
2019-2020年采用的是"部分模型化聚合+Spark Jar任务"架构。这个阶段通过预定义的Spark作业处理特定分析场景,虽然功能上能够实现基本分析需求,但存在明显的性能瓶颈。单事件分析需要超过3分钟返回结果,漏斗和路径分析甚至需要30分钟以上,严重影响了产品可用性。
2020-2021年升级为"无模型化明细+Flink+ClickHouse"架构。这一代架构引入了ClickHouse列式数据库,直接对用户行为明细数据进行实时分析。性能得到显著提升,90%的事件分析查询可以在5秒内返回,漏斗分析也缩短到30秒内。然而这种架构对资源消耗极大,Flink任务峰值需要1200个计算核心,存储几经扩容后仍面临不足10%剩余空间的窘境。
2021年至今发展为"Iceberg全模型化聚合+ClickHouse"的混合架构。这一架构创新性地结合了数据湖技术与OLAP引擎的优势,通过全模型化聚合将每天千亿级数据压缩到百亿级别,同时实现了秒级查询响应。实时资源节约了1400个计算核心,存储资源节省40%,查询效率提升60%以上。
2. ClickHouse在用户行为分析中的核心优势
ClickHouse之所以成为B站用户行为分析的核心引擎,主要基于其在海量数据分析场景下的四大独特优势:
列式存储结构天然适合分析场景。用户行为分析通常只需要查询少数几个字段,而不需要整行数据。ClickHouse的列式存储避免了读取无关字段带来的IO浪费,配合高效的压缩算法(如ZSTD),可以将存储空间减少30%以上。在实际测试中,B站采用ZSTD(1)压缩算法后,存储空间节省显著,而查询性能基本不受影响。
向量化执行引擎大幅提升计算效率。ClickHouse的查询执行不是传统的逐行处理,而是采用SIMD指令对数据块进行批量操作。这种设计特别适合用户行为分析中的聚合计算,如UV统计、留存率计算等场景。在B站的实践中,向量化引擎使得单节点每秒可以处理数亿行数据的聚合运算。
原生支持RoaringBitmap数据结构。用户行为分析中常见的交并差集计算(如计算同时完成多个行为的用户群)通过RoaringBitmap可以得到极致优化。B站利用这一特性实现了高效的标签人群圈选功能,将原本需要分钟级计算的复杂人群筛选优化到秒级响应。
分布式能力与线性扩展。ClickHouse的分布式表引擎可以透明地将数据和查询分布到集群各节点。B站通过合理的数据分片策略(如按user_id哈希分片),实现了计算资源的线性扩展。当数据量增长时,只需增加节点即可保持查询性能稳定。
3. 海量数据导入与重平衡实践
3.1 数据导入方案的三次迭代
B站在ClickHouse数据导入方案上经历了三个阶段的演进:
最初的JDBC写入方案直接通过Flink任务将数据写入ClickHouse。这种方式实现简单,延迟可以控制在秒级,但存在明显的资源竞争问题。数据排序、索引生成和压缩都在ClickHouse服务端完成,高峰期会严重影响查询性能。同时高频的小批量写入导致大量merge操作,产生"写放大"效应,消耗额外30%以上的磁盘IO和CPU资源。
第二阶段采用基于HDFS的BulkLoad方案。这一方案的核心创新是将data part文件的生成过程转移到Spark端完成,利用YARN集群资源进行数据预处理。生成的文件上传到HDFS后,ClickHouse通过改造过的FETCH语句拉取并attach。实测显示,这种方案将写入对查询的影响降低了90%,merge压力减少70%以上。但HDFS作为中间存储带来了额外的网络开销和存储成本。
当前阶段实现了直达ClickHouse的BulkLoad方案。通过开发DataReceive服务,Spark Executor可以直接将data part文件传输到ClickHouse节点,完全绕开HDFS。这一优化使导入性能再提升100%,同时系统稳定性得到进一步增强。DataReceive服务实现了完善的流量控制机制,单节点可支持每秒GB级别的数据传输。
3.2 数据重平衡的自动化实践
随着数据量持续增长,ClickHouse集群需要定期扩容。B站开发了一套自动化重平衡工具,主要解决以下问题:
平衡度量化通过变异系数实现。该指标消除了数据绝对大小的影响,能够客观反映表在各节点的分布均匀程度。计算公式为:变异系数=标准差/平均值。当该值超过0.3时,系统会自动触发平衡操作。
针对不同场景提供两种平衡算法:
- 装箱算法:追求极致均衡,采用Best Fit策略配合AVL树优化,适合数据量相对较小的表
- 贪心算法:以最小迁移成本达到较优均衡,通过轮询实现局部最优,适合PB级大表平衡
平衡执行流程设计了完善的原子性保证机制。采用"预判断+fetch+detach+attach+drop detached"的管道式操作,配合异常重试和回滚,确保即使在网络抖动等异常情况下也不会出现数据丢失或重复。同时通过max_replicated_fetches_network_bandwidth参数实现精细化的流量控制,平衡过程对查询性能的影响控制在5%以内。
4. 核心分析功能的实现与优化
4.1 事件分析与留存计算
事件分析是用户行为分析的基础功能,B站通过多级聚合模型实现了高效查询:
第一层是DWD明细数据,保留原始行为日志的所有细节。第二层DWB进行轻度聚合,将相同user_id和event_id的行为按时间窗口聚合,私有参数存入Array结构,指标值通过Map类型聚合。第三层DWS实现高度聚合,采用RoaringBitmap存储用户集合,查询时通过位运算快速计算UV等指标。
留存分析利用ClickHouse的Retention函数族实现。通过将用户行为序列转换为留存矩阵,可以高效计算任意时间窗口的留存率。例如计算第N日留存的SQL模式为:
SELECT
sum(r[1]) AS active_users,
sum(r[2]) AS retained_users,
retained_users / active_users AS retention_rate
FROM (
SELECT
retention(day1_active, dayN_active) AS r
FROM (
SELECT
user_id,
toDate(time) = today() - 1 AS day1_active,
toDate(time) = today() - 1 - N AS dayN_active
FROM user_events
WHERE event = 'video_view'
)
GROUP BY user_id
)
这种实现方式相比传统JOIN方法性能提升10倍以上,且不受时间窗口大小的限制。
4.2 漏斗与路径分析优化
漏斗分析通过windowFunnel函数实现,该函数可以找出用户在规定时间窗口内完成的一系列行为序列。B站通过查询下推技术进一步优化性能:
-- 原始查询
SELECT
level, count()
FROM (
SELECT
user_id,
windowFunnel(3600)(time, event='start', event='step1', event='step2') AS level
FROM distributed_table
GROUP BY user_id
)
GROUP BY level
-- 优化版本V1
SELECT
level, sum(cnt)
FROM cluster(default,
view(SELECT
level, count() AS cnt
FROM local_table
GROUP BY level)
)
GROUP BY level
-- 优化版本V2
SELECT
level, sum(cnt)
FROM cluster(default,
view(SELECT
windowFunnel(3600)(time, event='start', event='step1', event='step2') AS level,
count() AS cnt
FROM local_table
GROUP BY level)
)
GROUP BY level
通过将计算尽可能下推到各分片执行,三个版本的性能提升分别为5倍和30倍。
路径分析采用桑基图可视化用户行为流转。技术实现上分为三步:
- 将用户行为序列聚合为路径模式
- 使用sequenceCount函数统计各路径出现次数
- 通过RBM计算路径间的用户重叠度
针对路径分析中的噪声问题(如页面刷新导致的重复事件),B站开发了基于马尔可夫链的清洗算法,可以有效识别并过滤非关键路径节点。
5. 标签人群圈选与系统集成
5.1 RoaringBitmap在人群圈选中的应用
B站构建了统一的标签画像平台,底层基于ClickHouse的RoaringBitmap实现高效人群圈选。技术架构包含三个关键组件:
字典服务将业务属性编码为数字ID。采用改进的Snowflake算法生成64位ID,配合多级缓存(Redis+RockDB)实现50万QPS的高并发查询。服务设计考虑了故障恢复能力,通过BulkLoad机制可在40分钟内恢复20亿+的属性字典。
位图索引构建器定期将用户标签转化为RoaringBitmap。根据标签热度采用不同的存储策略:高频标签使用全局位图,低频标签使用分段位图。这种混合存储方式节省了60%以上的内存空间。
查询引擎支持复杂的布尔运算。通过ClickHouse的bitmapAnd、bitmapOr等函数,可以实现多标签的组合查询。例如查找"最近7天观看科技类视频且未充值会员的用户":
WITH
tech_viewers AS (
SELECT bitmapFromArray(groupArray(user_id)) AS bm
FROM user_events
WHERE event = 'video_view'
AND category = 'tech'
AND time >= now() - interval 7 day
),
non_vip AS (
SELECT ~bm AS bm
FROM vip_users
)
SELECT bitmapCardinality(bitmapAnd(tech_viewers.bm, non_vip.bm))
FROM tech_viewers, non_vip
5.2 多系统集成实践
北极星分析平台与B站的其他数据系统深度集成,形成了完整的数据分析生态:
与AB测试平台集成,可以直接将行为分析结果作为实验分组依据。通过共享RoaringBitmap格式的人群包,实现了分钟级的实验人群创建。
与推荐系统对接,将用户行为特征实时反馈到推荐算法。利用ClickHouse的物化视图机制,关键行为指标可以在5秒内更新到推荐特征库。
与风控系统联动,通过异常行为模式识别潜在风险账号。基于窗口函数的时序分析可以实时检测刷量、爬虫等异常行为。
6. 关键性能优化经验
6.1 查询下推的最佳实践
查询下推是ClickHouse性能优化的核心手段,B站总结了以下实践经验:
适合下推的场景:
- 数据已按查询维度合理分片(如按user_id分片后计算UV)
- 中间结果集较大(如countDistinct、windowFunnel等聚合)
- 需要减少网络传输量的宽表查询
不适合下推的场景:
- 简单聚合(如sum、count)
- 全局排序(如top N查询)
- 跨分片精确去重
下推实现方式:
- 使用distributed_group_by_no_merge设置禁止中间结果合并
- 通过cluster() + view()组合将聚合完全下推
- 对分布式表使用GLOBAL关键字控制执行计划
6.2 跳数索引优化技巧
针对Array/Map类型的字段查询,B站开发了专门的跳数索引优化方案:
BloomFilter索引适合低基数字段。对Map类型的key构建BloomFilter,可以将包含特定key的part快速定位,减少90%以上的无效数据读取。
MinMax索引适合有序数据。对Array中的时间戳字段构建MinMax索引,可以高效支持时间范围查询。
Granularity自适应调整。根据字段分布特征动态设置索引粒度,热点数据采用更细粒度(如8192行),稀疏数据采用更大粒度(如65536行)。
6.3 资源隔离与限流机制
为保证查询稳定性,B站实施了多层次的资源管控:
查询队列分离。将实时查询与离线分析分配到不同的处理队列,避免长查询阻塞关键业务。
内存限额控制。通过max_memory_usage参数限制单查询内存使用,配合max_memory_usage_for_all_queries控制集群总内存。
并发请求限制。根据用户等级设置max_concurrent_queries,保障高优先级用户的查询资源。
7. 未来演进方向
7.1 多模数据分析支持
当前架构正在向支持多业务通用模型的方向演进:
行为数据模型泛化。将"用户-内容-行为"的三元组模型扩展为N元组,支持更丰富的业务场景。例如社区互动场景引入"回复对象"作为第四维度。
异构数据统一查询。通过定义标准映射规则,将不同业务线的数据自动转换为统一模型,保持查询接口的一致性。
7.2 实时分析能力增强
基于Hudi的增量处理。将底层存储迁移到Hudi,支持分钟级的数据新鲜度,同时保持批处理的吞吐能力。
流式OLAP集成。探索将ClickHouse与流处理引擎(如Flink)深度集成,实现亚秒级的实时分析。
7.3 查询优化持续深入
ZOrder索引开发。通过多维空间填充曲线优化多条件查询,使得单个索引可以同时高效支持user_id、event_type、time等多维度过滤。
JIT编译优化。针对高频查询模式开发定制化的代码生成器,将解释执行的查询计划编译为本地代码,预计可提升30%以上的计算性能。
更多推荐
所有评论(0)