必须用redis扛瞬时写、mysql保最终一致性,因直接mysql更新会因行锁争抢和连接池耗尽导致卡死;hash无法原子判断是否已点赞,sadd/sismember才是唯一可靠选择。

直接用 MySQL 记录点赞/收藏,高并发下会卡死——行锁争抢、连接池耗尽、事务回滚复杂,这不是性能问题,是架构级错误。必须用 Redis 扛住瞬时写,MySQL 只做最终一致性落库。
为什么不能用 Hash 存用户行为?
Hash 的 HSET 无法原子判断「用户是否已点过」,HGET + HSET 中间存在竞态窗口;而点赞防重必须 O(1) 原子判断。SADD/SISMEMBER 是唯一可靠选择。
-
like:user:{userId}必须是 Set,不是 Hash,否则并发重复点赞无法拦截 - 别把 count 放进 Hash 的 field 里——没法用
INCR原子递增,还得自己加锁 - Set 的成员值建议统一用字符串(如
'post_123'),避免整型被 Redis 自动截断
点赞总数和用户行为 key 怎么设计才不冲突?
两个 key 缺一不可:like:count:{postId}(String,计数)和 like:user:{userId}(Set,防重)。命名必须带业务前缀,否则和其他模块混用会互相污染。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 不要用
like:123这种裸 key,容易被其他功能覆盖或误删 - 用户维度 key 和内容维度 key 分开,避免单个 key 膨胀过大(Redis 单 key 最好不超过 1 万成员)
- 如果支持多类型(文章/评论/视频),key 中显式带 type:如
like:user:123:post、like:user:123:comment
取消点赞时如何避免负数和重复扣减?
不能先 SREM 再 DECR,因为 SREM 返回结果可能被并发请求覆盖。必须用 Lua 脚本原子执行判断+操作。
- 脚本示例:
if redis.call("sismember", KEYS[1], ARGV[1]) == 1 then redis.call("srem", KEYS[1], ARGV[1]); return redis.call("decr", KEYS[2]) else return 0 end - ThinkPHP 中调用:
$redis->eval($script, ['like:user:123', 'like:count:456'], ['post_456']) - 返回
0表示未点过,非0是新计数值;前端据此决定是否刷新 UI
定时同步到 MySQL 容易漏掉哪些关键校验?
Redis 故障、内存淘汰、主从延迟都会导致缓存与数据库脱节。只靠「定时任务跑一遍」远远不够。
- 同步前必须查 MySQL 的唯一索引(
user_id + post_id + type),插入失败说明已存在,跳过或报错 - 同步后要对比 Redis
like:count:{postId}和 MySQL 当前值,差值超阈值(如 >5)需告警,不是静默覆盖 - 队列中待同步的
post_id必须设 TTL(如 1 小时),防止 Redis 挂掉后堆积失效数据
最常被忽略的是:Redis key 设计没分用户维和内容维,或者防重逻辑放在 PHP 层做判断——只要并发稍高,就一定出现重复点赞或计数错乱。这一步错了,后面所有优化都是徒劳。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










