zoukankan      html  css  js  c++  java
  • 分布式锁

    Redisson

    1.引入redission 作为分布式锁框架

    https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95  (中文配置文档地址)

    <!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.13.2</version>
    </dependency>
     @Bean(destroyMethod = "shutdown")
        RedissonClient redisson() throws IOException{
            //1.创建配置
            Config config = new Config();
            config.useSingleServer().setAddress("redis://192.168.56.10:6379");
            //2.根据config,创建RedissonClient对象
            return Redisson.create(config);
        }

    2.分布式锁和同步器

    可重入锁

    A、B方法共用一号锁,可重入锁 避免死锁

       //1.调用getLock 获取一把锁,只要锁名一样就是同一把锁
            RLock lock = redissonClient.getLock("my-lock");
            //2.加锁
            lock.lock();
            try{
                System.out.println("加锁成功,执行业务..."+Thread.currentThread().getId());
                Thread.sleep(30000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //3.解锁
                System.out.println("释放锁。。。"+Thread.currentThread().getId());
                lock.unlock();
            }

    Redisson-lock

    • 锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s。不用担心业务时间长,锁自动过期被删掉
    • 加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认30s后自动删除

    读写锁

    /**
         * 保证一定能读到最新数据,修改期间,写锁是一个排他锁(互斥锁),读锁是一个共享锁
         * 写锁没释放读就必须等待
         * 读+读:相当于无锁,并发读,只会在redis中记录所有读锁,他们都会同时加锁成功
         * 写+读:等待写锁释放
         * 写+写:阻塞
         * 读+写:有读锁,写也需要等待
         * 只要有写的地方都必须等待
         * @return
         */
        @GetMapping("/write")
        @ResponseBody
        public String writeValue() {
            RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
            String s = "";
            RLock rLock = readWriteLock.writeLock();
            try {
                rLock.lock();
                System.out.println("写锁加锁成功。。。"+Thread.currentThread().getId());
                s = UUID.randomUUID().toString();
                redisTemplate.opsForValue().set("writeValue", s);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                rLock.unlock();
                System.out.println("写锁释放。。。"+Thread.currentThread().getId());
            }
            return s;
        }
    
        @RequestMapping("read")
        @ResponseBody
        public String read() {
            RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
            String s = "";
            RLock rLock = readWriteLock.readLock();
            try {
                rLock.lock();
                System.out.println("读锁加锁成功。。。"+Thread.currentThread().getId());
                Thread.sleep(30000);
                s = (String) redisTemplate.opsForValue().get("writeValue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                rLock.unlock();
                System.out.println("读锁释放。。。"+Thread.currentThread().getId());
            }
            return s;
        }

    闭锁(CountDownLatch)

    @GetMapping("/lockDoor")
        @ResponseBody
        public String lockDoor() throws InterruptedException {
            RCountDownLatch door = redissonClient.getCountDownLatch("door");
            door.trySetCount(5);
            door.await();
            return "放假了。。。";
        }
    
        @GetMapping("/gogogo/{id}")
        @ResponseBody
        public String gogogo(@PathVariable("id") Long id){
            RCountDownLatch door = redissonClient.getCountDownLatch("door");
            door.countDown();//计数-1
            return id+"班人都走了。。。";
        }

    信号量(Semaphore)

    /**
         * 车位停车
         * 3车位
         * 信号量用于分布式限流
         */
        @GetMapping("/park")
        @ResponseBody
        public String park() throws InterruptedException {
            RSemaphore park = redissonClient.getSemaphore("park");
            //park.acquire();//获取一个信号,获取一个值,占一个车位
            boolean b = park.tryAcquire();
            return "ok=>"+b;
        }
    
        @GetMapping("/go")
        @ResponseBody
        public String gos() throws InterruptedException {
            RSemaphore park = redissonClient.getSemaphore("park");
            park.release();//释放一个值,车位
            return "ok";
        }

    缓存数据一致性

    双写模式,失效模式 都会导致缓存不一致问题。

    1.如果是用户维度数据(订单数据,用户数据),这种并发几率很小,不用考虑。缓存加上过期时间,每隔一段时间触发读的主动更新即可

    2.如果是菜单、商品介绍等基础数据,也可以去使用canal订阅binlog的方式

    3.缓存数据+过期时间也足够解决大部分业务对于缓存的要求

    4.通过加锁保证并发读写,写写的时候按顺序排队。读读无所谓。所以适合使用读写锁

    总结:

    • 我们能放入缓存的数据本就不应该是实时性、一致性要求高的。所以缓存数据加上过期时间,保证每天能拿到最新数据即可。
    • 不应过度设计,增加系统的复杂性
    • 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

    缓存一致性-解决-Canal

    • 使用Canal更新缓存
    • 使用Canal解决数据异构

    数据一致性解决方案根据系统来

    1. 缓存的所有数据都有过期时间,数据过期下一次查询触发主动更新
    2. 读写数据的时候,加上分布式的读写锁。经常写经常读,肯定有影响。
  • 相关阅读:
    Xcode工程文件打不开:cannot be opened because the project file cannot be parsed
    MAC系统压缩文件传到WINDOWS下出现乱码
    iOS-Xcode必备插件XAlign:瞬间优化你的代码
    那些不能错过的Xcode插件
    Xcode 5.0.1安装插件:规范注释生成器VVDocumenter + OSX 10.9.2
    由pushViewController说起可能出线的各种死法
    iPhone开发中从一个视图跳到另一个视图有三种方法:
    深入理解typedef
    通俗易懂地讲解 __block 变量
    Blocks Programming Topics
  • 原文地址:https://www.cnblogs.com/sgrslimJ/p/13716162.html
Copyright © 2011-2022 走看看