使用Redis的Sorted Set(ZSET)实现点赞排序,每条动态对应一个ZSET,成员为用户ID,分值为时间戳+随机数避免相同时间冲突。点赞时ZINCRBY增加分数,取出Top N用ZRANGE。并发冲突用Lua脚本原子执行:local key = KEYS[1] local user = ARGV[1] local score = ARGV[2] local liked = redis.call('ZSCORE', key, user) if liked then return 0 end redis.call('ZADD', key, score, user) redis.call('SADD', key..':liked', user) return 1。排序延迟通过ZREVRANGE实时获取Top点赞用户,提升用户体验。
方案一:ZSet + 时间戳排序
针对社交平台的点赞功能,使用Redis ZSet存储点赞数据,每个帖子一个ZSet,member为用户ID,score为点赞时间戳(毫秒级)+小随机数防雪崩。点赞操作:ZADD post:1:likes 当前时间戳 用户ID。取消点赞:ZREM。获取点赞榜:ZREVRANGE post:1:likes 0 9 WITHSCORES。海量数据用Pipeline批量操作,减少RTT。
高效处理并发点赞
并发冲突解决方案:用SETNX实现分布式锁,点赞前加锁,执行ZADD后解锁。但锁开销大,推荐Lua脚本原子化:-- 点赞Lua脚本 local key = KEYS[1] local user_id = ARGV[1] local timestamp = ARGV[2] if redis.call('SISMEMBER', key..':users', user_id) == 1 then return 0 end -- 已赞 redis.call('SADD', key..':users', user_id) redis.call('ZADD', key, timestamp, user_id) return 1。脚本确保幂等性,解决重复点赞问题。
优化排序延迟
排序延迟痛点:ZSet score精确时间戳导致热点Key访问慢。优化:用时间窗口分片,每小时一个ZSet,合并时多Key ZUNIONSTORE临时Set再排序。持久化:每5分钟AOF RDB混合快照,故障恢复快。缓存穿透用布隆过滤器预判用户是否点赞,减少无效查询,提升系统稳定性。
海量点赞数据架构
单机Redis内存不足用Cluster分片,帖子ID哈希到slot。点赞计数用HyperLogLog近似PFADD PFCOUNT节省内存。实时排行榜:定时任务CRON每分钟ZREVRANGEBYSCORE刷新到MySQL,避免全量扫描。用户体验:WebSocket推送Top10点赞变化,点赞动画用Redis Pub/Sub异步通知。
实际代码实现
Python Redis客户端点赞函数:def like(post_id, user_id): score = time.time() * 1000 + random.random() / 1000 script = ''' if redis.call('SISMEMBER', KEYS[1], ARGV[1]) == 1 then return 0 end redis.call('SADD', KEYS[1], ARGV[1]) redis.call('ZADD', KEYS[2], ARGV[2], ARGV[1]) return 1 ''' r.eval(script, 2, f'post:{post_id}:liked_users', f'post:{post_id}:likes', user_id, score)。获取排序:r.zrevrange(f'post:{post_id}:likes', 0, 9, withscores=True)
监控与稳定性
系统稳定性:Redis Sentinel高可用,慢查询日志监控ZREVRANGE耗时>50ms报警。QPS峰值10w用连接池+Pipeline。数据一致性:点赞最终一致,异步落盘MySQL。用户体验提升:点赞后1s内显示排序位置,用ZREVRANK实时计算排名。
FAQ
Q: 如何防止用户重复点赞?
A: 用Set记录已点赞用户ID,Lua脚本检查SISMEMBER前原子判断。
Q: 海量数据内存怎么优化?
A: score用时间窗口分片ZSet,HyperLogLog估算计数,定期归档冷数据。
Q: 并发QPS 10w+怎么扛?
A: Redis Cluster分片+Pipeline批量+Lua原子脚本。
Q: 排序怎么实时更新?
A: 点赞即时ZADD,客户端ZREVRANGE 0 99获取Top100。