用Redis实现分布式锁,2.6.12之前版本方案:setnx加锁,del释放锁,如果锁没释放,设置过期时间,到了时间,del释放锁。但是,这会存在一些问题。
- setnx和expire不是原子操作。一旦redis宕机,expire没有设置成功,锁就无法释放。只有一个请求的setnx可以成功,任何一个请求的expire都可以成功。请求比较密集,过期时间一直刷新,导致锁一直有效。
- 超时后,删除其他线程的锁。在线程A执行过程中,锁已释放,A还未在执行业务,但是还未删除锁。线程B获取锁执行业务,线程A执行完,A误删B的锁。
- 多个线程并发获取锁、释放锁。同一时间有线程A、B在访问同一代码块。
对于上面的隐患,Redis已改善。下面,我们针对隐患逐一改善。
- Redis2.6.12以上版本,可以用set获取锁。set可以实现setnx和expire,这个是原子操作。
- Lua删除锁。Lua是原子操作。
- 让获取锁的线程开启一个守护线程,给线程还没执行完,又快要过期的锁续航。大概是这样的,线程A还没执行完,守护线程每当快过期时,延时expire时间。当线程A执行完,显示关闭守护线程。如果中间宕机,锁超过超时,守护线程也不在了,自动释放锁。