必须用redis扛瞬时请求、mysql保最终一致性,因直接mysql更新会因行锁争抢和连接池耗尽导致卡死;redis用incr和sadd原子操作计数与防重,mysql通过定时任务同步并校验唯一索引。

点赞接口不能直接写 MySQL
并发一上来,UPDATE like_count = like_count + 1 就会卡死。行锁争抢、连接池耗尽、回滚复杂——这不是优化问题,是架构级缺陷。必须把实时计数和最终落库拆开。
Redis 扛住瞬时请求,MySQL 只负责最终一致性。这个分层不是可选项,是上线前必须落地的底线。
- 用户点击点赞,只操作 Redis:
INCR计数 +SADD记录用户行为 - 取消点赞用
SREM判断是否真删掉了,再DECR,避免重复请求导致负数 -
user_id和post_id的唯一索引必须落在 MySQL 表里,Redis 不做主数据源
Redis key 设计要兼顾查、防重、原子性
两个核心 key 缺一不可:like:user:{userId}(Set)存用户点过的所有内容 ID;like:count:{postId}(String)存总点赞数。前者防重复,后者扛高并发。
别用 Hash 存用户行为——HSET 无法原子判断是否存在;也别把 count 放进 Set 的 value 里——没法 INCR。结构错了,后面全白搭。
- 判断是否点过赞:用
$redis->sIsMember('like:user:123', '456'),O(1) 时间 - 点赞总数递增:用
$redis->incr('like:count:456'),天然原子 - key 命名带业务前缀,避免和其他模块冲突,比如不要直接叫
like:123
定时任务同步 Redis 和 MySQL 是刚需,不是锦上添花
Redis 故障、重启、内存淘汰,都会让 like:count:{postId} 和数据库记录脱节。不补这层,上线等于埋雷。
ThinkPHP 的命令行任务最稳妥:php think timer:syncLike 每 2 分钟扫一次 Redis,把增量写入 MySQL,并清空对应 key。别依赖“等用户刷页面时再同步”,那是拿一致性赌运气。
- 同步逻辑必须包含事务:先
INSERT INTO like_log记日志,再UPDATE post SET like_count = like_count + ? - 用
SCAN遍历like:count:*key,别用KEYS,否则阻塞主线程 - 同步完立即
DEL like:count:{postId},防止下次重复累加
配置 Redis 失败会静默降级,必须主动验证
ThinkPHP 8.0 不报错,但 Cache::set() 成功、Cache::get() 返回 null,说明根本没连上 Redis。这是线上最隐蔽的坑。
检查点就三个:config/cache.php 里 host、port、database、password(或 auth)全填了;config/session.php 单独配了 store;最后跑一遍 (new \think\cache\driver\Redis(config('cache.stores.redis')))->handler()->ping() 看返回 +PONG。
别信 .env 里的 REDIS_HOST,TP8 根本不读它——只当开关用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











