1. 项目背景与业务挑战
B站作为国内领先的年轻人文化社区,每天产生千亿级别的用户行为数据。这些数据蕴含着用户观看、互动、消费等关键行为信息,对内容推荐、运营策略和商业变现具有重要价值。然而传统的大数据分析架构在面对如此海量数据时,逐渐暴露出三大核心痛点:
首先是查询性能瓶颈。早期基于Spark的批处理架构,单次事件分析需要3分钟以上,漏斗分析甚至需要半小时才能返回结果。这种延迟使得数据分析结果失去时效性,严重制约了业务决策效率。
其次是资源消耗问题。实时处理链路需要维持1200core的计算资源,Redis维表占用400G内存,Kafka需要300个分区。随着数据量增长,存储成本呈指数级上升,到2021年底ClickHouse集群存储利用率已超过90%。
最后是功能局限性。明细数据模型难以支持复杂的留存分析和路径分析,30天的查询窗口无法满足长期趋势分析需求,私有参数变更需要重构整个数据管道。
2. 技术架构演进历程
2.1 第一阶段:Spark批处理架构(2019-2020)
初期采用典型的Lambda架构,离线和实时两条独立管道:
- 离线层:Hive数仓存储历史数据,通过Spark Jar作业进行预聚合
- 实时层:Flink处理Kafka流数据,写入Redis和HBase
该架构存在三个致命缺陷:
- 资源分配僵化:每个查询启动独立的Spark作业,无法根据查询复杂度动态调整资源
- 模型扩展困难:用户维度信息硬编码在模型中,新增分析维度需要重构ETL流程
- 并发能力受限:共享的Spark Session导致查询排队,高峰期成功率不足80%
2.2 第二阶段:ClickHouse明细方案(2020-2021)
2020年引入ClickHouse作为核心OLAP引擎,架构实现三大突破:
- 实时明细写入:Flink直接清洗原始数据写入ClickHouse,端到端延迟控制在秒级
- 位图计算优化:使用RoaringBitmap实现用户分群的高效交并运算
- 字典服务升级:String类型ID转Bigint,压测支持40万QPS的编码转换
性能指标显著提升:
- 事件分析P90响应时间从3分钟降至5秒
- 漏斗分析从30分钟缩短到30秒
- 实时数据可分析窗口从T+1缩短到T+0
但代价是资源消耗剧增,到2021年集群扩容三次后仍面临存储瓶颈。
2.3 第三阶段:Iceberg+ClickHouse混合架构(2021至今)
当前架构实现"降本增效"四重优化:
- 全模型化聚合:千亿明细压缩为百亿级聚合模型,存储降低40%
- Kappa架构统一:Iceberg作为唯一数据源,消除实时离线口径差异
- 批量导入优化:自研BulkLoad工具实现千亿数据小时级导入
- 计算下推设计:80%的计算在ClickHouse分片本地完成
关键性能对比:
| 指标 | 第二代方案 | 当前方案 | 提升幅度 |
|---|---|---|---|
| 事件分析耗时 | 30-50s | 2.77s | 10-18x |
| 漏斗分析耗时 | 30s | 0.58s | 50x |
| 存储成本 | 100% | 60% | 40%↓ |
| 计算资源 | 1200core | 800core | 33%↓ |
3. 核心技术创新点
3.1 混合存储模型设计
采用"热温冷"三级数据分层策略:
- 热数据(7天):ClickHouse明细表,支持亚秒级响应
- 温数据(30天):ClickHouse聚合模型,秒级查询
- 冷数据(180天):Iceberg归档存储,分钟级分析
关键技术实现:
-- ClickHouse表分区策略 PARTITION BY toYYYYMMDD(event_time) ORDER BY (event_id, user_id) SAMPLE BY cityHash64(user_id) -- Iceberg分区映射 ALTER TABLE iceberg_bdp.uba_events SET PARTITION SPEC (year INT, month INT, day INT)3.2 高效字典服务
自研分布式编码服务解决String类型转换难题:
- 编码算法:改进Snowflake算法,支持63位有序ID
- 多级缓存:本地Cache → Redis集群 → RockDB持久化
- 容灾方案:小时级Hive备份 + 批量恢复工具
压测表现:
- 单节点QPS:50万+
- 缓存命中率:70%+
- 故障恢复:20亿数据40分钟重建
3.3 批量导入优化
演进三代导入方案对比:
JDBC直写:
- 优点:实现简单,延迟低
- 缺点:写放大严重,merge压力大
HDFS中转:
- 优点:资源消耗降低50%
- 缺点:额外存储开销,网络瓶颈
直连DataReceive服务:
- 采用HTTP流式传输
- 服务端磁盘负载均衡
- 性能较HDFS方案提升100%
关键配置参数:
<yandex> <data_receive> <max_concurrent_connections>100</max_concurrent_connections> <max_receive_bandwidth>1GB/s</max_receive_bandwidth> </data_receive> </yandex>4. 典型应用场景实现
4.1 事件分析优化
私有参数处理采用创新Map结构:
# Spark聚合逻辑示例 def aggregate_events(df): return df.groupBy("user_id", "event_id").agg( collect_list("public_params").alias("public_params"), map_from_arrays( collect_list("private_param_keys"), collect_list("private_param_values") ).alias("private_params") )ClickHouse查询优化技巧:
-- 低效写法 SELECT count(DISTINCT user_id) FROM events WHERE arrayExists(x -> x = 'click', private_params) -- 优化写法(使用物化视图) SELECT bitmapCardinality(click_users) FROM event_stats_mv WHERE date = today()4.2 漏斗分析加速
窗口函数下推实现:
-- 原始查询(中心化计算) SELECT windowFunnel(3600)(event_time, event_id = 'page_view', event_id = 'add_cart', event_id = 'checkout' ) AS funnel_steps FROM distributed_events -- 优化版本(计算下推) SELECT sum(funnel_steps) FROM cluster(default, SELECT windowFunnel(3600)(event_time, ...) AS funnel_steps FROM local_events GROUP BY user_id )性能对比:
| 用户规模 | 原始方案 | 优化方案 |
|---|---|---|
| 100万 | 12.3s | 2.1s |
| 1000万 | 78.5s | 9.8s |
4.3 路径分析实现
桑基图数据生成流程:
- 原始事件 → 按user_id聚合为事件链
- 过滤干扰事件(心跳、自动刷新等)
- 构建前缀树统计路径频次
- RBM压缩存储用户集合
关键ClickHouse函数:
WITH paths AS ( SELECT sequenceMatch('(?1).*(?2).*(?3)')(event_time, event_id = 'play', event_id = 'like', event_id = 'share' ) AS matched FROM user_event_sequences ) SELECT sum(matched) FROM paths5. 集群运维实践
5.1 数据平衡算法
两种平衡策略对比:
装箱算法(极致均衡):
- 时间复杂度:O(n log n)
- 适用场景:<100TB表
- 平衡效果:变异系数<0.1
贪心算法(快速平衡):
- 时间复杂度:O(n)
- 适用场景:>100TB表
- 资源消耗:迁移数据量减少60%
平衡执行流程:
- 预检查part是否需要merge
- 目标节点fetch part文件
- 源节点detach part
- 目标节点attach part
- 源节点清理detached part
5.2 查询监控体系
三级监控策略:
- 实时拦截:限制超过5分钟的查询
SET max_execution_time = 300 - 慢查询分析:每日TOP100查询优化
- 机器人巡检:自动检测缺失的skip index
5.3 压缩优化实践
ZSTD压缩效果对比:
| 表类型 | LZ4大小 | ZSTD(1)大小 | 压缩率 |
|---|---|---|---|
| 事件明细 | 1TB | 680GB | 32%↓ |
| 聚合模型 | 200GB | 120GB | 40%↓ |
| 路径分析 | 80GB | 45GB | 44%↓ |
配置方式:
ALTER TABLE events MODIFY SETTING compression = 'zstd', compression_level = 16. 未来演进方向
6.1 多模数据分析
正在开发的ZOrder索引技术:
-- 实验性功能 ALTER TABLE events ADD INDEX zindex (user_id, event_time) TYPE zorder预期效果:
- 多维度查询性能提升3-5倍
- 存储开销增加<15%
6.2 实时能力升级
Hudi替代方案设计:
- 增量摄取:分钟级延迟
- 近实时分析:10秒级响应
- 资源隔离:独立计算集群
6.3 服务端日志整合
通用映射规则示例:
mappings: - source_field: "req.path" target_dim: "page_url" type: "regex" pattern: "/video/(\\w+)" - source_field: "duration" target_metric: "view_duration" unit: "ms"从实际效果来看,这套架构最宝贵的经验在于:OLAP系统的设计必须遵循"数据特征决定技术选型"的原则。B站用户行为数据具有明显的时间局部性(新数据访问频繁)和用户聚集性(少数用户产生大量行为),因此采用时间分区+用户分片的混合分布策略,配合多级聚合模型,才能实现成本和性能的最佳平衡。