三、Redis缓存问题
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。
1、缓存击穿(热点的key,大并发)
(1)、现象:缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。做电商项目的时候,把这货就称为“爆款”。
(2)、解决方案:
方案一:在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先利用Redis的分布式锁功能去加锁,当操作返回成功时,再进行load db的操作并回设缓存;否则可能同时候的其他进程已经load db并回设到缓存了,这时候重试get缓存的方法。
方案二:大多数情况下这种爆款很难对数据库服务器造成压垮性的压力。达到这个级别的公司没有几家的。所以,对主打商品都是早早的做好了准备,让缓存永不过期。即便某些商品自己发酵成了爆款,也是直接设为永不过期就好了。
2、缓存穿透(某个key查询为空,未做缓存处理)
(1)、现象:缓存穿透是指查询一个数据库一定不存在的key,请求量很大,如果数据库查询对象为空,则不放进缓存。
(2)、解决方案:
方案一:最常见的是采用布隆过滤器,布隆过滤器是一种数据结构,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
方案二:如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,最长不超过五分钟。如果空值能够被缓存起来,这就意味着缓存空间需要存储更多的键,因为这当中可能会有很多空值的键。
3、缓存雪崩(大量的key)
(1)、现象:是指在某一个时间段,缓存集中过期失效。
原因一:数据在某个时间段比较集中的放入了缓存,假设缓存一个小时。一小时过后,这批数据的缓存就都过期了,而此时对这批数据的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。
原因二:缓存服务器某个节点宕机或断网,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
(2)、解决方案:
方案一:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
注意:加锁排队的解决方式分布式环境的并发问题,有可能还要解决分布式锁的问题;线程还会被阻塞,用户体验很差。因此,在真正的高并发场景下很少使用。
方案二:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
方案三:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。