• Redis系列【分布式锁解决方案之Redisson】


    1.分布式锁解决方案

    常用的一般有Zookeeper,Redisson,数据库。

    • Zookeeper方案使用的是CP(保证了一致性和分区容错性,牺牲了一点可用性),适合流量请求不是很大,一致性要求较高的业务场景。
    • Redisson方案使用的是AP(保证了可用性和分区容错性,牺牲了一点一致性),适合高并发场景,对一致性要求不是很高的业务场景。一致性可以人工通过脚本弥补,也可以通过redlock去解决。

    2.Redission分布式锁解决方案

    1. 在pom中引入Redisson坐标
    <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.17.3</version>
    </dependency>
    
    1. 在配置类或启动类中注入Redisson对象
        @Bean
        public Redisson redisson(){
            //redis为单机模式
            Config config = new Config();
            config.useSingleServer().setAddress("redis://localhost:6379").setPassword("123456");
            return (Redisson) Redisson.create(config);
        }
    
    1. 业务代码编写
    package com.fast.controller;
    
    import org.redisson.Redisson;
    import org.redisson.api.RLock;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author hjw
     * @since 2022年06月23日 11:42:00
     */
    @RestController
    @RequestMapping("stock")
    public class StockController {
        @Autowired
        private RedisTemplate redisTemplate;
        @Autowired
        private Redisson redisson;
    
        @GetMapping("init")
        public String init() {
            redisTemplate.opsForValue().set("stock", 100);
    
            return "库存新增成功";
        }
    
        @GetMapping("sale")
        public String saleGoods() {
            //这样写,单机部署不会有问题,多节点就会出问题,因为synchronized只能基于jvm做加锁,多个节点属于多个jvm了
            synchronized (this) {
                int stock = (int) redisTemplate.opsForValue().get("stock");
                if (stock > 0) {
                    int i = stock - 1;
                    redisTemplate.opsForValue().set("stock", i);
                    System.out.println("库存剩余:" + i);
                } else {
                    System.out.println("库存数量不足");
                    return "库存数量不足";
                }
            }
            return "库存扣减成功";
        }
    
        @GetMapping("saleImprove")
        public String saleImprove() {
            //使用redisson实现分布式锁
            String lockKey = "product_001";
    //        String clientId = UUID.randomUUID().toString();
            **RLock redissonLock = redisson.getLock(lockKey);**
            try {
                //使用redisTemplate还需要手写子线程每隔30s*1/3=10s,根据clientId给每个线程的lockKey进行续期,防止lockKey失效后,业务未执行完,结果下个线程进来了
    //            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,clientId,30, TimeUnit.SECONDS);
    //            if (!result){
    //                return "error_code";
    //            }
                **redissonLock.lock();**//相当于setIfAbsent(lockKey,clientId,30, TimeUnit.SECONDS);
                int stock = (int) redisTemplate.opsForValue().get("stock");
                if (stock > 0) {
                    int i = stock - 1;
                    redisTemplate.opsForValue().set("stock", i);
                    System.out.println("库存剩余:" + i);
                } else {
                    System.out.println("库存数量不足");
                    return "库存数量不足";
                }
            }finally {
                **redissonLock.unlock();**
    //            if (clientId.equals(redisTemplate.opsForValue().get(lockKey))){
    //                redisTemplate.delete(lockKey);
    //            }
            }
            return "库存扣减成功";
        }
    }
    
    1. 若想进一步优化,则可以采用1.8中ConcurrentHashMap的设计思想,分段加锁
      进一步把库存1000,进行拆分成stock1-200,stock2-200,stock3-200,stock4-200,stock5-200,然后再进行分配,第一个请求进来访问stock1,进行库存加减,第二个访问stock2进行库存加减...依此类推,分别加锁解锁,就可以大大提高并发量。
  • 相关阅读:
    Git连接Github
    全民来打飞机~~(设计探讨)
    mongo学习
    redis aop持久化
    redis的超时删除策略
    redis 学习,没事的时候看看增长知识面
    MySQL query / clause execution order
    用redis做超时判断 感觉写的很有意思
    python 编码问题(转载)
    git 个人学习
  • 原文地址:https://www.cnblogs.com/hujunwei/p/16408347.html
Copyright © 2020-2023  润新知