失效链接处理 |
Redis分布式锁深度介绍 PDF 下载
本站整理下载:
提取码:my3c
相关截图:
主要内容:
1.知识准备
必须的前置知识包括:
Redis的基本命令 ** Spring的Java配置方式
JDK中的线程同步方式,例如synchronize关键字,Lock 等 ** 下列知识如果也会,会更轻松:
SpringBoot基本使用 SpringDataRedis的基本使用 zookeeper的使用
2.什么是分布式锁
在讨论分布式锁前,我们先假设一个业务场景:
2.1.业务场景
我们假设一个这样的业务场景:
在电商中,用户购买商品需要扣减商品库存,一般有两种扣减库存方式:
下单减库存
优点:用户体验好,下单成功,库存直接扣除,用户支付不会出现库存不足情况缺点:用户一直不付款,这个商品的库存就会被占用,其他人就无法购买了。
支付减库存
优点:不会导致库存被恶意锁定,对商家有利
缺点:用户体验不好,用户支付时可能商品库存不足了,会导致用户交易失败
那么,我们一般为了用户体验,会采用下单减库存。但是为了解决下单减库存的缺陷,会创建一个定时任务,定时去清理超时未支付的订单。
在这个定时任务中,需要完成的业务步骤主要包括:
1.查询超时未支付订单,获取订单中商品信息
2.修改这些未支付订单的状态,为已关闭
3.恢复订单中商品扣减的库存如图:
但是,如果我们给订单服务搭建一个100台服务节点的集群,那么就会在同一时刻有100个定时任务触发并执行,设想一下这样的场景:
订单服务A执行了步骤1,但还没有执行步骤B 订单服务B执行了步骤1,于是查询到了与订单服务A查询到的一样的数据订单服务A执行步骤2和3,此时订单中对应商品的库存已经恢复了订单服务B也执行了步骤2和步骤3,此时订单中对应商品的库存再次被增加库存被错误的恢复了多次,事实上只需要执行一次就可以了。
就像这样:
因为任务的并发执行,出现了线程安全问题,商品库存被错误的增加了多次,你能想到解决办法吗?
2.2.为什么需要分布式锁 对于线程安全问题,我们都很熟悉了,传统的解决方案就是对线程操作资源的代码加锁。如图:
理想状态下,加了锁以后,在当前订单服务执行时,其它订单服务需要等待当前订单服务完成业务后才能执行,这样就避免了线程安全问题的发生。
但是,这样真的能解决问题吗?答案时否定的,为什么呢。
2.2.1.线程锁
我们通常使用的synchronized或者Lock都是线程锁,对同一个JVM进程内的多个线程有效。因为锁的本质是内存中存放一个标记,记录获取锁的线程时谁,这个标记对每个线程都可见。
获取锁:就是判断标记中是否已经有线程存在,如果有,则获取锁失败,如果没有,在标记中记录当前线程
释放锁:就是删除标记中保存的线程,并唤醒等待队列中的其它线程因此,锁生效的前提是:
互斥:锁的标记只有一个线程可以获取共享:标记对所有线程可见
然而我们启动的多个订单服务,就是多个JVM,内存中的锁显然是不共享的,每个JVM进程都有自己的锁,自然无法保证线程的互斥了,如图:
要解决这个问题,就必须保证各个订单服务能够共享内存中的锁标记,此时,分布式锁就闪亮登场了!
2.2.2.分布式锁
线程锁时一个多线程可见的内存标记,保证同一个任务,同一时刻只能被多线程中的某一个执行。但是这样的锁在分布式系统中,多进程环境下, 就达不到预期的效果了。
而如果我们将这个标记变成多进程可见,保证这个任务同一时刻只能被多个进程中的某一个执行,那这样的锁就是分布式锁了。
|