分布式锁-Redis红锁解决方案 古城微笑少年丶 2023-09-29 15:28 1阅读 0赞 ### 文章目录 ### * 1:分布式锁的概念 * * 1:概念 * 2:锁/分布式锁/事务区别 * 2:本文使用的案例场景 * * 1:需求 * 2:controller层代码 * 3:锁控制层代码(使用synchronized 不成功) * 4:调用的订单业务代码 * 3:Redis解决方案-红锁 * * 1:介绍 * 2:红锁原理 * 3:红锁使用说明-官网介绍 * 4:红锁实战 * * 1:注册红锁的RedissonClient * 2:红锁使用 [分布式锁-Redis解决方案和Redisson解决方案][-Redis_Redisson] [分布式锁-数据库mysql解决方案][-_mysql] [分布式锁-Redis红锁解决方案][-Redis] # 1:分布式锁的概念 # ## 1:概念 ## 分布式锁(多服务共享锁) 在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问 控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。 ## 2:锁/分布式锁/事务区别 ## * 锁 单进程的系统中,存在多线程同时操作一个公共变量,此时需要加锁对变量进行同步操作,保证多线程的操作线性执行消除并发修改。解决的是单进程中的多线程并发问题。 * 分布式锁 只要的应用场景是在集群模式的多个相同服务,可能会部署在不同机器上,解决进程间安全问题,防止多进程同时操作一个变量或者数据库。解决的是多进程的并发问题 事务 解决一个会话过程中,上下文的修改对所有数据库表的操作要么全部成功,要不全部失败。所以应用在service层。解决的是一个会话中的操作的数据一致性。 * 分布式事务 解决一个联动操作,比如一个商品的买卖分为添加商品到购物车、修改商品库存,此时购物车服务和商品库存服务可能部署在两台电脑,这时候需要保证对两个服务的操作都全部成功或者全部回退。解决的是组合服务的数据操作的一致性问题 # 2:本文使用的案例场景 # ## 1:需求 ## 当在打车软件中,乘客下了订单。多个司机抢单,此时因为单子只有一个,多个司机对此共享资源进行抢,此处应该使用分布式锁; ## 2:controller层代码 ## @GetMapping("/do/{orderId}") public String grab(@PathVariable("orderId") int orderId, int driverId){ System.out.println("order:"+orderId+",driverId:"+driverId); //此处调用锁控制层代码 grabService.grabOrder(orderId,driverId); return ""; } ## 3:锁控制层代码(使用synchronized 不成功) ## 使用synchronized 不能保证多台服务器只有一个抢成功;因为synchronized 只能锁本服务的资源;多台服务的资源是锁不住的; @Autowired OrderService orderService; @Override public String grabOrder(int orderId, int driverId) { String lock = (orderId+""); synchronized (lock.intern()) { try { System.out.println("司机:"+driverId+" 执行抢单逻辑"); //此处调用订单业务代码 boolean b = orderService.grab(orderId, driverId); if(b) { System.out.println("司机:"+driverId+" 抢单成功"); }else { System.out.println("司机:"+driverId+" 抢单失败"); } } finally { } } return null; } ## 4:调用的订单业务代码 ## 这一层就是写的伪代码,后续并不关注他 ![在这里插入图片描述][547316cf12ae41d18969f579f25c5560.png] # 3:Redis解决方案-红锁 # ## 1:介绍 ## > 红锁本质上就是使用多个Redis做锁。例如有5个Redis,一次锁的获取,会对每个请求都获取一遍,如果获取锁成功的数量超过一半(2.5),则获取锁成功,反之失败; > 释放锁也需要对每个Redis释放 ## 2:红锁原理 ## 1. 在Redis的分布式环境中,我们假设有5个Redis master。这些节点完全互相独立,没有主从关系 2. 线程1向这5个redis加锁,当加到第三个的时候,4和5加不上了;但是符合红锁的n/2+1原则,所以线程1获取到了锁; 3. 当redis3挂了,此时线程1获取到了锁,正在顺序执行, 4. 线程2来到了redis抢占锁,因为3挂了,1,2有锁,只有4和5可以加锁,因为我们注册的时候是5台,4和5这两台不满足n/2+1原则,抢占锁失败; ## 3:红锁使用说明-官网介绍 ## 基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。 RLock lock1 = redissonInstance1.getLock("lock1"); RLock lock2 = redissonInstance2.getLock("lock2"); RLock lock3 = redissonInstance3.getLock("lock3"); RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3); // 同时加锁:lock1 lock2 lock3 // 红锁在大部分节点上加锁成功就算成功。 lock.lock(); ... lock.unlock(); 大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。 另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。 RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3); // 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开 lock.lock(10, TimeUnit.SECONDS); // 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开 boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); ... lock.unlock(); ## 4:红锁实战 ## ### 1:注册红锁的RedissonClient ### @Component public class RedisConfig { @Bean(name = "redissonRed1") @Primary public RedissonClient redissonRed1(){ Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6379").setDatabase(0); return Redisson.create(config); } @Bean(name = "redissonRed2") public RedissonClient redissonRed2(){ Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6380").setDatabase(0); return Redisson.create(config); } @Bean(name = "redissonRed3") public RedissonClient redissonRed3(){ Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6381").setDatabase(0); return Redisson.create(config); } @Bean(name = "redissonRed4") public RedissonClient redissonRed4(){ Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6382").setDatabase(0); return Redisson.create(config); } @Bean(name = "redissonRed5") public RedissonClient redissonRed5(){ Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6383").setDatabase(0); return Redisson.create(config); } } ### 2:红锁使用 ### package com.online.taxi.order.service.impl; import com.online.taxi.order.constant.RedisKeyConstant; import com.online.taxi.order.service.GrabService; import com.online.taxi.order.service.OrderService; import org.redisson.Redisson; import org.redisson.RedissonRedLock; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service public class GrabRedisRedissonRedLockLockServiceImpl implements GrabService { // 红锁 @Autowired @Qualifier("redissonRed1") private RedissonClient redissonRed1; @Autowired @Qualifier("redissonRed2") private RedissonClient redissonRed2; @Autowired @Qualifier("redissonRed3") private RedissonClient redissonRed3; @Autowired @Qualifier("redissonRed4") private RedissonClient redissonRed4; @Autowired @Qualifier("redissonRed5") private RedissonClient redissonRed5; @Autowired OrderService orderService; @Override public String grabOrder(int orderId , int driverId){ System.out.println("红锁实现类"); //生成key String lockKey = ("" + orderId).intern(); //redisson锁 单节点 // RLock rLock = redissonRed1.getLock(lockKey); //红锁 redis son RLock rLock1 = redissonRed1.getLock(lockKey); RLock rLock2 = redissonRed2.getLock(lockKey); RLock rLock3 = redissonRed3.getLock(lockKey); RLock rLock4 = redissonRed4.getLock(lockKey); RLock rLock5 = redissonRed5.getLock(lockKey); RedissonRedLock rLock = new RedissonRedLock(rLock1,rLock2,rLock3,rLock4,rLock5); try { /**红锁 * waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败 * leaseTime 锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完) */ boolean b1 = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS); if (b1){ System.out.println("加锁成功"); // 此代码默认 设置key 超时时间30秒,过10秒,再延时 System.out.println("司机:"+driverId+" 执行抢单逻辑"); boolean b = orderService.grab(orderId, driverId); if(b) { System.out.println("司机:"+driverId+" 抢单成功"); }else { System.out.println("司机:"+driverId+" 抢单失败"); } System.out.println("加锁成功"); }else { System.out.println("加锁失败"); } } finally { rLock.unlock(); } return null; } } [-Redis_Redisson]: https://blog.csdn.net/qq_41694906/article/details/124906434 [-_mysql]: https://blog.csdn.net/qq_41694906/article/details/124906307 [-Redis]: https://blog.csdn.net/qq_41694906/article/details/124906428 [547316cf12ae41d18969f579f25c5560.png]: https://img-blog.csdnimg.cn/547316cf12ae41d18969f579f25c5560.png
还没有评论,来说两句吧...