分布式缓存
常见缓存中间件:Memcached mongoDB Redis
Memcached: 简单 key-value 数据结构,不支持持久化(可重启缓存功能并不算),不支持集群(客户端自己控制),性能强。
mongoDB: 数据结构非常全面的文档型数据库,支持持久化,支持集群,性能中等。
Redis:技能五种基本数据类型和扩展类型,支持持久化,支持集群,性能强。
缓存穿透:意指透过,表示缓存层形同虚设,没有发挥一点拦截功能,当恶意请求获取的 key 并不存在于数据库中,而无法创建缓存。解决方案:能在业务层校验的则校验,或为该key 存放一个空值。
缓存击穿:单一数据过期或不存在时。解决方案:当key不存在时,先加锁,从数据库读取写入缓存后,再释放锁。
缓存雪崩:同一时刻,缓存大面积过期或redis 宕机。解决方案:设置缓存过期时间随机分布。
更新缓存的过程:就两个操作,更新数据库和更新缓存。由于不是原子操作,所以每一步都有可能失败,怎么才能保证线程交替和中途失败的影响最小呢?
1,先更新缓存,再更新数据库:如果数据库更新失败,缓存也无法回滚。
2,先删除缓存,再更新数据库:假如线程A更新数据库之前,正好线程B又把旧数据更新到缓存中了。
3,先更新数据库,再更新缓存:假如更新缓存失败,总不可能为此回滚数据库吧?
4,先更新数据库,再删除缓存:删除缓存也有可能失败,但概率比较低。两个线程交替完成,也不会有问题。
5,先删除缓存,再更新数据库,再删除缓存:假如线程A删了缓存,B写入了旧缓存,A刚更新完数据库,C又读了缓存,C读到的就是旧数据。 但这需要三个缓程存恰好配合才会出问题,所以,这个才是相对最好的方案。
任何一个方案都不是完美的,但如果剩下1%的问题需要花好几倍的代码去解决,从技术来讲,得不偿失,所以我们要平衡技术成本和收益。