Redis重要知识点
缓存穿透、雪崩、缓存击穿
缓存穿透
描述:大量查询缓存中和数据库中都没有的数据,就会导致数据库的访问量变大,容易出现问题。
解决方法:遇到缓存和数据库中都没有值的数据,就在缓存中设置一个空值,并且设置一个过期的时间,这样就不会大量的访问到数据库。而且当数据库中有这个值了,因为过期时间的存在,缓存也会更新这个正确的值。
雪崩
描述:在缓存中,大面积的数据,同时过期,这时数据库的压力就会骤增,如果严重的话,数据库可能就会崩掉
解决办法: 在设置数据过期时间的时候,可以使用随机数设置,这样可以有效的避免数据同时过期。
缓存击穿
描述: 有一些热点数据,访问量特别大,当这个数据过期的时候,会有很多请求同时访问到数据库,数据库的压力又变大了。
解决办法: 这种情况可以给给访问数据库的方法加锁,同一时间只允许一个请求访问数据库,其他请求得到锁之后要再查一下缓存,如果有值了就直接return。
本地缓存和分布式缓存
本地缓存
不做分布式的情况
开发注意:缓存穿透和雪崩问题比较好解决,
-
缓存穿透直接给数据库中也没查到的数据设置一个null或者-1之类的值就可以。
-
雪崩需要在set的时候设置随机的过期时间。
-
缓存击穿需要在代码上加锁,加锁的位置可以使用
public String getValue(){ if(缓存中没有){ //给对象加锁 synchronized(this){ //得到锁之后再判断一次缓存中是否有数据 if(缓存中没有){ //查询数据库 }else{ return val; } } }else{ //返回值 return val; } } //也可以使用给方法加锁的方法 public synchronized void setValue2(){ ... }
要注意的是,要把从数据库中查到的值放到缓存中后,再释放锁。
分布式锁
在使用分布式的情况下,每个服务器加锁只是给自己的内部容器加锁了,如果按照本地缓存的方式加锁,那么有几台服务器,就会访问数据库多少次。正常情况下,也就够用了,不会给数据库带来多大的压力,但是如果有特殊需求的话,那就需要使用分布式锁了。
问题分析: 本地的锁不生效的原因是因为各个服务器的容器不互通,彼此之间访问不到,所以我们需要使用一个公共的空间来进行加锁,这个公共的空间我们可以使用redis。
解决方案 :
-
在想要上锁的时候在redis中存一个key为lock的字段(可自定义),并在设置key的时候带上NX,这样只有key不存在的时候才设置key的值,当有多个请求同时set lock的时候,只有一个会生效,这样就起到了锁的作用。
-
set lock的时候也要加一个过期的时间,这个时间需要用set方法的EX或者PX来设置(保证原子性),防止极端情况下代码不能向下执行(比如断电,这样即使使用catch也不能释放锁),造成的死锁问题。
-
lock的value需要设置成一个随机数,比如UUID,这样当业务结束了需要释放锁的时候,可以进行判断,如果是自己的锁,就释放,不然容易把其他线程的锁释放掉(有可能在执行业务的时候用的时间过长,过了时间期限后,锁就会自动释放掉)
-
删除lock的时候需要使用官方给出的脚本方式删除掉,以保证原子性
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
这是java中用法的一个实例
以上都总结自尚硅谷雷丰阳的视频,有兴趣的可以看原版的视频。
该部分是50-57讲