分布式锁
分布式锁实现
数据库(乐观锁)
1 | update T t |
判断数据库返回的修改行数是否等于1,等于1则是抢到了锁,没有抢到返回0
优点:
- 简单可靠
缺点:
- 性能差
单机Redis(悲观锁)
1 | SET key value NX PX|EX time |
NX:如果key不存在时命令才成功PX|EX:过期时间(毫秒、秒)setnx命令不能设置ttl,所以建议使用set命令
TTL设置不能过小,防止主动释放晚于被动过期,可以同时运行一个守护线程续期
释放时需要Lua脚本保证原子性
1 | if redis.call("get", KEYS[1]) == ARGV[1] then |
可能发生的问题
- 锁被其他线程删除。使用lua脚本确保value和获取锁时一致
- 锁过期。合理评估执行时间,或使用看门狗机制,定时续期
- 锁不可重入。业务代码实现可重入,比如记录重入次数,当重入次数为0时,释放该锁
- 锁失效。master节点宕机,slave还未同步到该锁。使用redis集群锁
ZooKeeper
一些锁
可重入锁:同一个线程可以重复获取的锁,获取N次也需要释放N次
公平锁:按照请求顺序获取锁。优点:线程不会饿死;缺点:吞吐低,CPU唤醒线程开销大
非公平锁:直接尝试获取锁。优点:吞吐高;缺点:线程饿死
读写锁:读锁+写锁。同时只能有一个写者或多个读者