☰
Redis 实现播放量原子计数 + 定时任务同步 MySQL
2026/9/25 21:37:55 网站建设 项目流程

一、业务场景

做音乐网站项目,每一次用户点击歌曲播放,就需要该歌曲播放数加一。 假设并发量上来,如果每一次播放请求直接 update 修改 MySQL。大量访问会频繁触发数据库写操作,数据库压力会很高。

于是想到使用 Redis 做中间缓存。播放先写到 Redis,隔一段时间批量同步回 MySQL,减少直接操作数据库的次数。

二、实现思路

1.用户点击播放,后端接收请求;
2. 使用 Redis INCR原子命令,对对应歌曲 id 的播放计数自增 + 1;INCR 是原子操作,不用担心并发冲突;
3. 开启 SpringBoot 定时任务,每隔一段时间,读取 Redis 里面所有歌曲播放增量;
4. 将增量批量更新 MySQL;同步完成之后清空 Redis 里面已经落库的增量数据。
注意隐患:如果服务器突然宕机。Redis 还没来得及同步的数据,会有丢失风险。 解决思路:可以开启 Redis 持久化 RDB/AOF 降低丢数概率;或者缩短定时任务间隔时间。

三、核心关键代码片段

1.播放量自增接口(使用 RedisTemplate)

//歌曲id,播放一次计数+1 // redisKey格式:play:count:{songId} String key = "play:count:" + songId; redisTemplate.opsForValue().increment(key);

2.定时任务(@Scheduled,每 10 分钟执行一次同步逻辑)

@Component @EnableScheduling public class PlayCountSyncTask { @Resource private RedisTemplate<String,Object> redisTemplate; @Resource private SongMapper songMapper; @Scheduled(fixedDelay = 10 * 60 * 1000) public void syncPlayCountToMysql(){ //1.查询redis中所有play:count开头的key //2.循环取出歌曲id和增量 //3.批量update mysql播放数量 //4.同步成功,删除redis里面这一批key } }

定时任务不要写太短间隔,频繁同步就失去 Redis 缓冲意义;太长宕机丢数风险变大。

四、Redis‑MySQL 缓存不一致问题

我们这套方案:Redis 暂存增量,定时任务批量同步。这是异步更新,天然就容易出现缓存和数据库不一致。

4.1当前项目会出现不一致的两种场景

1.还没到定时同步时间,MySQL 直接修改歌曲播放量(后台手动改数据库数值)。Redis 里面还保留旧增量,同步之后就会叠加,造成播放数量出错。

2.定时任务执行一半程序宕机:定时任务遍历一批 key,一部分同步成功入库,还没来得及删除 Redis 里对应的 key,服务宕机。服务重启后,定时任务再次执行,会把这部分增量再次同步一遍,造成播放量重复累加,数据变多。

注意:普通用户点击播放只会往 Redis 做 increment,不会直接写 MySQL。普通播放不会产生不一致;人为修改 MySQL 才是不一致主要来源。

4.2几种解决思路

方案 1:业务约束

业务上约定:所有播放量修改只能走 Redis。后台不允许直接手动修改 MySQL 播放量字段。

优点:实现零代码改动。适合课程演示。

缺点:现实生产环境很难强制所有人不碰数据库。

方案 2:更新 MySQL 同时主动清理对应 Redis Key

如果后台需要修改歌曲播放量(管理员编辑):

修改 MySQL 播放数字;

立刻删除该歌曲对应的play:count:{songId}这个 Redis key。

Redis 这条增量作废。下一次定时任务不会再把旧增量刷进去。

优点:简单好实现。

缺点:如果删除 Redis 网络超时失败,不一致仍然会发生。

方案 3:记录增量日志

不要直接用 Redis 存最终增量数字。Redis 只记录变更。

或者数据库新建一张增量日志表。每次播放除了 Redis 自增,插入一条增量日志。定时任务读取日志表同步,同步完标记已处理。就算 Redis 宕机,数据库日志还留存记录,不会丢数据。

优点:解决宕机丢数;减少不一致风险。

缺点:每一次播放都插入数据库,又增加数据库 IO,违背最开始减轻数据库压力初衷。

五、强一致 vs 最终一致

如果业务强行要求强一致性该怎么办?

本项目 Redis 暂存增量定时同步只能保障最终一致性。若业务场景(余额等)要求强一致:

优先方案:放弃 Redis 做计数缓冲,读写直接访问 MySQL。牺牲一部分吞吐量换取数据准确。

分布式事务 Seata 理论可实现事务回滚,但是 Redis 不支持 XA 协议,工程落地麻烦,很少用于解决 Redis‑MySQL 一致性。

六、思考总结

“不存在优雅、高性能的方案让 Redis 与 MySQL 保证强一致性。工业上的思路是业务选型规避这个难题。

如果要求强一致,就不要把写逻辑交给 Redis;

如果想用 Redis 扛住高并发写压力,就要接受最终一致性。”

如果非要强一致又想要高性能,可以换专门组件,例如 Redis 本身的 Redisson 的 RLock 只是锁,锁只能减少并发冲突,锁解决不了网络故障导致双写不一致。锁≠强一致。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询