Python点赞功能怎么做_Redis Set缓存点赞状态与定时持久化

4次阅读

redis 用 set 存点赞状态可避免数据库行锁和多次查询,key 设为 ”post:7806:liked_users”,配合 lua 脚本保证判断与添加原子性,再异步落库 + 定时校准实现高性能与一致性。

Python 点赞功能怎么做_Redis Set 缓存点赞状态与定时持久化

点赞状态为什么不能只存数据库

每次点一下就查一次、写一次,用户量上来后 UPDATE user_post SET likes = likes + 1 会锁行,高并发下直接拖慢整个帖子服务。更麻烦的是“已点赞”这种布尔状态,查一遍才能知道要不要加,多一次 round-trip。

用 Redis SET 存用户 ID 是最轻量的判断方式:存在即点过,不存在就能点。但得注意 SET 不是唯一选择——HSETBITFIELD 在某些场景更省空间,不过对点赞这种“用户 - 帖子”二元关系,SET 直观、原子、调试方便。

常见错误现象:

  • 把用户 ID 当字符串拼进 key 里(如 "post:123:likes:u456"),导致 key 过多、无法批量操作
  • INCR 统计数但没同步更新 Set,造成“已点却没进榜”或“重复点”

Redis Set 的 key 设计和原子操作怎么写

key 必须能快速定位到某篇帖子的所有点赞者,推荐格式:"post:7806:liked_users"。别用 "user:{uid}:liked_posts" 反向建,除非你要做“谁点过我”通知——那是另一个读路径。

立即学习 Python 免费学习笔记(深入)”;

核心动作只有两个:判断是否点过、添加并计数。必须用 Lua 脚本保证原子性,否则并发时可能重复加 1 或漏判:

local liked_key = "post:" .. ARGV[1] .. ":liked_users" local is_liked = redis.call("SISMEMBER", liked_key, ARGV[2]) if is_liked == 1 then     return {0, "already liked"} end redis.call("SADD", liked_key, ARGV[2]) redis.call("INCR", "post:" .. ARGV[1] .. ":like_count") return {1, "ok"}

说明:

  • ARGV[1]post_idARGV[2]user_id(建议转成字符串,避免整型比较陷阱)
  • 别用 SETNX + 单独 INCR,网络延迟可能导致两次请求都通过判断
  • 如果业务允许“点两次取消”,脚本要改:先 SREMSISMEMBER 判断当前状态

定时持久化不是“定期刷库”,而是“补漏 + 兜底”

Redis 挂了不可怕,可怕的是重启后所有 like_countliked_users 全丢。但你不需要每秒都写 DB——那样反而压垮 MySQL。真正该做的,是把“新增点赞”异步落库,并用定时任务校准差异。

做法分两层:

  • 每次成功点赞后,发一条消息到 Celery/RQ/Kafka,由 worker 异步执行 INSERT INTO post_likes (post_id, user_id) VALUES (?, ?),失败重试 3 次
  • 每天凌晨跑一个校准脚本:对比 post:{id}:like_count 和 DB 中 COUNT(*) FROM post_likes WHERE post_id = ?,差值超过阈值(比如 5%)就触发全量同步

性能影响:

  • 异步写库让主流程 RT 压在 5ms 内;Redis 本身不持久化 RDB/AOF 也完全 OK
  • 校准脚本用 SCAN 遍历 key,别用 KEYS,避免阻塞

Python 客户端怎么防雪崩和连接泄漏

redis-py 时,默认连接池大小是 10,小流量够用,但上线后瞬间几百 QPS 就会卡住。必须显式配置:

redis_client = redis.Redis(connection_pool=redis.ConnectionPool(         host="localhost",         port=6379,         db=0,         max_connections=50,         retry_on_timeout=True,         health_check_interval=30) )

容易踩的坑:

  • 在 Flask/FastAPI 的 request scope 里反复 new Redis() 实例,连接数指数增长
  • 没设 socket_timeout,Redis 假死时整个 Web 请求 hang 住(默认无超时)
  • pipeline 批量操作时忘了 execute(),脚本一直不发,连接卡住

还有一个隐蔽问题:点赞接口被刷。别只靠前端按钮置灰,后端必须加 INCR + EXPIRE 做用户粒度频控,比如 "rate_limit:u{uid}:post",10 秒最多点 3 次。

Redis Set 看似简单,但 key 命名一错、原子性一漏、连接一松,线上就变成慢接口和数据不一致的温床。尤其要注意 Lua 脚本里别调 redis.call("KEYS") 或循环大集合——那不是缓存,是定时炸弹。

text=ZqhQzanResources