分布式锁-Redisson
目录
1.分布式并发问题
2.如何解决分布式并发问题呢 ?
3.使⽤Redis实现分布式锁-代码实现
4.解决因线程异常导致⽆法释放锁的问题
5.解决因t1过期释放t2锁的问题
6.看⻔狗机制
7.分布式锁框架-Redisson
7.1 Redisson介绍
7.2 在SpringBoot应⽤中使⽤Redisson
7.3 Redisson⼯作原理
7.4 Redisson使⽤扩展
7.4.1 Redisson单机连接
7.4.2 Redisson集群连接
7.4.3 Redisson主从连接
7.5 分布式锁总结
7.5.1 分布式锁特点
7.5.2 锁的分类
7.5.3 Redission的使⽤
1.分布式并发问题
2.如何解决分布式并发问题呢 ?
3.使⽤Redis实现分布式锁-代码实现
@Transactional
public Map<String,String> addOrder(String cids,Orders order) throws
SQLException {logger.info("add order begin...");Map<String, String> map = null;//1.校验库存:根据cids查询当前订单中关联的购物⻋记录详情(包括库存)String[] arr = cids.split(",");List<Integer> cidsList = new ArrayList<>();for (int i = 0; i < arr.length; i++) {cidsList.add(Integer.parseInt(arr[i]));}//根据⽤户在购物⻋列表中选择的购物⻋记录的id 查询到对应的购物⻋记录List<ShoppingCartVO> list =
shoppingCartMapper.selectShopcartByCids(cidsList);//从购物⻋信息中获取到要购买的 skuId(商品ID) 以skuId为key写到redis中: 12 3boolean isLock = true;String[] skuIds = new String[list.size()]; //记录已经锁定的商品的IDfor (int i = 0; i <list.size() ; i++) {String skuId = list.get(i).getSkuId(); //订单中可能包含多个商品,
每个skuId表示⼀个商品Boolean ifAbsent =
stringRedisTemplate.boundValueOps(skuId).setIfAbsent("fmmall");if(ifAbsent){skuIds[i] = skuId;}isLock = isLock && ifAbsent;}//如果isLock为true,表示“加锁”成功if(isLock){try{//1.⽐较库存: 当第⼀次查询购物⻋记录之后,在加锁成功之前,可能被其他
的并发线程修改库存List<ShoppingCartVO> list =
shoppingCartMapper.selectShopcartByCids(cidsList);boolean f = true;String untitled = "";for (ShoppingCartVO sc : list) {if (Integer.parseInt(sc.getCartNum()) >
sc.getSkuStock()) {f = false;}untitled = untitled + sc.getProductName() + ",";}if (f) {//2.添加订单//3.保存快照//4.修改库存//5.删除购物⻋map = new HashMap<>();logger.info("add order finished...");map.put("orderId", orderId);map.put("productNames", untitled);}}catch(Exception e){e.printStackTrance();}finally{//释放锁for (int m = 0; m < skuIds.length ; m++) {String skuId = skuIds[m];if(skuId!=null && !"".equals(skuId)){stringRedisTemplate.delete(skuId);}}}return map;}else{//表示加锁失败,订单添加失败// 当加锁失败时,有可能对部分商品已经锁定,要释放锁定的部分商品for (int i = 0; i < skuIds.length ; i++) {String skuId = skuIds[i];if(skuId!=null && !"".equals(skuId)){stringRedisTemplate.delete(skuId);}}return null;}
}
4.解决因线程异常导致⽆法释放锁的问题
5.解决因t1过期释放t2锁的问题
- 在加锁的时候,为每个商品设置唯⼀的value
- 在释放锁的时候,先获取当前商品在redis中对应的value,如果获取的值与当前value相 同,则释放锁
- Redis的操作都是原⼦性的
- 要解决如上问题,必须保证查询操作和删除操作的原⼦性——使⽤lua脚本
- 在resources⽬录下创建unlock.lua,编辑脚本:
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end
- 配置Bean加载lua脚本
@Bean
public DefaultRedisScript<List> defaultRedisScript(){DefaultRedisScript<List> defaultRedisScript = new
DefaultRedisScript<>();defaultRedisScript.setResultType(List.class);defaultRedisScript.setScriptSource(new ResourceScriptSource(new
ClassPathResource("unlock.lua")));return defaultRedisScript; }
- 通过执⾏lua脚本解锁
@AutoWired
private DefaultRedisScript defaultRedisScript;
//执⾏lua脚本
List<String> keys = new ArrayList<>();
keys.add(skuId);
List rs = stringRedisTemplate.execute(defaultRedisScript,keys ,
values.get(skuId));
System.out.println(rs.get(0));
6.看⻔狗机制
7.分布式锁框架-Redisson
7.1 Redisson介绍
7.2 在SpringBoot应⽤中使⽤Redisson
- 添加依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version></dependency>
- 配置yml
redisson:addr:singleAddr:host: redis://47.96.11.185:6370password: 12345678database: 0
- 配置RedissonClient
@Configuration
public class RedissonConfig {@Value("${redisson.addr.singleAddr.host}")private String host;@Value("${redisson.addr.singleAddr.password}")private String password;@Value("${redisson.addr.singleAddr.database}")private int database;@Beanpublic RedissonClient redissonClient(){Config config = new Config();config.useSingleServer().setAddress(host).setPassword(password).se
tDatabase(database);return Redisson.create(config);}
}
- 在秒杀业务实现中注⼊RedissonClient对象
7.3 Redisson⼯作原理
7.4 Redisson使⽤扩展
7.4.1 Redisson单机连接
- application.yml
redisson:addr:singleAddr:host: redis://47.96.11.185:6370password: 12345678database: 0
- RedissonConfig
@Configuration
public class RedissonConfig {@Value("${redisson.addr.singleAddr.host}")private String host;@Value("${redisson.addr.singleAddr.password}")private String password;@Value("${redisson.addr.singleAddr.database}")private int database;@Beanpublic RedissonClient redissonClient(){Config config = new Config();
config.useSingleServer().setAddress(host).setPassword(password).se
tDatabase(database);return Redisson.create(config);}
}
7.4.2 Redisson集群连接
- application.yml
redisson:addr:cluster:hosts: redis://47.96.11.185:6370,...,redis://47.96.11.185:6373password: 12345678
- RedissonConfig——RedissonClient对象
@Configuration
public class RedissonConfig {@Value("${redisson.addr.cluster.hosts}")private String hosts;@Value("${redisson.addr.cluster.password}")private String password;/*** 集群模式* @return*/@Beanpublic RedissonClient redissonClient(){Config config = new Config();config.useClusterServers().addNodeAddress(hosts.split("
[,]")).setPassword(password).setScanInterval(2000).setMasterConnectionPoolSize(10000).setSlaveConnectionPoolSize(10000);return Redisson.create(config);}
}
7.4.3 Redisson主从连接
- application.yml
redisson:addr:masterAndSlave:masterhost: redis://47.96.11.185:6370slavehosts:redis://47.96.11.185:6371,redis://47.96.11.185:6372password: 12345678database: 0
- RedissonConfig --- RedissonClient
@Configuration
public class RedissonConfig3 {@Value("${redisson.addr.masterAndSlave.masterhost}")private String masterhost;@Value("${redisson.addr.masterAndSlave.slavehosts}")private String slavehosts;@Value("${redisson.addr.masterAndSlave.password}")private String password;@Value("${redisson.addr.masterAndSlave.database}")private int database;/*** 主从模式* @return*/@Beanpublic RedissonClient redissonClient(){Config config = new Config();config.useMasterSlaveServers().setMasterAddress(masterhost).addSlaveAddress(slavehosts.split("[,]")).setPassword(password).setDatabase(database).setMasterConnectionPoolSize(10000).setSlaveConnectionPoolSize(10000);return Redisson.create(config);}
}
7.5 分布式锁总结
7.5.1 分布式锁特点
7.5.2 锁的分类
7.5.3 Redission的使⽤
//获取公平锁RLock lock = redissonClient.getFairLock(skuId);//获取⾮公平锁RLock lock = redissonClient.getLock(skuId);
//阻塞锁(如果加锁成功之后,超时时间为30s;加锁成功开启看⻔狗,剩5s延⻓过期时间)lock.lock();//阻塞锁(如果加锁成功之后,设置⾃定义20s的超时时间)lock.lock(20,TimeUnit.SECONDS);//⾮阻塞锁(设置等待时间为3s;如果加锁成功默认超时间为30s)boolean b = lock.tryLock(3,TimeUnit.SECONDS);//⾮阻塞锁(设置等待时间为3s;如果加锁成功设置⾃定义超时间为20s)boolean b = lock.tryLock(3,20,TimeUnit.SECONDS);
lock.unlock();
//公平⾮阻塞锁RLock lock = redissonClient.getFairLock(skuId);boolean b = lock.tryLock(3,20,TimeUnit.SECONDS);
- 伪代码
HashMap map = null;加锁try{if(isLock){校验库存if(库存充⾜){保存订单保存快照修改库存删除购物⻋map = new HashMap();...}}}catch(Exception e){e.printStackTrace();}finally{释放锁}return map;
- Java代码实现
/**
* 保存订单业务
*/
@Transactional
public Map<String, String> addOrder(String cids, Orders order)
throws SQLException {logger.info("add order begin...");Map<String, String> map = null;//1.校验库存:根据cids查询当前订单中关联的购物⻋记录详情(包括库存)String[] arr = cids.split(",");List<Integer> cidsList = new ArrayList<>();for (int i = 0; i < arr.length; i++) {cidsList.add(Integer.parseInt(arr[i]));}//根据⽤户在购物⻋列表中选择的购物⻋记录的id 查询到对应的购物⻋记录List<ShoppingCartVO> list =
shoppingCartMapper.selectShopcartByCids(cidsList);//加锁boolean isLock = true;String[] skuIds = new String[list.size()]; Map<String, RLock> locks = new HashMap<>(); //⽤于存放当前订单的锁for (int i = 0; i < list.size(); i++) {String skuId = list.get(i).getSkuId();boolean b = false;try {RLock lock = redissonClient.getLock(skuId);b = lock.tryLock(10, 3, TimeUnit.SECONDS);if (b) {skuIds[i] = skuId;locks.put(skuId, lock);}} catch (InterruptedException e) {e.printStackTrace();}isLock = isLock & b;}//如果isLock为true,表示“加锁”成功try {if (isLock){//1.检验库存boolean f = true;String untitled = "";list =
shoppingCartMapper.selectShopcartByCids(cidsList);for (ShoppingCartVO sc : list) {if (Integer.parseInt(sc.getCartNum()) >
sc.getSkuStock()) {f = false;}untitled = untitled + sc.getProductName() + ",";}if (f) {//如果库存充⾜,则进⾏下订单操作logger.info("product stock is OK...");//2.保存订单order.setUntitled(untitled);order.setCreateTime(new Date());order.setStatus("1");//⽣成订单编号String orderId =
UUID.randomUUID().toString().replace("-", "");order.setOrderId(orderId);int i = ordersMapper.insert(order);//3.⽣成商品快照for (ShoppingCartVO sc : list) {int cnum = Integer.parseInt(sc.getCartNum());String itemId = System.currentTimeMillis() +
"" + (new Random().nextInt(89999) + 10000);OrderItem orderItem = new OrderItem(itemId,
orderId, sc.getProductId(), sc.getProductName(),
sc.getProductImg(), sc.getSkuId(), sc.getSkuName(), new
BigDecimal(sc.getSellPrice()), cnum, new
BigDecimal(sc.getSellPrice() * cnum), new Date(), new Date(), 0);orderItemMapper.insert(orderItem);//增加商品销量}//4.扣减库存:根据套餐ID修改套餐库存量for (ShoppingCartVO sc : list) {String skuId = sc.getSkuId();int newStock = sc.getSkuStock() -
Integer.parseInt(sc.getCartNum());ProductSku productSku = new ProductSku();productSku.setSkuId(skuId);productSku.setStock(newStock);productSkuMapper.updateByPrimaryKeySelective(productSku);//5.删除购物⻋:当购物⻋中的记录购买成功之后,购物⻋中对应
做删除操作for (int cid : cidsList) {shoppingCartMapper.deleteByPrimaryKey(cid);}map = new HashMap<>();logger.info("add order finished...");map.put("orderId", orderId);map.put("productNames", untitled);}}}catch (Exception e){e.printStackTrace();}finally {//释放锁for (int i = 0; i < skuIds.length; i++) {String skuId = skuIds[i];if (skuId != null && !"".equals(skuId)) {locks.get(skuId).unlock();System.out.println("-----------------------
unlock");}}}return map; }
分布式锁-Redisson相关推荐
- 高性能分布式锁-redisson
RedLock算法-使用redis实现分布式锁服务 译自Redis官方文档 在多线程共享临界资源的场景下,分布式锁是一种非常重要的组件. 许多库使用不同的方式使用redis实现一个分布式锁管理. 其中 ...
- 分布式锁 - Redisson的看门狗(watchdog)机制
分布式锁 - Redisson的看门狗(watchdog)机制 前言 本篇文章从Redisson的加锁(tryLock)入手,带大家由源码来了解一下watchdog的自动延迟加锁操作,如果对Redis ...
- 分布式锁-Redisson快速入门
分布式锁-Redisson快速入门 一.引入依赖 二.配置Redisson客户端 三.使用Redisson的分布式锁 一.引入依赖 <dependency><groupId>o ...
- 分布式锁Redisson的使用
文章目录 高性能分布式锁-redisson的使用 1.官方文档 2.项目集成 3.配置Redisson 4.锁的获取和释放 5.业务逻辑中使用分布式锁 高性能分布式锁-redisson的使用 1.官方 ...
- 分布式锁redisson的使用 看门狗原理
redisson 能干嘛 为什么要用分布式锁 redisson ,不用分布式锁 redisson 我们需要解决哪些问题? 单机版的服务使用Synchronize和Lock是没问题的,如果一上集群,每个 ...
- Redis分布式锁Redisson
文章目录 分布式锁 不可重入Redis分布式锁 Redisson 快速入门 可重入的Redis分布式锁 Redisson的multiLock 分布式锁 分布式锁:满足分布式系统或集群模式下多进程可见并 ...
- redis 分布式锁 看门狗_分布式锁Redisson的使用,看门狗机制
Redisson简介 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式 ...
- 缓存-分布式锁-Redisson简介整合
Redisson分布式 GitHub中文文档 概念:是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).它不仅提供了一系列的分布式的Java常用对象,还提 ...
- 分布式锁——Redisson
1.安装redis a.由于官方是没有Windows版的,所以我们需要下载微软开发的redis,网址:https://github.com/MicrosoftArchive/redis/release ...
最新文章
- 计算点、线、面等元素之间的交点、交线、封闭区域面积和闭合集(续1)
- Tomcat性能调优-JVM监控与调优
- esp32 怎么分配freertos 堆栈大小_嵌入式开发入门-从STM32CudeMX、FreeRtos、Proteu仿真开始...
- SegmentFault 美团云采访实录
- Leetcode--319. 灯泡开关
- windows服务器远程执行命令(PowerShell+WinRM)
- Property 'filter' does not exist on type 'Observable' 报错解决方法
- 数组的声明、创建、初始化
- Kindle 文言文 古汉语 字典
- javascript 实现自动定时刷新网页脚本
- Flink单机部署,slots为0,TaskExecutor akka.tcp://xxx has no more allocated slots for job
- php 开启 exec,php怎么开启exec()函数?
- 计算机系学生的梦想,编写我们的梦想——北大计算机系学生生活掠影-北京大学网络与信息.ppt...
- 2020年小米高级 PHP 工程师面试题
- php加速模块cpan模块,查看perl模块和cpan模块介绍
- java Web api接入讯飞aiui(1)
- springboot+poi开发excel导出 加载Excel模板导出 Excel批量导出详解
- csgo准星设置代码_csgo准星设置指令
- 涵数计算机怎么玩游戏,科学计算器
- Android网络请求归纳 HttpUrlConnection| Vollety|OKHttp3|Retrofit2