缓存——使用场景及遇到的问题
缓存
缓存的作用:
高并发、高性能
高性能:查询速度快
高并发:缓存是走内存的,内存天然就支撑高并发
常见缓存问题:
- 缓存与数据库双写不一致
- 缓存雪崩、缓存穿透、缓存击穿
- 缓存并发竞争
如何保证缓存和数据库一致性:
使用串行化,即:将读请求和写请求放到一个内存队列中。
串行化可以保证缓存与数据库一直,但是会导致系统吞吐量大幅度降低。非严格要求,不要串行化。
经典缓存+DB读写模式
》读:先读缓存,没有读取DB,读出来放入缓存。
》更新:先更新DB,再删除缓存。
缓存雪崩
请求量大时,缓存此时也跌机了,导致大量请求直接请求DB,带着DB也跌机了。
缓存雪崩的事前事中事后的解决方案:
- 事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
- 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
- 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
缓存穿透
缓存中查询不到,大量的请求直接查询DB.
解决方案:
- 每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如
set -999 UNKNOWN
。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。
- 每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如
缓存击穿
某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解决方案:
- 若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。
- 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
- 若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。
缓存不一致解决方案
先删除缓存,再更新数据库。如果数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。
亿流量级高并发场景下缓存不一致问题:
数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。完了,数据库和缓存中的数据不一样了..
解决方案:入队列(jvm内存队列) > (阻塞 超时即新引入的问题)
完续......
以上是 缓存——使用场景及遇到的问题 的全部内容, 来源链接: utcz.com/z/518709.html