1. 项目背景与核心需求
最近在做一个番剧短视频平台的流量分析系统,需要处理每天TB级的用户行为日志。传统单机架构根本扛不住这种数据量,最终选择了ThinkPHP/Laravel+Hadoop的技术组合。这个方案最大的优势是既能利用PHP框架快速开发业务功能,又能借助Hadoop生态处理海量数据。
核心要解决三个问题:
- 用户行为分析:统计每集视频的完播率、跳出点,找出内容质量问题
- 流量趋势预测:分析不同时段的访问波动,为服务器扩容提供依据
- 个性化推荐:基于用户画像(地域/年龄/性别)优化内容分发
2. 技术架构设计
2.1 整体架构分层
系统采用典型的三层架构:
[数据采集层] Flume/Kafka -> [存储计算层] HDFS/Hive -> [业务应用层] PHP+Web2.2 框架选型对比
在PHP框架选择上做了AB测试:
- ThinkPHP:更适合快速上线,文档齐全但扩展性一般
- Laravel:优雅的ORM和队列系统,适合长期迭代
最终方案:核心业务用Laravel,管理后台用ThinkPHP
2.3 Hadoop组件选型
根据数据规模选择组件:
- 日活<100万:HDFS+MapReduce+Hive
- 日活>100万:改用Spark替换MapReduce
- 实时分析:增加Spark Streaming
3. 数据处理全流程实现
3.1 日志采集方案
原始日志格式示例:
2023-08-01 12:00:00|user123|video456|play|120s|Beijing|iOS使用Flume配置:
agent.sources = logsrc agent.channels = memchan agent.sinks = hdfsout agent.sources.logsrc.type = exec agent.sources.logsrc.command = tail -F /var/log/video.log agent.sinks.hdfsout.hdfs.path = hdfs://cluster/data/raw/%Y%m%d3.2 数据清洗关键代码
MapReduce清洗程序核心逻辑:
public class LogCleaner extends Mapper<LongWritable, Text, Text, IntWritable> { protected void map(LongWritable key, Text value, Context context) { String[] fields = value.toString().split("\\|"); if(fields.length == 7) { String userId = fields[1]; String videoId = fields[2]; String action = fields[3]; // 过滤异常数据 if(isValidAction(action)) { context.write(new Text(videoId), new IntWritable(1)); } } } }3.3 Hive数据仓库设计
建立星型模型:
-- 事实表 CREATE TABLE fact_play ( dt STRING, user_id STRING, video_id STRING, duration INT, region STRING ) PARTITIONED BY (day STRING); -- 维度表 CREATE TABLE dim_video ( video_id STRING, title STRING, category STRING, length INT );4. PHP与Hadoop集成方案
4.1 Java桥接技术
通过PHP-JavaBridge调用Hadoop分析结果:
$bridge = new Java("php.java.bridge.JavaBridge"); $hadoopService = new Java("com.analysis.HadoopService"); $result = $hadoopService->getHotVideos(date("Ymd"));4.2 性能优化方案
- 缓存策略:
- Redis缓存热数据(TTL 1小时)
- Laravel队列异步生成报表
- 查询优化:
// 不好的写法 $videos = DB::table('video_stats')->get(); // 优化写法 $videos = DB::table('video_stats') ->select('video_id', 'play_count') ->where('date', $today) ->orderBy('play_count', 'desc') ->limit(100) ->get();5. 数据可视化实现
5.1 ECharts集成示例
时段流量折线图配置:
option = { xAxis: { type: 'category', data: ['0h', '2h', '4h', '6h', '8h', '10h'] }, series: [{ data: [120, 200, 150, 80, 70, 110], type: 'line', smooth: true }] }5.2 实时看板方案
WebSocket服务端代码片段:
$server = new Swoole\WebSocket\Server("0.0.0.0", 9502); $server->on('message', function ($server, $frame) { $data = Redis::get('realtime_stats'); $server->push($frame->fd, json_encode($data)); });6. 踩坑经验总结
- 时区问题:
- Hadoop集群默认UTC时间
- 解决方案:所有时间戳存储时转为UTC,展示时再转换
- 小文件问题:
- Flume直接写入HDFS会产生大量小文件
- 解决方案:配置Flume的hdfs.rollInterval=3600(1小时滚动)
- PHP内存溢出:
- 处理大数据集时容易OOM
- 解决方案:改用生成器(yield)分批处理
- 跨集群问题:
- 测试环境和生产环境Hadoop配置不同
- 解决方案:使用Hadoop的ViewFS统一路径
这个项目让我深刻体会到,大数据系统最难的往往不是技术实现,而是各个组件之间的协调配合。比如有一次因为Hive表字段类型和PHP接收类型不匹配,导致整个看板数据显示异常。后来我们建立了严格的数据类型检查清单,类似问题再没发生过。