INCR能避免并发丢数据因其是Redis原子命令,而GET+SET非原子操作易致竞态;需用带超时context调用,key应按“post:123:likes”格式设计并校验ID防注入。

为什么用 INCR 而不是 GET + SET 实现点赞计数
直接用 GET 读出旧值、加 1、再 SET 回去,看似简单,但在并发场景下会丢数据。比如两个请求同时读到 10,各自加 1 后都写回 11,实际应为 12。INCR 是 Redis 原子命令,底层由单线程保证执行不中断,天然解决竞态问题。Echo 路由里调用 redisClient.Incr(ctx, key) 就行,不用加锁或重试逻辑。
INCR 的 key 设计要带业务前缀和唯一标识
别用硬编码字符串如 "like_count",否则不同文章、用户会互相覆盖。推荐格式:"post:123:likes" 或 "user:456:liked_posts"。注意两点:
- 冒号分隔层级,利于 Redis CLI 查看和运维工具识别
- ID 部分必须是可信来源(如 URL 参数经
strconv.ParseInt校验),避免注入恶意 key(如"post:123:likes;flushdb") - 如果需要限制单用户对某内容只能点一次,得配合
SETNX先存用户-内容关系,再INCR
Echo 中处理点赞请求时的上下文与错误传递
Go-Redis 的所有命令都要求传 context.Context,别直接用 context.Background()。Echo 的 c.Request().Context() 才能正确继承超时和取消信号。常见错误是忘记检查 err,导致 Redis 连接失败时接口静默返回 0:
count, err := rdb.Incr(c.Request().Context(), "post:"+postID+":likes").Result()
if err != nil {
c.JSON(500, map[string]string{"error": "redis incr failed"})
return
}
另外,INCR 返回的是 int64,Echo c.JSON 可以直接序列化,不用转 string。
立即学习“go语言免费学习笔记(深入)”;
计数器要不要持久化到 MySQL?看场景再决定
纯实时展示(如微博点赞数跳动)用 Redis INCR 完全够用;但若需做数据分析、审计或防刷,就得落库。此时不要在每次点赞都写 MySQL——高并发下 DB 成瓶颈。建议:
- Redis 计数器照常用,MySQL 每小时/每千次增量同步一次
- 用
EXPIRE给 key 加过期时间(如 7 天),防止冷数据堆积 - 如果用 Redis Cluster,确保 key 的 hash tag(如
{post:123}:likes)让同一内容始终落在同个 slot
真正难的不是写这十几行代码,而是想清楚“这个数字到底要多准”——准到毫秒级?还是准到分钟级就够了?这个判断比选哪个函数重要得多。


















