读路径先想清楚
多级缓存的读路径只有两种写法:先读 L1 再读 L2,或者直接读 L2 再由 L1 预热。前者延迟最低,但对 L1 的过期时间更敏感;后者实现简单,代价是每次都多一次远程调用。
项目里的选择是第一种,但给 L1 设置了明显更短的过期时间。理由很直接:L1 是加速器,不是数据源。
失效顺序必须由近到远
更新数据时的删除顺序,比读路径更容易出错。正确的顺序是:先删 L2(Redis),再删 L1(本地内存)。
反过来做会留下一个很小但真实存在的窗口:L1 删掉之后、L2 删除之前,另一个副本读到旧的 L2 值并重新写回自己的 L1,于是旧值在本地内存里活到了过期时间。并发越高,这个窗口越容易被命中。
缓存穿透与空值
查不到的数据反复打到数据库,是最常见的问题。简单的兜底是把空结果也缓存一小段时间,窗口短到不会影响正常写入后的可见性。
更严格的做法是加布隆过滤器,但引入新组件之前先问一句:这个接口真的会被不存在的键高频访问吗。多数业务并不需要。
序列化格式也影响一致性
L1 存对象引用、L2 存字节流,两边对同一个值的理解可能并不一致。日期精度、枚举序列化、空字符串与 null 的区分,都会在两边产生细微差别。
统一的做法是:进缓存之前先序列化成同一种格式,L1 存的也是这份字节流。多一次反序列化,换掉一整类很难排查的问题。
什么时候不要缓存
写多读少的数据不适合缓存,一致性要求高的数据也不适合。判断标准可以简化成一句:这份数据在过期窗口内变旧,业务能不能接受。
不能接受,就不要缓存,让数据库去扛。
评论 0
还没有评论,来聊聊你的看法。