分布式锁相关知识预热
分布式锁简介:
分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现。
在集群架构中,多个JVM虚拟机之间为了保证数据的一致性 ,所以引进了分布式锁的的概念。
分布式锁的设计要求
- 1.在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
- 2.高可用的获取锁与释放锁
- 3.高性能的获取锁与释放锁
- 4.具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
- 5.具备锁失效机制,防止死锁
- 6.具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败
分布式锁的实现有哪些
- Memcached:利用 Memcached 的 add 命令。此命令是原子性操作,只有在 key 不存在的情况下,才能 add 成功,也就意味着线程得到了锁。
- Redis:和 Memcached 的方式类似,利用 Redis 的 setnx 命令。此命令同样是原子性操作,只有在 key 不存在的情况下,才能 set 成功。
- Zookeeper:利用 Zookeeper 的顺序临时节点,来实现分布式锁和等待队列。Zookeeper 设计的初衷,就是为了实现分布式锁服务的。
- Chubby:Google 公司实现的粗粒度分布式锁服务,底层利用了 Paxos 一致性算法。
布式锁实现的三个核心要素:加锁、解锁、锁超时
加锁
最简单的方法是使用redis的 setnx 命令。key 是锁的唯一标识,按业务来决定命名。比如想要给一种商品的秒杀活动加锁,可以给 key 命名为 “lock_sale_商品ID” 。
而 value 设置成什么呢?我们可以姑且设置成 1。加锁的伪代码如下:
setnx(lock_sale_商品ID,1)
当一个线程执行 setnx 返回 1,说明 key 原本不存在,该线程成功得到了锁;当一个线程执行 setnx 返回 0,说明 key 已经存在,该线程抢锁失败。
解锁
有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行 del 指令,伪代码如下:
del(lock_sale_商品ID)
释放锁之后,其他线程就可以继续执行 setnx 命令来获得锁。
锁超时
锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住(死锁),别的线程再也别想进来。
所以,setnx 的 key 必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。setnx 不支持超时参数,所以需要额外的指令,伪代码如下:
expire(lock_sale_商品ID, 30)
分布式锁应用的场景举例:
- 在电商系统里面生成订单号;
- 电影票购票的订单号等;
主要就是全局ID的生成策略
使用分布式锁生成订单号技术
1.使用数据库实现分布式锁
缺点 : 性能差、线程出现异常时,容易出现死锁
2.使用redis实现分布式锁
缺点 : 锁的失效时间难控制、容易产生死锁、非阻塞式、不可重入(不过可以通过技术手段实现可重入,有兴趣的可以查看redis实现分布式锁)
分布式情况下,怎么解决订单号生成不重复
- 使用分布式锁
- 提前生成好,订单号,存放在redis取。获取订单号,直接从redis中取。