分析:
百万并发下,如果所有的请求都落到db,那么db一定会承受不住,导致宕机。
所以一定要加缓存。
那么加了缓存就保证db不会因为大量并发而导致宕机嘛?缓存有什么问题?
1)缓存击穿:
大量并发请求一个不存在的数据。缓存一定不会命中,然后都落到db。
解决:加null值。如果第一个请求在db返回null,那么也把他加到缓存中,这样后边的请求就会在缓存中命中null值返回。
2)缓存穿透:
大量并发请求一个数据,这个数据在缓存中刚好过期,那么所有的请求就会落到db。
解决:加锁,第一个进来的请求拿到锁之后,查db,查完db把数据放入缓存中,这样后边的数据就会命中缓存。
3)缓存雪崩:
大量缓存中的数据同时失效。
解决:对缓存中的数据设置一个过期时间。
1. 用“SET key value [EX seconds] [NX|XX]”
1)开始我加的本地锁,锁的粒度只能锁住当前服务。
即用synchronized锁住当前服务,这样其实也没有什么问题,就算服务有50个的话,最多也就有50个请求落到db,也可以接收。
2)然后用分布式锁,锁的粒度是锁住当前所有服务。
用redis的“SET key value [EX seconds] [NX|XX]”这个命令:意思就是往redis中存一个值只有在不存的时候,才存。
就用这个特性,来锁住所有服务。
大量请求进来,被网关负载均衡到各个服务。
请求进来如果缓存命中->则直接返回。
缓存没命中:
第一个请求拿到分布式锁->第一个请求查db->查完db放入缓存中->释放锁,这样后边的请求就能从缓存命中。
其他请求自旋。
这样就达到了分布式锁的目的,即大量请求进来,只查一遍db。
但是这样写工作量还是很大的,因为每个放入缓存中的数据都要这么做。
还要考虑:锁的过期时间的设置,原子删锁(执行lua脚本)
2.用redisson
上边的不会有问题,就是工作量太大了,所以我们引入了redisson。
redisson的底层已经为我们考虑周到了,比如原子删锁,锁的过期时间,设置。
伪代码就变成
1. 锁住
2. 查db
3. 释放锁
就这三步也可以保证分布式锁。
==========缓存一致性===========
redisson成功的实现分布式锁,并且能解决缓存击穿,穿透,雪崩问题。
但是如果要是考虑缓存一致性的话,就有些麻烦了。
失效模式(写数据库,删除缓存)和双写模式(写数据库,更新缓存)来解决缓存一致性。
这样只要有写的时候,就会操作缓存,对缓存写有压力。话说回来,经常写的数据也不会放入缓存。
但是工作量还是比较大的。
3.
所有引入了spingCache框架,
读模式:
1)缓存击穿: ache-null-values=true
2)缓存雪崩:spring.cache.redis.time-to-live=3600000
3)缓存击穿: @Cacheable开启缓存的这个注解只能加本地锁,但是对数据库的影响并没有多大。
4)缓存一致性问题解决:
用@CacheEvict注解来实现失效模式
@CacheEvict删除掉我们指定的所有缓存
用@CachePut注解来实现双写模式
@CachePut查完数据,如果数据是最新数据就把他再一次放入缓存
写模式:
这个框架并没有处理,所以这个框架适合读多写少,即时性,一致性要求不高的数据。
缓存总结
最新推荐文章于 2024-11-02 21:00:20 发布