1.为什么要有分布式锁?
在单机环境下,多个线程去访问共享资源,要保证线程安全,可以在代码块上加上synchronized或lock锁。
但是在多机器或者是分布式微服务架构下,synchronized锁和lock会失效,它只能保证单个jvm内部多个线程之间的互斥,而没有办法让集群下的多个jvm进程之间互斥。所以我们需要分布式锁,满足集群模式下多进程可见(让多个jvm进程都看到同一个锁监视器)并且互斥的锁,同时还要保证这个锁的高可用,高性能以及安全性。
2.实现分布式锁
实现分布式锁的核心是实现多进程之间互斥。常见的有三种方案:
1.MySQL
2.Redis
3.Zookeeper
Redis实现分布式锁
Redis可以利用setnx这样的互斥命令来实现锁。如果插入key成功,则表示获得到了锁,如果有人插入成功,其他人插入失败则表示无法获得到锁。释放锁就是将这条key删除。
同时考虑到安全性,如果这条锁还没来得及删除,服务宕机,那么这个锁将会永远存在,其它人也拿不到锁,这样会产生死锁问题。我们可以在setnx获取锁时,同时给它加一个过期时间,将来一旦服务出现故障,没有手动释放,到期以后,锁也会自动释放。
但是自动释放锁会出现误删别人锁的情况。假设业务阻塞,锁超时释放。这时其它线程获取到锁,还没有执行完,前面这个线程突然恢复运行,将该线程的锁释放掉。
针对这种情况,可以给锁加一个唯一标识(可以用线程id),在进行删除锁的时候,先进行判断,如果这个锁是自己的再删,如果不是,执行失败。同时还要保证查询唯一标识和删除锁是一个原子操作,否则依然会有安全问题。
可以使用lua脚本保证多条命令的原子性。
锁的超时时间设置多长?
太长:无效等待时间太多
太短:业务还没执行完
设置时间:比实际业务执行时间长一点。