← 每日面试题 学习看板

如何实现一个点赞系统?

面试题/01实践场景题/实践场景题目/场景与业务实践/如何实现一个点赞系统?.md

完整讲解(权威版)

最后更新:2026-10-28 · 基于第 1 次作答

L1 · 30 秒速记

结论:先定边界(单次点赞/取消/归属/统计维度),再用 Redis ZSet(时间戳分数)做判重 + 时间排序,计数异步批量落库,热点内容用本地缓存 + Redis 挡读。 关键词:ZADD NX 判重 · ZREM 取消 · 定时落库 · 计数批量更新 边界:单用户单内容一次点赞;计数最终一致;需防刷限频;强一致场景不适用。

L2 · 核心讲解

核心考点定位:考察点赞系统的高并发设计——先边界后设计、防重复点赞的原子性、取消与计数一致性、热点内容读优化、防刷幂等;面试官想看到"定边界→给方案"而非一上来就堆 Redis。

机制链(≥3 环):

  1. 点赞 → ZADD key score member(member=用户,score=时间戳)判重:检查返回值(0 表示已存在)或用 NX,保证原子防并发重复 → 同一用户不会重复点赞。
  2. 已点赞再点 → 取消 ZREM + 计数减一:保证取消幂等,防重复取消把计数减到负数。
  3. 计数不能每次点都写库 → 先缓存累加(INCR)+ 定时批量落库:高频写合并为低频批量,削峰保护 DB。
  4. 读多写少的热点内容 → 计数与 ZSet 进本地缓存 + Redis:百万赞内容读不压垮下游,读放大被缓存挡住。
  5. 批量落库失败 → 定时任务重试 + 幂等兜底:最终一致要能对账,防止缓存与 DB 计数漂移。

证据锚点:

对比与权衡:

方案防重复计数一致性读性能边界
ZADD NX 判重原子最终一致中依赖 Redis 可用
SADD 判重 + INCR原子最终一致中集合较大时内存高
DB 唯一索引强一致强一致低高并发扛不住
本地缓存 + Redis靠 Redis最终一致高热点读、需失效兜底

常见误区:

  1. 错误:"每次点赞都写数据库" → 正确:缓存累加 + 定时批量落库 → 为什么:高频写会打爆 DB,必须合并写入削峰。
  2. 错误:"ZADD 一定能防重复" → 正确:要 ZADD NX 或检查返回值(0 表示已存在),先查后加有竞态 → 为什么:先 ZSCORE 再 ZADD 非原子,并发下可能重复写入。
  3. 错误:"取消点赞直接 ZREM 就行" → 正确:要 ZREM + 计数减一,且防重复取消 → 为什么:只删不减会导致计数虚高,重复取消会减到负数。
  4. 错误:"热点内容直接查 Redis 就够" → 正确:百万赞热点要本地缓存 + Redis,防单点读放大 → 为什么:超大热点会打爆单个 Redis 实例,需多级缓存。

适用边界:

延伸追问(≥3)

  1. 怎么防重复点赞?
  1. 取消点赞的计数怎么保证?
  1. 热点内容读怎么优化?
  1. 点赞数据怎么落库?

面试者参考回答

【结论】 点赞系统先定边界(单次点赞/取消/归属/统计维度),再用 Redis ZSet 做判重 + 时间排序,计数异步批量落库,热点读用本地缓存 + Redis。

【三点】

第一,防重复:ZADD NX(member=用户)原子判重,返回 0 表示已存在;不能先 ZSCORE 再 ZADD,并发下会竞态重复。

第二,取消与计数:ZREM 取消 + 计数减一,防重复取消;计数先缓存累加(INCR),定时批量刷库,避免每次点赞都写 DB。

第三,热点与防刷:百万赞内容用本地缓存 + Redis 挡读,防单点读放大;同一用户限频(SETNX 时间窗)防刷。

【边界】 三点提醒:一是计数是最终一致(批量落库),需强一致场景不适用;二是依赖 Redis 可用,缓存故障要降级;三是只覆盖"单用户单次 + 归属"边界,多用户复合(如关注 + 点赞)要重新定边界。

面试记录 2026-10-28(第 1 次)

题目:如何实现一个点赞系统?

难度:medium

标签:practice/business, redis/zset, general/knowledge, difficulty/medium

评分:⭐⭐⭐⭐☆(4/5)

我的回答

点赞系统先确定边界;都需要哪些功能 首先肯定是点赞,是否能一个重复点赞?重复点赞有上限么?点赞功能要有归属么?点赞能取消么?点赞功能应对什么数据?是单个功能有点赞,还是点赞要复用到多个功能上?比如朋友圈,评论,用户是否都能点赞?是否一个体系?统计上有要求么?有一定时间段内的点赞统计需求么?还是只有量级的统计不考虑周期;圈定访问然后开始做实现;如果只是最基础的单个环境点赞,单个用户同一个内容单次点赞,可以取消,点赞要有归属;点赞功能肯定不能每次点击都进行数据库更新,所以最好的实现是批量进行更新,一段时间才进行一次点赞,前置可以存储在缓存中,另外由于要有归属,并且单个人只能点赞单次可是在redis中存储到zset中,按照时间戳作为分数存储,一个顺序的list,然后定期将list落库;点赞次数定期更新到对应记录关联的点赞数据表的点赞次数栏目;

点评

评分:⭐⭐⭐⭐☆(4/5)

优点:有先定边界再设计的工程思维、用 ZSet 时间戳分数 + 定期落库 + 批量更新点赞数正确且高效、考虑单次点赞/取消/归属需求边界。

不足:没讲防重复点赞的原子性(ZADD 返回 0 表示已存在或 SADD 判重;高并发下要防并发重复);没讲取消点赞(ZREM)与计数一致性(取消要减一防重复取消);没讲热点内容(百万赞)的读优化(缓存 ZSet、读多写少用本地缓存);没讲幂等/防刷(同一用户限频、验证码/风控)。

补充:补①ZADD NX 判重;②取消 ZREM + 计数减;③热点读(本地缓存 + Redis);④防刷限频。

完整讲解见上方「完整讲解(权威版)」