1. 做数据缓存
2.缓存穿透(空值被查询):布隆过滤器,保存空值
3.缓存雪崩(大面积同时失效): 存储的数据同时失效 (设置过期时间随机),造集群
4.缓存击穿(单个热点key失效):(集群万能csdn搜redis集群),加锁(只让一个去查热点key,然后放入缓存 ,其余直接就可以查缓存了)
synchronized (this):(不适合分布式,一个服务对应一个线程):当前对象进行加锁,springboot对象默认单例,可以用此方法,也可以换为ReentrantLock。synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略(说白了消耗性能更低)。 注意:避免在程序在写入数据进入redis的时候,对redis进行了盘空操作,会对数据库进行两次查询。 故,查询完毕数据库 并 写入redis是一个类似于原子操作 !!
加锁:
1.相较于本地锁,更慢,分布式锁吃性能,适合金融业务和秒杀。
2.加锁记得设置超时时间(会造成两个问题) , 免得 程序异常一直没有释放锁 。
设置锁的过期时间和设置一把锁应该是原子操作!!不然可能锁设置了,服务器宕机了,分布式另一个程序一直得不到锁。
实现:stringRedisTemplate.opsForValue().setIfAbsent(key,value,time,time Unit)
删锁:1.业务超时 ,删锁删除的是别人的锁。所以占用锁的时候设置一个uuid,每个人的uuid都不一样,自己删除自己的锁。
2.即是设置了uuid 但是,还是会删除别人的锁
所以获取值验证是不是自己的锁+删除锁 是一个组合的原子操作
(1)自动锁的时间续期 (2)TTL:time to live,自动设置过期时间 ,但是一旦自己设置了超时时间,会造成锁的时间不会自动续期,从而导致 当业务时间大于 锁的时间的时候,自己的锁被释放了,但是 业务还没有执行完毕,导致 其他线程占用锁而删除,可当第一个线程业务执行完毕时候删除锁的时候,删除的是第二个锁。因为分布式锁事务里面的锁一般都是一把锁 。
最佳:直接设置30秒锁的超时时间 ,自动到期释放锁,业务时长一般不会超过30秒,超过30秒业务肯定会出现问题,手动解锁
读锁:相当于无锁
不能同时写,可以同时读,写的时候不能读
闭锁:相当于等前面的所有锁释放,自己才能释放
信号量:获得信号量和释放信号量
(1)双写模式:先改数据库 ,再改缓存
漏洞:当1线程在 写入往redis写缓存之前卡住了,2号线程过来直接完成了双写模式,再切换1线程对缓存进行写,则2号线程是后发生的,但是缓存写进去的确是1号线程。
导致数据库保存的是后发生二号线程写的数据,redis缓存是缓存的一号线程的数据
解决:1.加锁,双写模式是一套操作
2.加超时时间,但是要允许一定时间的数据库的不一致,例如菜单业务。
(2) 失效模式:改完数据库,删了redis缓存,下次查询请求会先先查数据库再写入redis。
漏洞(不过发生概率较低):1.当缓存中没有数据,线程二更新数据(更新数据的时间是比较长的)
线程三读取数据(读取数据时间是比较短的)
线程二写完数据,删除缓存(删除空缓存),线程三卡住,等待二号线程缓存删除完毕,更新缓存,此时缓存是就的数据库中的数据,造成了不一致性的问题。(此时即使写与)
.
解决:经常性修改的数据不需要放入缓存 ,免得造成不一致问题。
总结:加过期时间可以解决大部分读写不一致的问题,也可以加读写锁保证不发生数据不一致性的问题。
遇到一致性要求高的 ,去数据库查数据。遇到一致性要求不高的,直接缓存。或者canal技术(就是一个消息中间键),mysql更新 ,便通知redis更新
第一步
启动方法上加@EnableCache
配置的结果
注解操作
1.默认永不过期 。需要指定过期时间(ttl)
2.将存缓数据转化为json数据
springcache默认不能将缓存数据转化为json,所以需要自己放置一个CacheRedisconfigration来重新配置springcache缓存机制
自己配置了rediscacheconfigration 则 会造成 yml文件配置的springcache 属性全部失效,例如自己配置的过期时间 ,因为你自己指定了配置!!
解决缓存穿透
同时修改多种缓存操作
所以同一类型的数据指定同一分区,而且分区名作为前缀
1.缓存穿透:查询永不存在
2.缓存击穿:同时查询一个正常过时的数据
解决 1.(加本地锁)所有的服务加上锁,查询即使是个服务 查询十次数据库也不是很慢对吧?
( 分布式锁太浪费性能。又要网络IO。万一碰上阻塞全完蛋了。)
3.缓存雪崩:大量同时过期(不建议加随机过期时间 , 你大量key加进去就是不同时间的)
4.写模式 (数据一致性):加读写锁(太慢了)
引入canal
读多写多的直接走数据库
总结:常规数据直接springcache(度多写少,一致性要求不高,及时性)记得设置过期时间就行
特殊数据特殊设计
https://blog.csdn.net/weixin_45699541/article/details/126208427?spm=1001.2014.3001.5501
![在这里插入图片描述](https://img-blog.csdnimg.cn/d8c8f6003dcb48c899fd068363663171.png
redis20种用法
ps:为什么 redisson作为分布式锁比 使用 redistemplate (unset)好 ,因为redisson 有wactdog机制
原生redis 存在问题,
假如你使用原生redis 作为分布式锁 ,在你服务器宕机的时候 ,程序执行到一半还没有释放锁,锁会得不到释放。其余线程得不到锁,会阻塞。
使用redisson: 内部有watchdog机制 ,每隔几秒给锁加时,假如你服务器宕机了 。锁会超时自动释放,不仅不会死锁 而且也免费给锁续命 。
锁可以无线续命,但是 假如 程序发生了异常, 锁就一直得不到释放,那么该怎么办呢?
解决:如果程序释放锁操作时因为异常没有被执行,那么锁无法被释放,所以释放锁操作一定要放到 finally {} 中;