博客
关于我
Redisson实现分布式锁(3)—项目落地实现
阅读量:447 次
发布时间:2019-03-06

本文共 4201 字,大约阅读时间需要 14 分钟。

Redisson实现分布式锁(3)—项目落地实现

有关Redisson实现分布式锁前面写了两篇博客作为该项目落地的铺垫。

1、

2、

这篇讲下通过Redisson实现分布式锁的项目实现,我会把项目放到GitHub,该项目可以直接运用于实际开发中,作为分布式锁使用。

一、项目概述

1、技术架构

项目总体技术选型

SpringBoot2.1.5 + Maven3.5.4 + Redisson3.5.4 + lombok(插件)

2、加锁方式

该项目支持 自定义注解加锁 常规加锁 两种模式

自定义注解加锁

@DistributedLock(value="goods", leaseTime=5)  public String lockDecreaseStock(){    //业务逻辑  }

常规加锁

//1、加锁 redissonLock.lock("redisson", 10); //2、业务逻辑 //3、解锁 redissonLock.unlock("redisson");

3、Redis部署方式

该项目支持四种Redis部署方式

1、单机模式部署2、集群模式部署3、主从模式部署4、哨兵模式部署

该项目已经实现支持上面四种模式,你要采用哪种只需要修改配置文件application.properties,项目代码不需要做任何修改。

4、项目整体结构

redis-distributed-lock-core # 核心实现|---src      |      ---com.jincou.redisson                           |# 通过注解方式 实现分布式锁                           ---annotation                           |# 配置类实例化RedissonLock                           ---config                           |# 放置常量信息                           ---constant                           |# 读取application.properties信息后,封装到实体                           ---entity                               |# 支持单机、集群、主从、哨兵 代码实现                           ---strategyredis-distributed-lock-web-test # 针对上面实现类的测试类|---src      |      ---java            |            ---com.jincou.controller                                 |# 测试 基于注解方式实现分布式锁                                 ---AnnotatinLockController.java                                 |# 测试 基于常规方式实现分布式锁                                 ---LockController.java      ---resources                           | # 配置端口号 连接redis信息(如果确定部署类型,那么将连接信息放到core项目中)            ---application.properties

二、测试

模拟1秒内100个线程请求接口,来测试结果是否正确。同时测试3中不同的锁:lock锁、trylock锁、注解锁。

1、lock锁

/**     * 模拟这个是商品库存     */    public static volatile Integer TOTAL = 10;    @GetMapping("lock-decrease-stock")    public String lockDecreaseStock() throws InterruptedException {        redissonLock.lock("lock", 10);        if (TOTAL > 0) {            TOTAL--;        }        Thread.sleep(50);        log.info("======减完库存后,当前库存===" + TOTAL);        //如果该线程还持有该锁,那么释放该锁。如果该线程不持有该锁,说明该线程的锁已到过期时间,自动释放锁        if (redissonLock.isHeldByCurrentThread("lock")) {           redissonLock.unlock("lock");        }        return "=================================";    }

压测结果

没问题,不会超卖!

2、tryLock锁

/**     * 模拟这个是商品库存     */    public static volatile Integer TOTAL = 10;    @GetMapping("trylock-decrease-stock")    public String trylockDecreaseStock() throws InterruptedException {        if (redissonLock.tryLock("trylock", 10, 5)) {            if (TOTAL > 0) {                TOTAL--;            }            Thread.sleep(50);            redissonLock.unlock("trylock");            log.info("====tryLock===减完库存后,当前库存===" + TOTAL);        } else {            log.info("[ExecutorRedisson]获取锁失败");        }        return "===================================";    }

测试结果

没有问题 ,不会超卖!

3、注解锁

/**     * 模拟这个是商品库存     */    public static volatile Integer TOTAL = 10;    @GetMapping("annotatin-lock-decrease-stock")    @DistributedLock(value="goods", leaseTime=5)    public String lockDecreaseStock() throws InterruptedException {        if (TOTAL > 0) {            TOTAL--;        }        log.info("===注解模式=== 减完库存后,当前库存===" + TOTAL);        return "=================================";    }

测试结果

没有问题 ,不会超卖!

通过实验可以看出,通过这三种模式都可以实现分布式锁,然后呢?哪个最优。

三、三种锁的锁选择

观点 最完美的就是lock锁,因为

1、tryLock锁是可能会跳过减库存的操作,因为当过了等待时间还没有获取锁,就会返回false,这显然很致命!2、注解锁只能用于方法上,颗粒度太大,满足不了方法内加锁。

1、lock PK tryLock 性能的比较

模拟5秒内1000个线程分别去压测这两个接口,看报告结果!

1)lock锁

压测结果 1000个线程平均响应时间为31324。吞吐量 14.7/sec

2)tryLock锁

压测结果 1000个线程平均响应时间为28628。吞吐量 16.1/sec

这里只是单次测试,有很大的随机性。从当前环境单次测试来看,tryLock稍微高点。

2、常见异常 attempt to unlock lock, not ······

在使用RedissonLock锁时,很容易报这类异常,比如如下操作

//设置锁1秒过去        redissonLock.lock("redisson", 1);        /**         * 业务逻辑需要咨询2秒         */        redissonLock.release("redisson");

上面在并发情况下就会这样

造成异常原因:

线程1 进来获得锁后,但它的业务逻辑需要执行2秒,在 线程1 执行1秒后,这个锁就自动过期了,那么这个时候 线程2 进来了获得了锁。在线程1去解锁就会抛上面这个异常(因为解锁和当前锁已经不是同一线程了)

所以我们需要注意,设置锁的过期时间不能设置太小,一定要合理,宁愿设置大点。

正对上面的异常,可以通过isHeldByCurrentThread()方法,

//如果为false就说明该线程的锁已经自动释放,无需解锁  if (redissonLock.isHeldByCurrentThread("lock")) {            redissonLock.unlock("lock");        }

好了,这篇博客就到这了!

至于完整的项目地址见GitHub。

如果对您能有帮助,就给个星星吧,哈哈!

GitHub地址

参考

1、

2、

3、

只要自己变优秀了,其他的事情才会跟着好起来(中将7)

转载地址:http://jvffz.baihongyu.com/

你可能感兴趣的文章
Mysql 脏页 脏读 脏数据
查看>>
mysql 自增id和UUID做主键性能分析,及最优方案
查看>>
Mysql 自定义函数
查看>>
mysql 行转列 列转行
查看>>
Mysql 表分区
查看>>
mysql 表的操作
查看>>
mysql 视图,视图更新删除
查看>>
MySQL 触发器
查看>>
mysql 让所有IP访问数据库
查看>>
mysql 记录的增删改查
查看>>
MySQL 设置数据库的隔离级别
查看>>
MySQL 证明为什么用limit时,offset很大会影响性能
查看>>
Mysql 语句操作索引SQL语句
查看>>
MySQL 误操作后数据恢复(update,delete忘加where条件)
查看>>
MySQL 调优/优化的 101 个建议!
查看>>
mysql 转义字符用法_MySql 转义字符的使用说明
查看>>
mysql 输入密码秒退
查看>>
mysql 递归查找父节点_MySQL递归查询树状表的子节点、父节点具体实现
查看>>
mysql 通过查看mysql 配置参数、状态来优化你的mysql
查看>>
mysql 里对root及普通用户赋权及更改密码的一些命令
查看>>