基于redis、redisson 实现分布式锁

1.为什么要使用分布式锁?
      在分布式场景下为了保证数据最终一致性。在单进程的系统中,存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步(lock—synchronized),使其在修改这种变量时能够线性执行消除并发修改变量。但分布式系统是多部署、多进程的,开发语言提供的并发处理API在此场景下就无能为力了。

2.分布式锁的使用场景
      电商网站用下单操作时需要使用,秒杀活动更是如此,否则会出现超卖(库存100,秒杀活动时库存变负数了)现象

3.分布式锁的实现方式
      大概有三种:1.基于关系型数据库,2.基于缓存,3基于zookeeper
      大部分网站使用的是基于缓存的,有更好的性能,而缓存一般是以集群方式部署,保证了高可用性

4.基于缓存redis,使用开源 redisson 实现分布式锁
  4.1新建SpringBoot项目,引入依赖:redisson依赖 netty 、jackson-core、jackson-databind

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--start redis distributed lock-->
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.6.5</version>
</dependency>
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.25.Final</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>
<!--end redis distributed lock-->

  4.2在启动类中注入 redisson 的bean,可以在配置文件中添加redis的连接地址、参数,然后在代码中引入配置文件

@SpringBootApplication
public class RedissonApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedissonApplication.class, args);
    }
    @Bean
    Redisson redissonSentinel() {
        //支持单机,主从,哨兵,集群等模式
        //此为哨兵模式
        Config config = new Config();
        config.useSentinelServers()
                .setMasterName("mymaster")
                .addSentinelAddress("redis://192.168.1.1:26379")
                .setPassword("123456");
        return (Redisson)Redisson.create(config);
    }
}

  4.3到此,就完成了,只要测试了,是不是很简单。模拟秒杀场景,有一个商品aaa,库存100个,2个并发请求110次,每请求一次库存减1,看最后商品数有没有变为负数,
  4.4在redis中,设置一个库存数量为100

@Test
public void test() throws Exception {
    //设置一个key,aaa商品的库存数量为100
    stringRedisTemplate.opsForValue().set("aaa","100");
    Assert.assertEquals("100", stringRedisTemplate.opsForValue().get("aaa"));
}

  4.5把项目复制一份,2个项目,分别使用下面的测试方法,同时启动,因库存为100,每次请求库存减1,启动2个测试方法各循环55次,总计110次请求,看库存打印结果会不会变负数

String lockKey = "testRedisson";//分布式锁的key
@Test
public void testDistributed(){
    //执行的业务代码
    for(int i=0; i < 55; i++){
        RLock lock = redisson.getLock(lockKey);
        lock.lock(60, TimeUnit.SECONDS); //设置60秒自动释放锁  (默认是30秒自动过期)
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("aaa").toString());
        if(stock > 0){
            stringRedisTemplate.opsForValue().set("aaa",(stock-1)+"");
            System.out.println("test2_:lockkey:"+lockKey+",stock:"+(stock-1)+"");
        }
        lock.unlock(); //释放锁
    }
}

测试结果:正确,redis的值没有变负数,说明被锁住了
这里写图片描述
再看看第一个项目打印的数据,可以看到第70,39被第二个项目的锁获得了
这里写图片描述
第二个项目打印的数据
这里写图片描述
测试成功

猜你喜欢

转载自blog.csdn.net/zhuyu19911016520/article/details/80418161