Redis ZSET是实现热门数据排行榜的最优解,因其支持原子增分(ZINCRBY)、O(log N)排序查询(ZREVRANGE)、按分数范围清理(ZREMRANGEBYSCORE)及多时间维度分key管理,可彻底规避MySQL ORDER BY在高并发下的性能瓶颈与事务竞争问题。

Revel 中用定时任务 + Redis 实现热门数据排行榜
Revel 本身不内置排行榜功能,但热门数据(如文章、商品点击榜)本质是「带权重的有序聚合」,直接靠数据库 ORDER BY score DESC LIMIT 10 在高并发下容易拖慢主库,也难应对实时性要求。最稳妥的做法是:用 Redis 的 ZSET 存储和更新分数,Revel 后端只负责触发更新、读取榜单、暴露 API。
为什么不能直接在 Controller 里查 MySQL 排行榜
常见错误是每次请求都执行类似 SELECT id, title, view_count FROM articles ORDER BY view_count DESC LIMIT 10 —— 这在视图量上涨后会迅速成为慢查询瓶颈,且无法支持「最近 24 小时热度」「加权热度(浏览 × 0.3 + 收藏 × 1.5)」等动态规则。
- MySQL 索引对
ORDER BY + LIMIT效率依赖排序字段的基数和选择性,view_count 变化频繁,索引维护开销大 - 无法原子性地递增并排序(比如用户每点一次,就给该文章 score += 1),需先
SELECT再UPDATE,易竞争 - 分页拉取历史榜单(如第 2 页 top 10–20)时,
LIMIT 10,10会随数据增长变慢
用 Redis ZSET 构建可更新的热度榜
核心逻辑是把「数据 ID」作为 member,「热度分」作为 score,用 ZINCRBY 原子累加,用 ZREVRANGE 拉取降序前 N 名。Revel 中可在 service 层封装:
// 示例:给 article:123 热度 +1
conn := redis.DefaultClient
_, err := conn.Do("ZINCRBY", "hot:articles:24h", 1, "article:123")
// 示例:获取当前热度 Top 10(含分数)
members, _ := conn.Do("ZREVRANGE", "hot:articles:24h", 0, 9, "WITHSCORES")
- score 必须是 float64 字符串(如
"127.5"),不能传整数类型,否则ZINCRBY报错ERR value is not a valid float - 建议按时间粒度分 key,如
hot:articles:24h、hot:articles:7d,避免单个 ZSET 过大导致ZREVRANGE延迟升高 - 用
ZREMRANGEBYRANK或ZREMRANGEBYSCORE定期清理过期或低分项,防止内存膨胀
在 Revel 中安全触发和读取榜单
不要在 HTTP 请求中同步调用耗时 Redis 操作;而是将计分逻辑下沉到异步事件或中间件。例如:
- 用户访问文章页时,Controller 不直接
ZINCRBY,而是发消息到redis pub/sub或写入job queue(如用github.com/robfig/cron定时批量处理) - 榜单接口(如
GET /api/hot/articles)只做ZREVRANGE+ 关联查 DB 获取标题、封面等字段,避免在 ZSET 里存冗余数据 - 注意 Redis 连接池配置:
MaxIdle和MaxActive需匹配 Revel 的并发模型,否则高流量下出现connection refused或超时
真正麻烦的不是写几行 ZINCRBY,而是热度衰减策略(比如 24 小时后分数 × 0.8)、多维度合并(浏览 + 转发 + 评论加权)、以及冷热数据切换——这些得靠业务逻辑兜底,Redis 只负责快和稳。











