辛德蕾拉|面试官:Redis缓存了解吗?面对这11道题是否有很多问号?( 十 )


辛德蕾拉|面试官:Redis缓存了解吗?面对这11道题是否有很多问号?解决方式很简单 , 每次系统 A 从数据库中只要没查到 , 就写一个空值到缓存里去 , 比如 set -999 UNKNOWN 。 然后设置一个过期时间 , 这样的话 , 下次有相同的 key 来访问的时候 , 在缓存失效之前 , 都可以直接从缓存中取数据 。
缓存击穿
缓存击穿 , 就是说某个key非常热点 , 访问非常频繁 , 处于集中式高并发访问的情况 , 当这个key在失效的瞬间 , 大量的请求就击穿了缓存 , 直接请求数据库 , 就像是在一道屏障上凿开了一个洞 。
解决方式也很简单 , 可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁 , 等待第一个请求构建完缓存之后 , 再释放锁 , 进而其它请求才能通过该 key 访问数据 。
9、如何保证缓存与数据库的双写一致性?面试官心理分析你只要用缓存 , 就可能会涉及到缓存与数据库双存储双写 , 你只要是双写 , 就一定会有数据一致性的问题 , 那么你如何解决一致性问题?
面试题剖析一般来说 , 如果允许缓存可以稍微的跟数据库偶尔有不一致的情况 , 也就是说如果你的系统不是严格要求“缓存+数据库” 必须保持一致性的话 , 最好不要做这个方案 , 即:读请求和写请求串行化 , 串到一个内存队列里去 。
串行化可以保证一定不会出现不一致的情况 , 但是它也会导致系统的吞吐量大幅度降低 , 用比正常情况下多几倍的机器去支撑线上的一个请求 。
Cache Aside Pattern
最经典的缓存+数据库读写的模式 , 就是 Cache Aside Pattern 。- 读的时候 , 先读缓存 , 缓存没有的话 , 就读数据库 , 然后取出数据后放入缓存 , 同时返回响应 。- 更新的时候 , 先更新数据库 , 然后再删除缓存 。
为什么是删除缓存 , 而不是更新缓存?
原因很简单 , 很多时候 , 在复杂点的缓存场景 , 缓存不单单是数据库中直接取出来的值 。
比如可能更新了某个表的一个字段 , 然后其对应的缓存 , 是需要查询另外两个表的数据并进行运算 , 才能计算出缓存最新的值的 。
另外更新缓存的代价有时候是很高的 。 是不是说 , 每次修改数据库的时候 , 都一定要将其对应的缓存更新一份?也许有的场景是这样 , 但是对于比较复杂的缓存数据计算的场景 , 就不是这样了 。 如果你频繁修改一个缓存涉及的多个表 , 缓存也频繁更新 。 但是问题在于 , 这个缓存到底会不会被频繁访问到?
举个栗子 , 一个缓存涉及的表的字段 , 在 1 分钟内就修改了 20 次 , 或者是 100 次 , 那么缓存更新 20 次、100 次;但是这个缓存在 1 分钟内只被读取了 1 次 , 有大量的冷数据 。 实际上 , 如果你只是删除缓存的话 , 那么在 1 分钟内 , 这个缓存不过就重新计算一次而已 , 开销大幅度降低 。 用到缓存才去算缓存 。
其实删除缓存 , 而不是更新缓存 , 就是一个 lazy 计算的思想 , 不要每次都重新做复杂的计算 , 不管它会不会用到 , 而是让它到需要被使用的时候再重新计算 。 像 mybatis , hibernate , 都有懒加载思想 。 查询一个部门 , 部门带了一个员工的 list , 没有必要说每次查询部门 , 都里面的 1000 个员工的数据也同时查出来啊 。 80%的情况 , 查这个部门 , 就只是要访问这个部门的信息就可以了 。 先查部门 , 同时要访问里面的员工 , 那么这个时候只有在你要访问里面的员工的时候 , 才会去数据库里面查询1000个员工 。
最初级的缓存不一致问题及解决方案
问题:先更新数据库 , 再删除缓存 。 如果删除缓存失败了 , 那么会导致数据库中是新数据 , 缓存中是旧数据 , 数据就出现了不一致 。


推荐阅读