likes
comments
collection
share

Redis实现简单分布式锁

作者站长头像
站长
· 阅读数 16

缓存安全问题

Redis实现简单分布式锁

缓存穿透😀

查询一个一定不存在的数据,由于缓存是不命中,将去查数据库,但数据库也是无此数据,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每此请求都要去请求DB,是去缓存意义

产生问题:

利用不存在的数据进行攻击,数据库瞬间压力增大,最终导致崩溃

解决:

null结果缓存,并加入短暂过期时间,或者布隆过滤器

缓存雪崩😀

我们在设置缓存时key采用了相同的过期时间,导致缓存在某一时刻同时失效,请求同时转发到DB,DB瞬时压力过大雪崩。

解决:

原有的失效时间基础增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间重复率就会降低,就很难引发集体失效的事件。

缓存击穿😀

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被高并发的访问,是一种"热点数据"。

如果这个key在大量请求同时进来前正好失效,那么所有对这个key的数据查询都落到db,称为缓存击穿

解决:

加锁,大量并发只让一个去查,其他人等待,查到以后释放锁,其他人获取到锁,先查缓存,就会有数据,不用去DB。

分布式如何如何加锁🤔

Redis实现简单分布式锁

微服务下本地锁,只能锁住当前进程,So需要分布式锁

分布式锁基本原理

Redis实现简单分布式锁

同时去一个地方占锁,如果占到,就执行逻辑。否则必须等待,直到其他线程释放锁。占锁可以去redis可以去DB,可以去任何能访问的地方,等待可以用自旋方式。

解决方式1(setNX):

Redis实现简单分布式锁

引出问题1:

setNX占到了锁,如果在执行业务逻辑中出现异常,或者在执行过程中宕机,没有删除锁逻辑,会造成死锁。

解决办法:

设置锁的自动过期,即使没有删除,会自动删除。

/**
	 * Set {@code key} to hold the string {@code value} if {@code key} is absent.
	 *
	 * @param key must not be {@literal null}.
	 * @param value must not be {@literal null}.
	 * @return {@literal null} when used in pipeline / transaction.
	 * @see <a href="https://redis.io/commands/setnx">Redis Documentation: SETNX</a>
	 */
	@Nullable
	Boolean setIfAbsent(K key, V value);
  

可以用RedisTemplate的setIfAbsent就是redis里面的setNX;

public ResponseData<Object> queryList()
    {
        //这里使用UUID原因是如果自己有自己的value防止误删别人的value
        String uuid = UUID.randomUUID().toString();
        //这里使用setNXEX因为要设置过期时间,为了确保原子性所以使用EX在setkey的同时直接设置过期时间
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
        if (lock){
            System.out.println("获取到锁,正常执行业务逻辑......");
            Object list;
            try {
                //拿到锁后开始执行业务逻辑
                list  = getList();
            }finally {
                //执行完业务逻辑后删除自己的value
                //这里使用lua脚本
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                redisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList("lock"),uuid);
            }
            return new ResponseData<>(list);

        }else {
            System.err.println("没获取锁重新尝试获取锁。。。。。");
            //如果获取锁失败,进行自旋重试
            return queryList();
        }
    }

    public Object getList()
    {
        if (redisUtil.get("list") == null){
            redisUtil.set("list",coCommodityService.list());
        }
        System.out.println("业务逻辑正在执行》》》》》》》");

        return redisUtil.get("list");
    }

Redis实现简单分布式锁

转载自:https://juejin.cn/post/7140262296307105829
评论
请登录