预防缓存击穿
一般来说,传说中的高并发解决方案的一大法宝就是使用缓存。没有缓存时,我们的请求是这样的:
(请求) -> (网关) -> (后端应用) -> (数据库)
/ \<--/ \<--/
(响应) <- /
可以看到,每次都是来一个请求,就查一次数据库。数据库最坏的情况下要在磁盘上查数据,因此当大量流量走向这种情况下的数据库, 数据库就抗不住了,接着就会带着后端应用一起挂。
而当我们使用缓存时,后端应用会先检查是否有缓存,如果有缓存,那么直接返回缓存,否则则查询数据库,并将结果缓存起来留待下次 使用,然后返回数据。
这里有一个漏洞,那就是,如果我知道某个查询无法查到数据,而应用按照上面的逻辑,有结果才缓存,否则不缓存。就可以绕过缓存, 直接把流量打到数据库上了。
预防这种情况的方法也很简单,那就是,就算没有查到结果,也在缓存里记一条。当下次再查询的时候,就还是可以从缓存里拿到一个 “没有结果”的结果了。带来的弊端就是,当数据更新之后,因为缓存里存了”没有结果”,所以拿到的还是没有结果,那咋办呢?解决方案 也挺简单,给缓存结果加TTL(就算是正常的,我们也要加TTL的嘛,不是吗?)。
请注意,这也不是万能的解决方案。更多的还是要在应用层做好校验,尽可能过滤掉本就不存在的数据。否则,因为缓存不存在的东西, 不存在的东西是可以无限构造的,这样的话,挂的可能不是DB,而是缓存了(再多的内存也不够用呀)。
当然了,上面说的这种方式,只是众多缓存策略中的一种,和它所面临的问题。缓存策略还有其他很多种,比如主动缓存等等。只是上面 所说的这种缓存方式比较常用。
没了。
更多文章
本站热门
- socks5 协议详解
- zerotier简明教程
- 搞定面试中的系统设计题
- frp 源码阅读与分析(一):流程和概念
- 用peewee代替SQLAlchemy
- Golang(Go语言)中实现典型的fork调用
- DNSCrypt简明教程
- 一个Gunicorn worker数量引发的血案
- Golang validator使用教程
- Docker组件介绍(一):runc和containerd
- Docker组件介绍(二):shim, docker-init和docker-proxy
- 使用Go语言实现一个异步任务框架
- 协程(coroutine)简介 - 什么是协程?
- SQLAlchemy简明教程
- Go Module 简明教程