美文网首页
分布式锁-Redis

分布式锁-Redis

作者: GIT提交不上 | 来源:发表于2020-05-14 16:10 被阅读0次

    一、缓存常见问题

      对于访问频率高、读多写少、一致性要求不高的数据适合做缓存。

    1.1 缓存穿透

      缓存穿透:缓存和数据库中都没有的数据,不断发起请求,如发起为id为-1的数据或id为特别大不存在的数据。
      解决方案:

    • 接口层增加校验,校验用户权限 & 对不合理的数据进行过滤
    • 使用布隆过滤器
    • 把查询结果为空的数据也进行缓存,设置较短的过期时间

    1.2 缓存击穿

      热点数据:缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来。
      解决方案:

    • 设置热点数据永不过期
    • 二级缓存
    • 互斥锁

      互斥锁示例代码如下所示:

    static Lock reenLock = new ReentrantLock();
     
    public List<String> getData04() throws InterruptedException {
        List<String> result = new ArrayList<String>();
        // 从缓存读取数据
        result = getDataFromCache();
        if (result.isEmpty()) {
            if (reenLock.tryLock()) {
                try {
                    System.out.println("我拿到锁了,从DB获取数据库后写入缓存");
                    // 从数据库查询数据
                    result = getDataFromDB();
                    // 将查询到的数据写入缓存
                    setDataToCache(result);
                } finally {
                    reenLock.unlock();// 释放锁
                    }
     
                } else {
                    result = getDataFromCache();// 先查一下缓存
                if (result.isEmpty()) {
                    System.out.println("我没拿到锁,缓存也没数据,先小憩一下");
                    Thread.sleep(100);// 小憩一会儿
                    return getData04();// 重试
                }
            }
        }
        return result;
    }
    

    1.3 缓存雪崩

      缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是并发查多条数据
      解决方案:

    • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
    • 二级缓存

    【分布式】缓存穿透、缓存雪崩,缓存击穿解决方案
    缓存穿透、缓存击穿、缓存雪崩区别和解决方案

    二、Redis实现分布式锁

    2.1 锁超时

      线程一获取到锁,但是未执行expire设置过期时间(setnx不支持设置过期时间)就Down掉了,此时就变成了死锁。

    if(setnx(lock_sale_商品ID,1) == 1){
        expire(lock_sale_商品ID,30)
        try {
            do something ......
        } finally {
            del(lock_sale_商品ID)
        }
    }
    

      使用set 指令的可选参数,设置过期时间:

    set(lock_sale_商品ID,1,30,NX)
    

    2.2 锁误删

      由于线程一执行过慢,锁自动释放了,线程二获取到锁,此时线程一执行完毕删除锁,造成锁误删。
      解决方法:在 del 释放锁之前做一个判断,验证当前的锁是不是自己加的锁,将当前的线程 ID 当做 value。

    //加锁
    String threadId = Thread.currentThread().getId()
    set(key,threadId ,30,NX)
    
    //解锁
    if(threadId .equals(redisClient.get(key))){
        del(key)
    }
    

      存在问题:判断和释放锁是两个独立操作,不是原子性
      解决方法:使用lua脚本执行保证原子性。
      优化:让获得锁的线程开启一个守护线程,用来给快要过期的锁续航。

    漫画:什么是分布式锁?
    什么是分布式锁

    三、Redission实现分布式锁

      加锁原则:

    • 锁的颗粒度要尽量小
    • 锁的范围尽量要小

      Redisson保证value的唯一性:UUID+ThreadID。
      Redission分布式锁原理如下:

    图3-1 Redission分布式锁原理.png

      存在问题:master节点宕机,导致多个客户端对同一个分布式锁完成了加锁。
      解决方法:使用RedLock(Redis Distributed Lock)。

    Redisson实现分布式锁(1)---原理

    三、RedLock实现分布式锁

      算法思想:在多个redis实例上创建锁,必须在大多数redis节点(超过一半)上都成功创建锁,才能算这个整体的RedLock加锁成功。
      CAP定理:在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    分布式锁实现Demo:https://github.com/just-right/vote/tree/distributed

    Redis RedLock 完美的分布式锁么?

    相关文章

      网友评论

          本文标题:分布式锁-Redis

          本文链接:https://www.haomeiwen.com/subject/odstohtx.html