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

关联表查询结果的 Redis 缓存如何设计

  •  
  •   3630cn2023 · 2023-12-25 20:43:16 +08:00 via Android · 2313 次点击
    这是一个创建于 366 天前的主题,其中的信息可能已经有所发展或是发生改变。
    表 T1(id, project_id, score), project 表
    T2(id, type),两个表关联查询的接口 A(userId),B(projectId),如果用 userId ,projectId 作为两个接口的 Redis 缓存 key ,那么接口 C 用通过表 1 的 id 更新数据的时候,A ,B 接口的 Redis 缓存如何更新?
    第 1 条附言  ·  2023-12-26 12:46:41 +08:00
    表 T1(user_id, project_id, score),是 user_id 写漏了
    第 2 条附言  ·  2023-12-26 12:51:50 +08:00
    表 T1(id ,user_id, project_id, score),手机编辑不好用
    9 条回复    2023-12-26 17:03:11 +08:00
    matrix1010
        1
    matrix1010  
       2023-12-25 21:02:08 +08:00
    这类问题没有完美解决方法,想尽可能自动处理的话可以参考 rails 的 Russian doll caching 。另外推荐看看这篇文章 https://blog.the-pans.com/when-and-how-to-invalidate-cache/
    bubble21
        2
    bubble21  
       2023-12-26 09:41:47 +08:00
    业务层使用 Spring-Cache 呢 @Cacheable,@CacheEvict ?
    InkAndBanner
        3
    InkAndBanner  
       2023-12-26 11:51:47 +08:00
    首先问题描述的挺不清晰的
    1. userId 是哪个表的 id ? T1 吗?
    2. 那“如果用 userId ,projectId 作为两个接口的 Redis 缓存 key” 意思是分别做 AB 两个接口的返回值缓存?还是 UserId+projectId 做 key ,score 做 value ?
    如果缓存的是连表查询的结果,并且假设你问的是更新方案的思路 那么俺觉得任意一表有写操作的时候,就应该把缓存失效掉。如果并发不高 直接更新缓存也是可以的。2 楼说的是具体的实现方案了。
    3630cn2023
        4
    3630cn2023  
    OP
       2023-12-26 12:44:29 +08:00 via Android
    @matrix1010
    感谢分享这篇好文章,看了大致思路是建一个 key 的关联表( A ,B ,C 接口缓存的 key ),C 接口更新数据的时候,通过这个 key 关联表查处 A ,B 接口哪些 key 要更新
    这样做成本太大了,就跟你说的一样,这类问题没有完美解决方法,这个文章的思路并不通用,
    3630cn2023
        5
    3630cn2023  
    OP
       2023-12-26 12:48:35 +08:00 via Android
    @bubble21
    @3630cn2023
    A ,B 接口的 key 如何设计,C 接口更新的时候如何更新 A ,B 接口的缓存?
    其实问题就是一份数据被缓存了两份,如何保证数据一致性
    3630cn2023
        6
    3630cn2023  
    OP
       2023-12-26 12:54:23 +08:00 via Android
    @matrix1010 应该说这个文章的实现不通用,思路是可以通用的
    whoami9426
        7
    whoami9426  
       2023-12-26 14:04:31 +08:00
    对于读多写少的场景可以这样设计:
    0. 缓存 key 上添加 sql 的标识以及参数: com.xx.xxmaaper.selectX1: userId: projectId
    1. 获取 sql 中关联的表名,设计存储这样的 json, json 中的 key 为表名,value 为 缓存过的相关的 sql 标识,设个过期时间取个名放到 redis 中
    ```json
    {
    "T1":["com.xx.xxmaaper.selectX1"] ,
    "T2": ["com.xx.xxmaaper.selectX1"]
    }
    ```
    2. 写一个拦截器,拦截修改更新的 sql ,获取上一步中的 json,如果更新的 sql 中所涉及的表存在于这个 json key 中,前缀删除 redis 中这个 key 的 values 也就是缓存过的 sql 结果,同时更新这个 json.
    totoro52
        8
    totoro52  
       2023-12-26 16:57:48 +08:00
    我觉得这是缓存颗粒度大小的问题,根据场景来决定缓存的颗粒度
    sampeng
        9
    sampeng  
       2023-12-26 17:03:11 +08:00
    这就是一个缓存设计问题。可大可小。引入缓存就要仔细解决缓存不一致问题。连表查这种缓存不一致是最熬人的。没什么特别通用的办法。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3265 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 11:45 · PVG 19:45 · LAX 03:45 · JFK 06:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.