- 为什么要用缓存?
- 什么场景下不适合使用缓存
- 用缓存可能出现哪些问题?
- 如何解决这些问题?
为什么要用缓存?
Web 应用对数据库的访问中,读的操作此处要远远大于写的操作次数,每一次读都将是对磁盘的一次 I/O 操作,这是一个非常漫长的过程,在高性能和高并发场景下,数据库很难做到。
Redis 把数据直接放在内存当中,服务器直接去读取内存中的数据,这样性能就会得到很大的提高,高并发的情况下,可以减轻数据库的压力。
不适合使用缓存的地方
缓存大多数是基于内存存储的,内存相对于硬盘来说,空间小,成本高。所以用缓存经常存储一些主要的数据,比如 session 信息等。
业务读操作是否多?
如果读操作多,可以用缓存,如果写操作多,频繁的修改数据库,修改缓存,完全没有必要用缓存。
内存容量有限,业务数据量很大,
比如几百兆的文件,使用缓存也是没有必要的。
是否是热点数据?
如果是一些冷数据,放入缓存,命中率不仅低,还占用空间。要看这些数据是不是业务常用的数据。
使用缓存可能出现哪些问题?
- 缓存雪崩
- 缓存穿透
- 缓存击穿
- 缓存和数据库双写一致性问题。
缓存雪崩
setRedis(Key,value,time);如果设置了相同的过期时间,客户端访问,大面积的缓存失效,此时相当于没有缓存,高并发的请求访问数据库,直接打崩数据库。所以我们可以设置随机的过期时间,让缓存不在同一时间失效。
增加本地ehcache,用户请求来了以后先查询本地的ehcahe 在查redis,最后再查数据库,将数据库结果写入本地的ehcache 和redis。
缓存穿透
用户传一些不符合约定的参数,此时缓存中一定没有该数据,(当然数据库中也没有),绕过缓存去访问数据库,占用数据库资源,拖垮数据库。
解决: 整体思路是不直接访问数据库就行。
- 我们可通过前端和后端校验,传的参数是否正确,如果不正确直接返回即可
- 我们可以在缓存中存入这些不存在数据(即空对象),即 key null 的形式,这样请求不会直接访问数据库,访问缓存后就直接返回了,但此时还是存在问题,一些不好的用户,不止传一种不符合规则的参数,例如 每次传的key 都不同,(-1,-2,-3,-4,…),这样就会占用大量的缓存。所以给这些空对象设置较短的过期时间。但是这样情况依然不能很有效的解决客户端长时间的攻击。
- 在数据库之前加布隆过滤器。如下图所示:
缓存击穿
大量访问某一热点key,可是key 失效,大量请求访问数据库,造成数据库打崩,所以可以设置热点key永不失效。
缓存和数据库双写一致性问题
用户更新数据,是先更新缓存,还是先更新数据库呢?更新操作发生后,查询数据,此时查到的数据可能不是最新值。也就是说当更新或者删除操作发生时,导致数据不一致。防止篇幅过程,下一篇我们 缓存和数据库双写一致性问题。
总结
无论是缓存雪崩,击穿,穿透,都是缓存失效或者缓存中没有数据,直接访问数据库,造成数据库服务器压力,拖垮数据库,我们解决这些问题的所有思路无非是减少同时访问数据库的请求,或者不访问数据库。所以让缓存起作用,缓存挂掉了,我们就限流,设置本地缓存。所以预防redis挂掉很重要。
如何预防呢?
- 我们可以采用redis 集群等高可用的主从架构,避免redis 挂掉。
- 如果redis真的挂了,我们可以设置本地缓存(ehcache) + 限流(hystrix)+熔断机制,防止数据库崩掉。保证服务正常运行。
- redis 混合持久化,重启后快速恢复缓存数据。