spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)
2023-09-14 08:59:33 时间
一,为什么要使用分布式锁?
如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性
但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁,
例如:两台机器上同时各有一个进程查询同一件商品的库存,此时商品库存数为1,
数据库给两台机器返回的都是1,
然后这两台机器同时下单,两个订单就超出了商品的库存数,
所以此时要使用分布式锁
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,使用redisson
redisson是对redis用java语言的封装
1,redisson的官网:
https://redisson.org/
2,redisson的官方文档:
https://github.com/redisson/redisson/wiki
3,使用redisson做分布式锁和mysql悲观锁(for update)的区别:
本质上没有区别,
但redis性能更强
三,演示项目的相关信息
1,地址
https://github.com/liuhongdi/distributedlock
2,原理:
在减库存之前,先加锁,
在减库存完成后,解锁
这样避免高并发时查询到相同的库存数而导致超卖情况
3,结构
![](https://img2020.cnblogs.com/blog/1938691/202006/1938691-20200616125218735-652677333.jpg)
4,数据表:
CREATE TABLE `goods` ( `goods_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `goods_name` varchar(500) NOT NULL DEFAULT '' COMMENT 'name', `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock', PRIMARY KEY (`goods_id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'
四,java代码的说明
OrderServiceImpl.java
@Service public class OrderServiceImpl implements OrderService { @Resource private RedissonClient redissonClient; @Resource private GoodsMapper goodsMapper; /* * 加锁减库存 * */ @Override public boolean decrementProductStoreLock(int goodsId, int buyNum) { String key = "dec_store_lock_" + goodsId; //生成锁对象 RLock lock = redissonClient.getLock(key); try { //2, TimeUnit.MINUTES lock.lock(2, TimeUnit.MINUTES); boolean upRes = updateGoodsStock(goodsId, buyNum); if (upRes == false) { return false; } } catch (Exception e) { System.out.println(e.getMessage()); return false; } finally { //解锁 if (lock.isHeldByCurrentThread()){ System.out.println("----------------release lock"); lock.unlock(); } } return true; } /* * 减库存 * */ @Transactional(isolation = Isolation.REPEATABLE_READ) public boolean updateGoodsStock(int goodsId, int buyNum) { Goods goodsOne = goodsMapper.selectOneGoods(goodsId); System.out.println("-------------------------当前库存:"+goodsOne.getStock()+"-------购买数量:"+buyNum); if (goodsOne.getStock() < buyNum || goodsOne.getStock() <= 0) { System.out.println("------------------------fail:buy fail,return"); return false; } int upStock = goodsOne.getStock()-buyNum; goodsOne.setStock(upStock); int upNum = goodsMapper.updateOneGoodsStock(goodsOne); System.out.println("-------------------------success:成交订单数量:"+upNum); return true; } /* * 不加锁减库存 * */ @Override public boolean decrementProductStoreNoLock(int goodsId, int buyNum) { return updateGoodsStock(goodsId, buyNum); } }
java代码说明:
String key = "dec_store_lock_" + goodsId: 加锁时加入商品id,这样不影响其他商品
RLock是redisson中定义的可重入锁
lock.lock(2, TimeUnit.MINUTES) 加锁,时长两分钟
lock.unlock(); 解锁
五,测试高并发时加锁的效果:
1,数据库的数据:
mysql> update goods set stock=3; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from goods; +----------+------------+-------+ | goods_id | goods_name | stock | +----------+------------+-------+ | 3 | green cup2 | 3 | +----------+------------+-------+ 1 row in set (0.00 sec)
我们设置商品的库存数为3
2,不加锁的情况
#-c:请求并发数
#-n:请求总数
[root@localhost ~]# ab -c 20 -n 20 http://127.0.0.1:8080/lock/buynolock
查看代码的打印输出:
-------------------------当前库存:3-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:2-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return
共成交5单,超出了库存数量
3,加锁的情况:
#-c:请求并发数
#-n:请求总数
[root@localhost ~]# ab -c 20 -n 20 http://127.0.0.1:8080/lock/buylock
查看代码的打印输出:
-------------------------当前库存:3-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:2-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return
只成功了3个订单,说明分布式锁有效
六,查看spring boot的版本:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.0.RELEASE)
相关文章
- Quartz在Spring中动态设置cronExpression (spring设置动态定时任务)------转帖
- Spring Boot启动时数据库初始化spring.datasource(转)
- spring boot: 用redis的消息订阅功能更新应用内的caffeine本地缓存(spring boot 2.3.2)
- spring boot:redis+lua实现生产环境中可用的秒杀功能(spring boot 2.2.0)
- redis集群之REDIS CLUSTER
- Spring Data - Redis 节选自《Netkiller Java 手札》
- spring boot:使用多个redis数据源(spring boot 2.3.1)
- spring boot:使用redis cluster集群作为分布式session(redis 6.0.5/spring boot 2.3.1)
- spring boot:redis+lua实现生产环境中可用的秒杀功能(spring boot 2.2.0)
- Docker搭建Redis高可用集群(基于redis-sentinel)
- Spring Boot with Redis
- spring学习12 -Spring 框架模块以及面试常见问题注解等
- Spring Boot with Redis
- [Bug]redis问题解决(MISCONF Redis is configured to save RDB snapshots)
- 10_Spring Boot 集成Dubbo + Mybatis + Redis
- 学习Spring Boot:(十七)Spring Boot 中使用 Redis
- 学习Spring Boot:(十四)spring-shiro的密码加密
- spring boot + redis --- 心得
- 【架构师修炼之路】Redis 极简教程 : 基本数据结构, 跳表原理, Spring Boot 项目使用实例
- 解决com.alibaba.fastjson.JSONException: write javaBean error问题以及解决Spring Boot加入Shiro导致spring aop失效的问题
- Windows下安装并设置Redis
- 001-Spring Cloud Edgware.SR3 升级最新 Finchley.SR1,spring boot 1.5.9.RELEASE 升级2.0.4.RELEASE注意问题点
- 在Spring中使用Redis
- 【java】Spring Boot --spring boot项目整合xxl-job
- 使用redis-shake工具迁移云Redis数据(二十一)