首页 > 应用开发 > Java > 使用Redis来做缓存,你应该知道的!
用户请求的 某个key在DB或者缓存中 存在,但是可能正好在当这个key在缓存中到达了失效时间而过期,而此时大量访问该数据请求过来,相当于在缓存中凿开一个缺口,一下子全部打在DB上,造成DB压力压垮DB。
用户通过请求一些缓存和DB中压根都 不存在的数据,致使每次请求都会绕过缓存,请求DB,给DB带来压力
当缓存服务器重启或者 大量缓存的keys过期失效,导致客户端过来的请求全部直接请求到DB中,造成DB压力大而崩溃( 注意这里和缓存击穿不同的是缓存击穿是单个key失效引起,而雪崩是大量key失效)
针对某一热点数据,在获取时可通过加互斥锁使得只有一个请求进行处理访问DB,并将数据放置到缓存中,后续阻塞的请求将直接命中缓存。
这样做的好处就是尽量使得缓存key们的过期时间均匀分散,不至于在同一个时间点大面积缓存过期失效引起雪崩
2)不设置缓存过期时间
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。 ----- 摘自百度百科
LRU缓存那就是将最近最少访问的缓存剔除。redis针对LRU机制提供了实现支持。redis在 redis.conf中提供了配置选项 maxmemory 来配置执行大小的内存数据集。或者在服务器运行时,可在客户端通过 CONFIG SET进行设置。
如果设置为0,那么则表示内存不受限制。当往redis中存放值时,达到我们指定的maxmemory值时,redis提供了多种不同的剔除值策略。可以通过在redis.conf中配置策略
即当达到maxmemory内存限制时,不做数据剔除,直接返回错误
即当达到maxmemory内存限制时,删除最近最少使用的key以保证新的值可以添加进来
即当达到maxmemory内存限制时,删除最近最少使用并且通过expire设置了过期时间的key,以保证新的值可以添加进来
即当达到maxmemory内存限制时,随机删除一些key以保证新的值可以添加进来
即当达到maxmemory内存限制时,随机删除一些设置了过期时间的key以保证新的值可以添加进来
即当达到maxmemory内存限制时,删除一些设置了过期时间并且TTK所剩时间最短的keys以保证新的值可以添加进来
其中volatile-lru、volatile-random、volatile-ttl如果没有满足条件的key可以进行删除,那么它们的行为就和noeviction策略一样。剔除策略也可以在运行时进行设置。
在redis3.0之后,redis针对之前近似LRU算法进行了性能上的改进提升,以及使得LRU算法更加接近准确。通过配置参数maxmemory-samples可以调整样本的数量来为LRU算法获取精度。注意redis的LRU算法并不是LRU真正具体的实现,因为那很耗费内存。但是其实redis计算出的近似值于真实算法值是很相近的。
从上述Redis官方给的测试数据可以看出,同样样本数未5,redis3.0要比之前好一点,当提高样本数之后,更加的好一点,但是与真实LRU算法实现还是差点火候。
我们可以以额外的CPU使用为代价将样本大小增加到10,以接近真实的LRU,并检查这是否会对缓存漏报率产生影响。
在生产环境中,很容易通过 CONFIG SET maxmemory-samples 命令来设置不同的样本值进行实验。
实现缓存的思想其实并不复杂,简单点说,就是查询先从缓存服务器中查询,如果查询到则立即返回,否则再去DB中查询返回然后再放至到缓存当中;;在对数据进行更新操作时,操作DB成功以后,在决定缓存的更新策略(更新缓存or删除缓存)。
spring-data-redis提供了spring抽象缓存的实现,为了借助于spring cache实现缓存,我们需要加载一个RedisCacheManager。
通过RedisCacheManager.create创建出的是默认配置的RedisCacheManager,如果我们想设置一些事务或者预定义缓存则可通过builder进行构建。
RedisCacheConfiguration配置类,可以用来配置比如设置缓存key的过期时间、是否缓存null值、是否启用key前缀、以及key和value缓存过程中二进制数据 的序列化策略。
RedisCacheConfiguration默认key的序列化器是StringRedisSerializer,value的序列化器是JdkSerializationRedisSerializer
大致贴下各层demo。
entity(注意需要序列化)
Dao
Service
config(注意@EnableCaching启用缓存)
Test 以及输入
从上述测试输出我们可以看出,在第一次进行调用(缓存中未存放相关值时,发生了真实调用,查库,然后返回结果),而第二次因为在第一次调用的基础上,缓存中已经有了值,所以直接将缓存结果返回, 完全未触发真实的方法逻辑,所以这里也是很需要注意的地方,如果不太了解,只是跟着网上demo去使用,则可能发生将其他业务逻辑包杂在上述方法中,造成有缓存清空未调用导致bug等。
1)缓存和DB的操作不在一个事务中进行,很可能缓存的操作和DB的操作其中一个成功一个失败,导致数据不一致。 2)多线程对缓存进行更新操作时,可能导致旧数据被更新至缓存中。
即读缓存,未命中,则从数据库中取,成功之后将数据放到缓存中
即读缓存,若命中,则成功返回
先更新数据库,然后删除缓存
着重与最后一条,当数据发生变化时,针对缓存如何进行处理?可能有以下几种情况:
此方案如果说更新数据库的时候,数据库宕机导致DB更新失败,造成了数据不一致
此方案主要存在两个问题: 1).假如线程A先更新了数据,然后准备做更新数据库,更新缓存的操作,此时线程B也更新了相同的数据,更新数据库更新缓存。按道理线程B的数据应该是最新的,但是因为多线程的时序问题,很可能最终缓存被更新成了旧值。 2).并不是所有缓存是热点数据,有可能此缓存不会被经常读取,而如果每次更新数据的时候,都要去更新一下缓存,就造成了不必要的性能和内存消耗。
一直想整理出一份完美的面试宝典,但是时间上一直腾不开,这套 一千多道面试题宝典,结合今年金三银四各种大厂面试题,以及 GitHub 上 star 数超 30K+ 的文档整理出来的,我上传以后,毫无意外的短短半个小时点赞量就达到了 13k,说实话还是有点不可思议的。
需要完整版的小伙伴,可以一键三连后, 点击这里!
内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、SpringBoot、SpringCloud、RabbitMQ、Kafka、Linux等技术栈(485页)
内容涵盖:Java基础、JVM、高并发、多线程、分布式、设计模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、数据库、云计算等
由于篇幅限制,详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
需要完整版的小伙伴,可以一键三连后, 点击这里!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70000428/viewspace-2787168/ ,如需转载,请注明出处,否则将追究法律责任。