V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
justdoit123
V2EX  ›  科技

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

  •  
  •   justdoit123 · 310 天前 · 970 次点击
    这是一个创建于 310 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

    5 条回复    2024-02-06 17:40:14 +08:00
    rrfeng
        1
    rrfeng  
       310 天前
    INCR 返回 1 就是不存在
    justdoit123
        2
    justdoit123  
    OP
       310 天前
    @rrfeng 我也想过这个方法。就是语义没那么明朗。
    Goooooos
        3
    Goooooos  
       310 天前   ❤️ 1
    lua 脚本对计数操作,能解决 2 、3
    性能不会差太多
    justdoit123
        4
    justdoit123  
    OP
       310 天前
    @Goooooos 原来 redis 还支持操作~ 谢谢!
    wu00
        5
    wu00  
       310 天前   ❤️ 1
    检查是否存在在 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;
    }
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2770 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 13:23 · PVG 21:23 · LAX 05:23 · JFK 08:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.