预防缓存击穿

一般来说,传说中的高并发解决方案的一大法宝就是使用缓存。没有缓存时,我们的请求是这样的:

(请求) -> (网关) -> (后端应用) -> (数据库)
           /    \<--/         \<--/
(响应) <- /

可以看到,每次都是来一个请求,就查一次数据库。数据库最坏的情况下要在磁盘上查数据,因此当大量流量走向这种情况下的数据库, 数据库就抗不住了,接着就会带着后端应用一起挂。

而当我们使用缓存时,后端应用会先检查是否有缓存,如果有缓存,那么直接返回缓存,否则则查询数据库,并将结果缓存起来留待下次 使用,然后返回数据。

这里有一个漏洞,那就是,如果我知道某个查询无法查到数据,而应用按照上面的逻辑,有结果才缓存,否则不缓存。就可以绕过缓存, 直接把流量打到数据库上了。

预防这种情况的方法也很简单,那就是,就算没有查到结果,也在缓存里记一条。当下次再查询的时候,就还是可以从缓存里拿到一个 “没有结果”的结果了。带来的弊端就是,当数据更新之后,因为缓存里存了”没有结果”,所以拿到的还是没有结果,那咋办呢?解决方案 也挺简单,给缓存结果加TTL(就算是正常的,我们也要加TTL的嘛,不是吗?)。

请注意,这也不是万能的解决方案。更多的还是要在应用层做好校验,尽可能过滤掉本就不存在的数据。否则,因为缓存不存在的东西, 不存在的东西是可以无限构造的,这样的话,挂的可能不是DB,而是缓存了(再多的内存也不够用呀)。

当然了,上面说的这种方式,只是众多缓存策略中的一种,和它所面临的问题。缓存策略还有其他很多种,比如主动缓存等等。只是上面 所说的这种缓存方式比较常用。

没了。


更多文章
  • Go语言解析GBK编码的xml
  • Golang log 源码阅读
  • 使用Go语言实现一个异步任务框架
  • Golang flag源码阅读及自己实现
  • Go使用gdb调试
  • Golang ASM简明教程
  • Golang context源码阅读与分析
  • Golang中的并发控制
  • 善用闭包(closure)让Go代码更优雅
  • Golang的可选参数实践
  • FreeBSD ipfw使用教程
  • Golang expvar库源码阅读
  • Golang SQL生成库 Squirrel 教程及源码阅读
  • Golang validator使用教程
  • 使用Redis的Stream模块实现群聊功能