如何设计一个 redis 计数缓存?

93 天前
 justdoit123

场景是这样的:需要对通知未读数量进行缓存。大概有如下三种操作:

  1. 读取这个“未读数量”,如果 redis 缓存里不存在,则从 db 里计算出这个值,然后写入 Redis 缓存,即调用 Redis 的 SET 。
  2. 增大这个数值。当有新的通知发给用户的时候,需要调用 INCR 把计数加一。
  3. 减小这个数值。当用户在客户端点击一条未读通知时,需要调用 DECR 把即计数减一。

这存在一个 BUG ,就是 2 、3 两种操作都没有去检查缓存是否存在,那么当缓存失效之后,直接发生了 2 、3 两种操作的时候,缓存里的这个 “未读数量” 就会变成 1 或者 -1.

现在准备在进行 2 、3 操作的之前,都先进行缓存是否存在的检查。

突然想到,为什么 Redis 不提供 INCR/DECR 缓存不存在的时候,就报错的版本?这样就不用每次都去检查缓存是否存在。只需要处理这个报错,然后再去初始化缓存即可,这样性能不是更好?

704 次点击
所在节点   科技
5 条回复
rrfeng
93 天前
INCR 返回 1 就是不存在
justdoit123
93 天前
@rrfeng 我也想过这个方法。就是语义没那么明朗。
Goooooos
93 天前
lua 脚本对计数操作,能解决 2 、3
性能不会差太多
justdoit123
93 天前
@Goooooos 原来 redis 还支持操作~ 谢谢!
wu00
93 天前
检查是否存在在 INCR 会存在并发问题。
只能是封装/重载一个 Increment ,通过 Lua 脚本保证原子性。
```
public long IncrementIfKeyExists(string key)
{
var db = redis.GetDatabase();
var script = LuaScript.Prepare("if redis.call('EXISTS', @key) == 1 then return redis.call('INCR', @key) else return nil end");
var result = (long?)db.ScriptEvaluate(script, new { key });
return result ?? 0;
}
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1014710

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX