一、数据库锁
- 基于MySQL锁表
完全依靠数据库唯一索引来实现,当想要获得锁时,即向数据库中插入一条记录,释放锁时就删除这条记录
这种方式存在以下问题:
-
锁没有失效时间,解锁失败会导致死锁,其他线程无法再获取到锁,因为唯一索引insert都会返回失败
-
只能是非阻塞锁,insert失败直接就报错了,无法进入队列进行重试
-
不可重入,同一线程在没有释放锁之前无法再获取到锁
-
采用乐观锁
增加版本号,根据版本号来判断更新之前有没有其他线程更新过,如果被更新过,则获取锁失败
二、缓存锁
这里主要是几种基于redis的
- 基于
setnx
、expire
基于setnx(set if not exist)的特点,当缓存里key不存在时,才会去set,否则直接返回false
如果返回true则获取到锁,否则获取锁失败,为了防止死锁,我们再用expire命令对这个key设置一个超时时间来避免。
但是这里看似完美,实则有缺陷,当我们setnx成功后,线程发生异常中断,expire还没来的及设置,那么就会产生死锁。
解决上述问题有两种方案
- 采用redis2.6.12版本以后的set,它提供了一系列选项
EX seconds
– 设置键key的过期时间,单位时秒
PX millisecond