去年为某视频平台做技术咨询时,他们最头疼的问题就是:用户打开APP看到的永远是热门榜单,那些真正符合口味的冷门佳作永远沉在海底。这正是我们要解决的痛点——用大数据技术为每个用户打造专属的影视图书馆。
这个基于ThinkPHP+Vue的推荐系统,核心在于三个技术支点:
关键设计原则:在保证推荐质量的前提下,前端交互延迟必须控制在300ms以内,这是影响用户体验的黄金阈值。
爬虫部分采用Scrapy-Redis分布式架构,这里分享几个真实对抗中的经验:
python复制# 反反爬策略配置示例(基于中间件)
class ProxyMiddleware(object):
def process_request(self, request, spider):
request.meta['proxy'] = random.choice(PROXY_POOL) # 自动切换代理IP
request.headers['User-Agent'] = fake_useragent() # 动态UA
# 智能限速算法
class AdaptiveDelayMiddleware:
def __init__(self):
self.last_response_time = time.time()
def process_request(self, request, spider):
current_delay = time.time() - self.last_response_time
ideal_delay = max(0.5, 2 - current_delay*0.1) # 动态调整请求间隔
time.sleep(ideal_delay)
关键挑战:
数据存储采用分层设计:
code复制HDFS
├── raw_data # 原始数据(保留30天)
├── cleaned # 清洗后数据(Parquet格式)
└── features # 特征数据集(定期更新)
Spark作业优化技巧:
scala复制// 使用DataFrame API替代RDD提升性能
val ratingsDF = spark.read.parquet("/cleaned/ratings")
.repartition(1000) // 控制分区数避免小文件
.cache() // 多次使用的DF一定要缓存
// 采用Delta Lake实现ACID事务
df.write.format("delta")
.mode("overwrite")
.save("/features/movie_vectors")
我们的算法组合拳:
协同过滤:ALS算法优化
python复制from pyspark.ml.recommendation import ALS
als = ALS(
rank=50, # 隐向量维度
maxIter=15, # 迭代次数
regParam=0.01, # 正则化系数
coldStartStrategy="drop" # 冷启动处理
)
内容推荐:BERT+TF-IDF双通道
实时反馈:Flink处理用户行为流
java复制DataStream<UserAction> actions = env
.addSource(new KafkaSource())
.keyBy("userId")
.window(TumblingEventTimeWindows.of(Time.minutes(5)))
.aggregate(new PreferenceAggregator());
创新性地引入"基因图谱"机制:
code复制weight = base_weight * e^(-0.1*t) # t为行为时间差(小时)
推荐API接口实现:
php复制class RecommendController extends Controller {
public function getRecommendations($userId) {
// 多级缓存策略
$cacheKey = "rec_{$userId}";
if ($data = Redis::get($cacheKey)) {
return json_decode($data, true);
}
// 实时计算
$result = $this->calculateRecommendations($userId);
// 缓存结果(设置阶梯过期时间)
Redis::setex($cacheKey,
count($result) > 10 ? 3600 : 600,
json_encode($result));
return $result;
}
private function calculateRecommendations($userId) {
// 调用Spark ML模型服务
$client = new SparkClient();
return $client->predict($userId);
}
}
推荐列表渲染的骚操作:
javascript复制// 虚拟滚动优化(万级数据不卡顿)
<template>
<div class="viewport" @scroll="handleScroll">
<div class="scroll-area" :style="{ height: totalHeight + 'px' }">
<div
v-for="item in visibleItems"
:key="item.id"
:style="{ transform: `translateY(${item.offset}px)` }"
>
<MovieCard :data="item"/>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
allItems: [], // 全量数据
visibleItems: [], // 可视区数据
itemHeight: 180, // 每项高度
bufferSize: 10 // 缓冲条数
}
},
computed: {
totalHeight() {
return this.allItems.length * this.itemHeight
}
},
methods: {
handleScroll() {
const scrollTop = this.$el.scrollTop
const startIdx = Math.max(0,
Math.floor(scrollTop / this.itemHeight) - this.bufferSize)
const endIdx = Math.min(
this.allItems.length,
startIdx + Math.ceil(this.$el.clientHeight / this.itemHeight) + this.bufferSize * 2
)
this.visibleItems = this.allItems
.slice(startIdx, endIdx)
.map((item, i) => ({
...item,
offset: (startIdx + i) * this.itemHeight
}))
}
}
}
</script>
通过火焰图发现的性能瓶颈:
数据库查询未使用覆盖索引 → 添加组合索引
sql复制ALTER TABLE user_behavior
ADD INDEX idx_uid_mid (user_id, movie_id);
特征计算重复执行 → 预计算+增量更新
bash复制# 每日凌晨跑批
0 3 * * * /usr/bin/spark-submit feature_update.py
序列化开销过大 → 改用MessagePack
php复制// 替换json_encode
msgpack_pack($data);
某次上线后服务器内存持续增长,通过以下步骤定位:
php复制class PredictClient {
private $channel;
public function __destruct() {
$this->channel->close();
}
}
当Vue遇到ThinkPHP的跨域时,试过这些方案:
JSONP:只支持GET,安全性差 ❌
CORS:推荐方案但要服务端配合 ✅
php复制// ThinkPHP中间件
public function handle($request, Closure $next) {
$response = $next($request);
$response->header([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET,POST,PUT',
'Access-Control-Allow-Headers' => 'Content-Type,Authorization'
]);
return $response;
}
Nginx反向代理:生产环境终极方案 🚀
nginx复制location /api/ {
proxy_pass http://backend;
add_header 'Access-Control-Allow-Origin' '$http_origin';
}
当用户历史行为记录超过百万时:
LIMIT 1000000, 20 效率极低sql复制-- 使用索引覆盖+延迟关联
SELECT t.* FROM user_actions t
JOIN (
SELECT id FROM user_actions
WHERE user_id = 123
ORDER BY create_time DESC
LIMIT 1000000, 20
) tmp ON t.id = tmp.id;
生产环境拓扑图:
code复制用户 → CDN(静态资源) → Nginx(负载均衡) → ThinkPHP集群
↘
→ Spark on YARN
↘
→ HDFS+HBase
关键配置参数:
yaml复制# php-fpm优化
pm = dynamic
pm.max_children = 100
pm.start_servers = 30
pm.min_spare_servers = 20
pm.max_spare_servers = 50
# Redis连接池
redis.pool.max_active = 200
redis.pool.max_wait = 500ms
上线三个月后的数据对比:
| 指标 | 旧系统 | 新系统 | 提升 |
|---|---|---|---|
| CTR | 1.2% | 3.8% | 217% |
| 观看完成率 | 35% | 58% | 66% |
| 用户留存率 | 41% | 67% | 63% |
特别说明:冷启动场景下(新用户前7天),混合算法的推荐准确率比纯协同过滤高42个百分点。
这套架构的通用性很强,最近我们将其改造应用于:
有个有趣的发现:当引入用户社交关系数据后,推荐多样性指标提升了28%,但需要特别注意隐私计算问题。我们最终采用联邦学习方案,在保护用户数据的前提下实现跨平台特征共享。