最后更新:2026-10-28 · 基于第 1 次作答
结论:先定边界(单次点赞/取消/归属/统计维度),再用 Redis ZSet(时间戳分数)做判重 + 时间排序,计数异步批量落库,热点内容用本地缓存 + Redis 挡读。 关键词:ZADD NX 判重 · ZREM 取消 · 定时落库 · 计数批量更新 边界:单用户单内容一次点赞;计数最终一致;需防刷限频;强一致场景不适用。
核心考点定位:考察点赞系统的高并发设计——先边界后设计、防重复点赞的原子性、取消与计数一致性、热点内容读优化、防刷幂等;面试官想看到"定边界→给方案"而非一上来就堆 Redis。
机制链(≥3 环):
ZADD key score member(member=用户,score=时间戳)判重:检查返回值(0 表示已存在)或用 NX,保证原子防并发重复 → 同一用户不会重复点赞。ZREM + 计数减一:保证取消幂等,防重复取消把计数减到负数。INCR)+ 定时批量落库:高频写合并为低频批量,削峰保护 DB。证据锚点:
ZADD key score member:点赞写入ZADD ... NX:不存在才写,原子判重ZREM:取消点赞ZSCORE:查单用户是否已赞ZCARD:点赞总数ZRANGE:按分数(时间)排序INCR:计数累加SETNX:防刷限频Lua 脚本:原子判重 + 更新对比与权衡:
| 方案 | 防重复 | 计数一致性 | 读性能 | 边界 |
|---|---|---|---|---|
| ZADD NX 判重 | 原子 | 最终一致 | 中 | 依赖 Redis 可用 |
| SADD 判重 + INCR | 原子 | 最终一致 | 中 | 集合较大时内存高 |
| DB 唯一索引 | 强一致 | 强一致 | 低 | 高并发扛不住 |
| 本地缓存 + Redis | 靠 Redis | 最终一致 | 高 | 热点读、需失效兜底 |
常见误区:
ZADD NX 或检查返回值(0 表示已存在),先查后加有竞态 → 为什么:先 ZSCORE 再 ZADD 非原子,并发下可能重复写入。ZREM + 计数减一,且防重复取消 → 为什么:只删不减会导致计数虚高,重复取消会减到负数。适用边界:
SETNX + 时间窗);统计周期维度(按天 ZSet);缓存失效兜底(定时重建)。ZADD NX(member=用户)原子判重,返回 0 表示已存在;避免先 ZSCORE 再 ZADD 的竞态Lua 脚本原子组合ZREM + 计数减一,且防重复取消(检查返回值)【结论】 点赞系统先定边界(单次点赞/取消/归属/统计维度),再用 Redis ZSet 做判重 + 时间排序,计数异步批量落库,热点读用本地缓存 + Redis。
【三点】
第一,防重复:ZADD NX(member=用户)原子判重,返回 0 表示已存在;不能先 ZSCORE 再 ZADD,并发下会竞态重复。
第二,取消与计数:ZREM 取消 + 计数减一,防重复取消;计数先缓存累加(INCR),定时批量刷库,避免每次点赞都写 DB。
第三,热点与防刷:百万赞内容用本地缓存 + Redis 挡读,防单点读放大;同一用户限频(SETNX 时间窗)防刷。
【边界】 三点提醒:一是计数是最终一致(批量落库),需强一致场景不适用;二是依赖 Redis 可用,缓存故障要降级;三是只覆盖"单用户单次 + 归属"边界,多用户复合(如关注 + 点赞)要重新定边界。
题目:如何实现一个点赞系统?
难度: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);④防刷限频。
完整讲解见上方「完整讲解(权威版)」