阅读 79

redis分布式锁存在的问题,redis分布式锁使用场景

image.png一、分布式锁定概述1、如果分布式锁定是分布式模型下只有一个数据(或有限制),则需要利用锁定技术来控制在某个时间点修改数据的过程的数量。 在独立模式下锁定不仅要使进程可见,而且要考虑进程和锁定之间的网络问题。 分布式锁只是Redis、Memcache等公共存储器,而不是某个进程分配的存储器,也可以对存储器进行标记。 关于利用数据库、文件等的上锁,和独立的安装一样,只要保证标签是互斥的就可以了。2,分布式锁具备的条件在分布式系统环境中,一种方法只能同时在一台机器上的一个线程上运行。 高可用性获取锁定和释放锁定锁定锁定具有高性能锁定获取和解锁可重入特性的锁定吊销机制,以及防止死锁的无阻塞锁定功能。 这意味着,如果没有获取锁,获取锁将失败。 3358 www.Sina.com/http://www.Sina.com/@ request mapping (/deduct _ stock ' ) public string deduct stock { string } //jedis.setnxstringredistemplate.expire (lock key,30,TimeUnit.SECONDS ); //为解决原子问题设置锁并设置超时时间的boolean result=stringredistemplate.ops for value ().setifabsent(lockkey,' aaa ',110 ) result ) { return 'error_code '; //实施业务逻辑、扣减库存……catch(exceptione ) { e.printStackTrace ); } finally { stringredistemplate.delete (lock key ); } return 'end '; ) 2、超时问题分析以上代码可以发现,当前锁的过期时间为10秒,如果当前扣减库存的业务逻辑需要15s才能执行,则在高并发时会出现问题。

首先,线程1执行到10s,然后锁定线程product_001 ),从第10s开始,它继续进入当前方法。 此时,如果使用锁定(product_001 )运行到15s,则线程1可以删除线程2使用的锁定(product_001 )线程3并锁定它. 实际的锁有http://www.Sina.com/@ request mapping (/deduct _ stock ) (public String deductStock ) {stringlockkey='produck try { //为了解决原子问题,将锁定设置和超时设置组合在一起,并将clientID作为值放入锁定中,boolean result=stringredistemplate.ops for value (.setition ) result ) { return 'error_code '; //实施业务逻辑、扣减库存……catch(exceptione ) { e.printStackTrace ); }finally { //仅当获取的锁定值为当前clientId时,锁定操作if (clientid.equals (stringredistemplate.ops for value ).get ) lockkey }这将确保每个线程删除的锁是添加到当前线程的锁,但仍然存在卖不出去的问题。 线程1在执行尚未完成时锁定已过期,因此线程2锁定成功

定义二、采用Redis实现分布式锁子线程,并定期检查1,常规代码实现。 3358www.Sina.com/的情况。

a)方案1:当前线程删除当前线程所加的锁

ong>a)简单实现 @AutowiredRedisson redisson;@RequestMapping("/deduct_stock_redisson")public String deductStockRedisson() { String lockKey = "product_001"; RLock rlock = redisson.getLock(lockKey); try { rlock.lock(); //业务逻辑实现,扣减库存 .... } catch (Exception e) { e.printStackTrace(); } finally { rlock.unlock(); } return "end";}

[图片上传失败...(image-3f8feb-1613999962476)]

多个线程去执行lock操作,仅有一个线程能够加锁成功,其它线程循环阻塞。加锁成功,锁超时时间默认30s,并开启后台线程,加锁的后台会每隔10秒去检测线程持有的锁是否存在,还在的话,就延迟锁超时时间,重新设置为30s,即锁延期。对于原子性,Redis分布式锁底层借助Lua脚本实现锁的原子性。锁延期是通过在底层用Lua进行延时,延时检测时间是对超时时间timeout /3b)tryLock()与lock()区别 lock()会阻塞等待tryLock()尝试获取锁,返回值为是否获取都锁 c)Redisson的可重入锁 Redis存储锁的数据类型是 Hash类型Hash数据类型的key值包含了当前线程信息

[图片上传失败...(image-494d35-1613999962476)]

d)采用Mq和Semaphore

信号量主要用于两个目的

一个是用于共享资源的互斥使用另一个用于并发线程数的控制

[图片上传失败...(image-40695-1613999962475)]

三、采用Redisson分布式锁的问题分析 1,主从同步问题

当主Redis加锁了,开始执行线程,若还未将锁通过异步同步的方式同步到从Redis节点,主节点就挂了,此时会把某一台从节点作为新的主节点,此时别的线程就可以加锁了,这样就出错了,怎么办?

a)采用zookeeper代替Redis

由于zk集群的特点,其支持的是CP。而Redis集群支持的则是AP。

b)采用RedLock

[图片上传失败...(image-fc10c8-1613999962475)]

假设有3个redis节点,这些节点之间既没有主从,也没有集群关系。客户端用相同的key和随机值在3个节点上请求锁,请求锁的超时时间应小于锁自动释放时间。当在2个(超过半数)redis上请求到锁的时候,才算是真正获取到了锁。如果没有获取到锁,则把部分已锁的redis释放掉。

@RequestMapping("/deduct_stock_redlock")public String deductStockRedlock() { String lockKey = "product_001"; //TODO 这里需要自己实例化不同redis实例的redisson客户端连接,这里只是伪代码用一个redisson客户端简化了 RLock rLock1 = redisson.getLock(lockKey); RLock rLock2 = redisson.getLock(lockKey); RLock rLock3 = redisson.getLock(lockKey); // 向3个redis实例尝试加锁 RedissonRedLock redLock = new RedissonRedLock(rLock1, rLock2, rLock3); boolean isLock; try { // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。 isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS); System.out.println("isLock = " + isLock); if (isLock) { //业务逻辑处理 ... } } catch (Exception e) { } finally { // 无论如何, 最后都要解锁 redLock.unlock(); }}

具体使用存在争议,不太推荐使用。如果考虑高可用并发推荐使用Redisson,考虑一致性推荐使用zookeeper。

2,提高并发:分段锁

由于Redisson实际上就是将并行的请求,转化为串行请求。这样就降低了并发的响应速度,为了解决这一问题,可以将锁进行分段处理:例如秒杀商品001,原本存在1000个商品,可以将其分为20段,为每段分配50个商品...

作者:MXC单薄的黄豆

原文链接:https://www.cnblogs.com/bbgs-xc/p/14412646.html


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐