分布式锁概念
解决分布式服务下,多线程环境对同一份资源竞争的数据安全性,分布式锁是 用于保证整个集群内的多线程并发线程安全性的一种手段
分布式锁特征
高性能、可重入、防死锁和互斥性
- 高性能:最求高QPS
- 可重入:当一次请求的代码中包含两个锁时,并且递归调用两个锁时,要保证可重入,否则会出现死锁
,重入几次就要释放几次
public synchronized void test1() {
// 阻塞卡死了
test2();
}
public synchronized void test2() {
test1()
}
- 防死锁
除上面的情况下,如果加锁后服务器宕机,锁不释放也会产生死锁
- 互斥性
实现分布式锁的方式
- Mysql
性能低下,通过insert时的唯一索引实现
- redis
可以通过setNx方法实现,这个是最简单的分布式锁,比如执行一个方法时,加一个锁,且不设置过期时间,或者设置一个很大的过期时间,但是有弊端,对于业务量不高也可以使用
另外一种就是使用redission框架,这个安全性更高
- zookeeper
该锁最安全,但是成本有点大,因为还要引进zookeeper
使用场景
- 保证接口的幂等,防止重复提交,这个时很高频的使用
- 分布式的任务调度,分布式的系统可能多个执行器执行定时任务,防止任务重复执行,使用分布式锁,这个在我执行实现的任务调度中也使用过
- 防止超卖的情况
这个实际工作中我也遇到过,做过抢单业务都会遇到此种情况
使用redis实现分布式锁特性
- 原子性
由于redis的设置分布式锁,需要两个命令
- 设置key value
- 设置过期时间
即便使用setNx方法,底层也是两个命令
setNX item-4 111
set item-4 111 EX 20
如果第一个命令执行成功了,第二个失败了,那么就没有了过期时间,照成死锁,就不具备原子性,要么都成功,要么都失败,所以要通过使用lua脚本来解决
- 过期时间
上面说了,没有过期时间可能会出现死锁的情况
- 锁续期
如果一个业务锁到了过期时间但是业务没有执行完,就会造成锁失效,没有锁住,这部分的解决方案就是通过watchdog来实现
- 正确释放锁
A上的锁被B释放了,这样就会造成错误释放锁,原因是A与B的key值是相同的,所以要注意key值
使用redis实现分布式锁的部署方式
- 单机
简单,但是风险大
- 哨兵 主从
如果mater挂掉,又没同步到slave会导致,锁丢失
- 集群
虽然是集群但是,也是部署在集群中其中一台机器上,挂掉就没了
- 红锁
全都是master,但是性能低,成本高
redission实现分布式锁
加锁
watchdog锁续期
watch会在锁在剩余三分之一时间过期时自动续期,从而实现锁续期