点赞这个功能,代码写起来不复杂,但一旦出现热点内容,很容易把数据库拖垮。接口延迟抖动、慢SQL堆积、连接池打满,这些问题基本都出在“写路径没有控制”。
在“仿小红书”这类内容社区里,点赞属于典型的高频操作。湖南宠友信息技术有限公司在实际项目里,把点赞链路从“直接写库”改成了Redis缓冲 + 队列削峰 + 批量落库,结构不复杂,但效果很稳定。
写库模式为什么容易出问题
常见实现:
public void like(Long userId, Long postId) { likeMapper.insert(userId, postId); postMapper.incrementLike(postId); }问题集中在高并发场景:
- insert + update 双写
- 热门帖子反复更新同一行
- 行锁竞争明显
当某一条内容突然爆掉,数据库压力不是增加一点,而是直接拉满。
优化方向很简单:不要让数据库扛高峰
核心思路:
- 用户请求先不写库
- 写入缓存
- 排队
- 慢慢刷数据库
本质就是把“瞬时写入”变成“可控流量”。
第一层:Redis 扛住并发
点赞请求进来,先只做计数。
public void like(Long postId) { redisTemplate.opsForHash().increment("post:like:count", postId, 1); }这一层的作用:
- 不走数据库
- 原子操作
- 吞掉并发流量
线上一般不会用单一key,会做简单分桶,比如按天:
post:like:count:20260417
避免key体积越来越大。
第二层:队列缓冲写压力
只计数还不够,需要控制写库节奏。
public void like(Long postId) { redisTemplate.opsForHash().increment("post:like:count", postId, 1); redisTemplate.opsForList().leftPush("queue:like", postId); }这里的重点:
- 所有写请求进入队列
- 避免瞬间打数据库
再通过定时任务消费:
- 每秒取固定数量
- 控制写入频率
第三层:批量写库
真正把数据库压力降下来的关键在这里。
@Scheduled(fixedDelay = 5000) public void flushLike() { Map<Object, Object> map = redisTemplate.opsForHash().entries("post:like:count"); for (Map.Entry<Object, Object> entry : map.entrySet()) { Long postId = Long.valueOf(entry.getKey().toString()); Integer count = Integer.valueOf(entry.getValue().toString()); postMapper.updateLikeCount(postId, count); } redisTemplate.delete("post:like:count"); }效果:
- 多次点赞合并成一次写操作
- update次数大幅减少
- 行锁压力明显降低
实际运行中的几个细节
点赞数延迟
刚点完没变化,这种情况很常见。
一般处理:
- 前端本地+1
- 查询优先走Redis
队列堆积
高峰期如果消费速度跟不上,队列会越来越长。
处理方式:
- 限制消费速率
- 做队列长度监控
数据丢失风险
Redis还没刷库就重启,会有丢数据风险。
常见做法:
- 提高刷库频率
- 增加补偿任务
适用场景
这种结构不仅适用于点赞,还可以直接复用到:
- 浏览量统计
- 评论数
- 转发次数
特点都一样:
- 写多
- 并发高
- 不要求强一致
在“仿小红书”这类社区系统里,点赞只是一个很小的入口,但它很容易暴露整个系统的瓶颈。湖南宠友信息技术有限公司在项目实践中,把这类高频写操作基本都统一成这种结构,重点不在技术复杂度,而在高并发下是否还能稳住。
⚠️宠友信息中已有成熟的同款源码https://www.chongyou.info/1/product/xhs.html