一、Redis作为缓存的作用
Redis一般是作为MySQL这种数据库的缓存,因为MySQL是对硬盘进行操作的,这样的查询速率是很低的,其次虽然Redis作为内存数据库,本身容量并不是很大,但是由于Redis中一般都是存的热点数据,根据“二八原则”,却可以满足大多数的查询请求,对于Redis作为缓存的情况,需要注意两点,缓存的更新策略和缓存使用的注意事项。
二、缓存的更新策略
主要分为
1.旁路缓存
查询时应用程序先从Redis中查询,如果命中,直接返回,如果没有命中,从MySQL中查询数据,然后写入Redis,再返回数据。如果是写操作,则是先在MySQL中完成数据更新,然后删除/失效Redis中对应的数据,这时有很小的概率发生数据不一致的情况、
2.穿透写
逻辑和旁路缓存策略很类似,只是在写操作的时候,强制只有缓存和MySQL都写入完成了才会返回,保证了强一致性,但是需要引入新的结构来保证缓存和MySQL都完成。
3.异步写
性能最高,但是不同步的风险也最大,而且Redis本身并不支持,需要引入额外数据结构或者使用tair(阿里开发的类似Redis的组件)。用于写操作很多但是对一致性要求不是很高的场景,本质是Redis先接收写操作,但是只有达到一定数量或者时间才会异步的,批量的写入MySQL中。
4.穿透读
这个是通过引入额外的Redis代理组件来代替应用程序在缓存没有命中的情况下的MySQL数据写入Redis中,好处是数据结构更加简洁,将写入Redis这一步抽象了出来。
其中1是主流的经典策略,其他本质上是对1的拓展,而1又有两种常见的实现方式
1.定期生成缓存
通过日志等方式统计一天或者规定时间的信息,在凌晨等流量低谷时期运行脚本,通过对信息进行复杂的排序来得到热点信息,然后提前写入到Redis中,并设置对应的过期时间(如24h),等到第二天再此执行这个逻辑。这样的好处是无需缓存预热,等到用户进行大量访问时,Redis已经设置好了热点信息。但是缺点就是对于突发信息,如春晚,可能只有当天有大量访问,热点信息的时效性并不好。
2.实时生成缓存
通过设置内存上限,不停的写入信息,当内存达到上限时,有这么几种缓存淘汰策略(Redis中是八种,因为还有设置过期时间和没设置过期时间之分)
1.FIFO:先进先出,就是删除存在最久的缓存。
2.LRU:删除最久未访问的。
3.LFU:删除最少访问的。
4.Random:就是随机删除,因为不太科学,所以很少使用。
三、缓存注意事项
注意这些注意事项是针对旁路缓存策略的,其他三种还有其他的注意事项。
1.缓存预热
实时生成策略的弊端,因为刚开始Redis缓存中是空的,每次都要从MySQL中查询才能写入缓存,导致刚开始时MySQL的压力就会比较大。
参考解决方法:可以结合实时生成和定时生成策略,先通过定时生成策略生成一批热点信息并写入Redis中,然后再进行实时生成策略。
2.缓存穿透
Redis缓存中没有,MySQL中也没有,但是用户却一直查询并且反复查询,导致MySQL的压力很大。原因可能是业务设置不合理,对非法数据的检验不太合理或者数据被误删了,或者是黑客恶意入侵。
参考解决方法:即使MySQL中没有,也可以返回一个如null等非法值来设置为缓存的value,还可以通过引入布隆过滤器来检测该数据是否存在来解决。
3.缓存雪崩
同一时间,大量的Key同时过期,但是这时候又有很多请求来查询,导致MySQL服务器压力倍增。原因可能是Redis集群出现大面积宕机,导致缓存失效,又或者是之前某一时期大量Key导入,而且设置为了统一的过期时间。
参考解决方法:针对第一种情况,加强监控报警,即时知道Redis集群的情况,针对2设置过期时间时加入一个随机因子,让它们的过期时间不相同。
4.缓存击穿
热点Key突然过期,但是这时仍然对它/它们有很多请求,导致MySQL压力又倍增。本质上是缓存雪崩的一种特例。
参考解决方法:基于统计发现热点信息,设置其过期时间很长或者直接永久不过期,但是这样对业务的架构需要调整,还有就是通过分布式锁让服务进行必要的降级,限制同一时间的并发量,虽然这样有些用户的体验不太好,但是好过服务器直接宕机。