本文目录导读:

- 方案一:基于固定公式的简单排序(适合MVP或中小型项目)
- 方案二:基于对数函数的Hacker News风格算法(适合社区/新闻站)
- 方案三:使用Redis实现实时热度排序(适合高并发)
- 方案四:定时离线计算(适合复杂逻辑、大数据量)
- 总结建议
在PHP项目中实现热度排序,通常需要结合时效性和用户行为来设计算法,常见的方案有两种:基于固定公式的权重排序(适合中小型项目)和基于对数衰减的分数计算型平台)。
下面提供几种核心实现思路和对应的PHP代码示例。
核心思想
热度 = 用户行为得分 × 时间衰减因子
- 用户行为:浏览量、点赞、评论、收藏、分享等,不同行为赋予不同权重。
- 时间衰减:越新的内容,热度加成越高;越旧的内容,得分慢慢变低。
基于固定公式的简单排序(适合MVP或中小型项目)
这种方案直接在SQL查询中计算得分,适合数据量不大的场景。
数据库表结构示例
CREATE TABLE articles (
id INT PRIMARY KEY AUTO_INCREMENT,VARCHAR(255),
views INT DEFAULT 0,
likes INT DEFAULT 0,
comments INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
PHP + MySQL 代码实现
<?php
// 定义行为权重
define('WEIGHT_VIEW', 1);
define('WEIGHT_LIKE', 5);
define('WEIGHT_COMMENT', 10);
// 定义时间衰减参数(半衰期:3600秒 = 1小时)
define('HALF_LIFE', 3600);
function getHotArticles($pdo, $page = 1, $pageSize = 20) {
$offset = ($page - 1) * $pageSize;
$now = time();
$sql = "SELECT id, title,
(
(views * :weight_view + likes * :weight_like + comments * :weight_comment)
/ POWER(2, (:now - UNIX_TIMESTAMP(created_at)) / :half_life)
) AS hot_score
FROM articles
ORDER BY hot_score DESC
LIMIT :offset, :pageSize";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':weight_view' => WEIGHT_VIEW,
':weight_like' => WEIGHT_LIKE,
':weight_comment' => WEIGHT_COMMENT,
':half_life' => HALF_LIFE,
':now' => $now,
':offset' => $offset,
':pageSize' => $pageSize
]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
说明:
- 公式:
热度 = (权重之和) / 2^((当前时间-发布时间)/半衰期) - 每经过一个半衰期,热度权重减半。
基于对数函数的Hacker News风格算法(适合社区/新闻站)
Hacker News 的经典热度算法更侧重于爆发性和早期推荐。
算法公式
热度 = (点赞数 - 踩数 + 1) / ((当前时间 - 发布时间) / 3600 + 2)^1.5
- 幂次1.5控制时间衰减的速度(越老衰减越快)。
PHP实现
<?php
function getHackerNewsScore($upvotes, $downvotes, $postTime) {
$score = ($upvotes - $downvotes);
if ($score < 0) $score = 0; // 防止负分
$ageHours = (time() - $postTime) / 3600;
if ($ageHours < 0) $ageHours = 0; // 防止未来时间
// 核心公式
return ($score + 1) / pow(($ageHours + 2), 1.5);
}
// 查询时直接计算
function getHackerNewsHotArticles($pdo) {
$sql = "SELECT id, title, upvotes, downvotes, created_at,
((upvotes - downvotes + 1) / POW((UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(created_at))/3600 + 2, 1.5)) AS hot_score
FROM articles
ORDER BY hot_score DESC
LIMIT 30";
return $pdo->query($sql)->fetchAll();
}
使用Redis实现实时热度排序(适合高并发)
如果项目量较大,直接在MySQL里算会拖慢查询,用Redis的 Sorted Set 可以实时更新热度。
行为发生时更新Redis
<?php
// 用户点赞某篇文章
$redis->zIncrBy('hot_articles', 5, 'article:123'); // 5 = 点赞权重
// 某文章被浏览
$redis->zIncrBy('hot_articles', 1, 'article:123'); // 1 = 浏览权重
// 时间衰减(定期执行)
$redis->zAdd('hot_articles', ['CH' => true], 'article:123', $newScore);
定时任务进行时间衰减
可以使用Laravel的Task Scheduling或crontab,每分钟执行一次衰减:
<?php
// 对Sorted Set中的所有元素执行时间衰减
public function decayHotScores() {
// 每个元素分数乘以衰减因子 0.95(每5分钟衰减5%)
$redis->zInterstore('hot_articles_temp', ['hot_articles'], [0.95]);
$redis->rename('hot_articles_temp', 'hot_articles');
}
获取热度排序
$hotArticles = $redis->zRevRange('hot_articles', 0, 19, true); // 前20名
定时离线计算(适合复杂逻辑、大数据量)
如果权重、算法经常变,或数据量极大,可以用定时脚本(如Laravel Job、cron任务)在后台算出热度并存入数据库缓存字段。
建立一个缓存字段
ALTER TABLE articles ADD COLUMN hot_score DECIMAL(10,4) DEFAULT 0;
定时脚本更新
<?php
// 每10分钟执行一次
function recalculateScores() {
$articles = Article::all();
foreach ($articles as $article) {
$age = time() - $article->created_at->timestamp;
$score = ($article->views * 1 + $article->likes * 5 + $article->comments * 10)
/ pow(($age / 3600 + 2), 1.5);
$article->hot_score = $score;
$article->save();
}
}
查询时直接 ORDER BY hot_score DESC,速度极快。
总结建议
| 项目规模 | 建议方案 | 原因 |
|---|---|---|
| 小型(日活<1000) | MySQL公式 | 简单、无额外依赖 |
| 中型社区/新闻站 | 方案二 + 方案四缓存 | 逻辑清晰,速度快 |
| 高并发、实时性强 | Redis Sorted Set | 避免数据库压力 |
| 算法复杂且多变 | 离线计算 | 灵活易维护 |
关于时间衰减的调整建议:
- 电影、知识类:半衰期可以设为7天(衰减慢)
- 短视频、新闻:半衰期设为30分钟(衰减快)
- 电商商品:可以按绝对时间分段(24小时内加权,之后逐步减弱)
你可以根据产品的核心指标(比如更看重“新”还是“热”)来调节权重和时间衰减系数。