能安全使用,但需用ON DUPLICATE KEY UPDATE配合VALUES()函数,否则高并发下多个事务基于同一旧值计算会导致累加丢失;正确写法为count = count + VALUES(count),且表须有唯一键或主键约束。

为什么不能直接用 UPDATE likes SET count = count + 1 更新计数?
因为高并发下会出现竞态条件:多个请求同时读取旧值、各自加1、再写回,导致最终只+1而非+n。MySQL 的 UPDATE ... SET count = count + 1 虽是原子操作,但前提是所有更新都走同一行;而一旦你把“用户A给文章B点赞”和“计数器更新”塞进同一条语句,就隐含了行锁竞争——热点文章的计数行会成为瓶颈,SHOW PROCESSLIST 里能看到大量 Waiting for table metadata lock 或 update 状态阻塞。
推荐结构:分离「行为记录」与「聚合结果」两张表
核心思路是不强求实时一致,用异步或批量方式合并点赞动作,再刷新计数。实际部署中,99% 的业务不需要毫秒级精确计数(比如微博首页显示“12.3w人赞过”,差几百根本没人察觉)。
-
like_log表存原始行为:id,user_id,item_id,created_at,主键或联合索引(user_id, item_id)防重复点赞 -
like_count表存聚合结果:item_id(主键),count,updated_at,只被后台任务或触发器更新 - 首次查计数时,若
like_count无对应item_id,可 fallback 到COUNT(*) FROM like_log WHERE item_id = ?,但要加缓存(如 Redis),避免频繁兜底查询
如何安全地批量刷新计数?
不要用 SELECT COUNT(*) FROM like_log WHERE item_id IN (...) 然后逐条 REPLACE INTO like_count —— 这在大表上会锁表、慢、且无法控制更新节奏。正确做法是让后台任务按时间窗口或分片处理:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 用
SELECT item_id, COUNT(*) FROM like_log WHERE created_at > ? GROUP BY item_id拉取增量日志,?是上次处理的created_at - 用
INSERT INTO like_count (item_id, count, updated_at) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE count = count + VALUES(count), updated_at = VALUES(updated_at)合并更新 - 每次只处理最近 5 分钟或 1000 条日志,避免单次事务过大;配合
WHERE id > ?分页更稳妥(因created_at可能重复)
Redis 缓存计数时要注意什么?
如果用 Redis 的 INCR 做实时计数,必须搭配 MySQL 落地,否则机器宕机就丢数据。关键不是“要不要用 Redis”,而是“怎么对齐”:
- 写入时:先
INCR like:count:{item_id},再异步写like_log;失败则发告警,人工核对 - 读取时:优先读 Redis,但每 100 次请求或每隔 30 秒,主动
SELECT count FROM like_count WHERE item_id = ?校验一次,不一致则重置 Redis 值 - 禁止用
EXPIRE让计数自动过期——会导致反复兜底查 DB,反而压垮 MySQL
最麻烦的不是设计,而是「什么时候该信 Redis,什么时候该信 MySQL」——这个判断逻辑一旦写错,后续所有优化都是空中楼阁。

















