redis实现分布式锁
目前市场上主要有redisson和redlock两种方案,redisson在主从架构下会出问题,因为redis主从架构只满足cap里面的ap,当主节点挂了以后可能会导致重复加锁(从节点成为主节点的时候),虽然redisson方案比zookeeper性能好,但是zk是满足cap的cp,稳定性比redisson好。redlock主要是解决redis ap不一致的方案,但是redlock性能又不是太好。
redisson实现的原理如下:
1、可以通过redis的set+nx+ex来获取锁,该命令如果key不存在就设置,否则就不设置,设置成功的线程即获得了锁
2、当任务结束后可以通过redis del key来释放锁
3、为了让线程1加的锁只能线程1释放,加锁key的value必须和加锁线程一一对应,删除的时候通过value判断是否是自己加的
上述方法实现锁遇到的问题如下:
a、当线程加锁和释放锁期间代码执行失败或机器挂了,key删除会失败,从而导致死锁(因为其他线程加锁不会成功)。
解决办法:通过对key加过期时间,当死锁后key会自动过期
c、设置key和key过期时间原子问题(加锁成功,设置过期时间不成功,又会导致死锁)
解决办法:python中通过set+nx+ex来保证原子操作
d、如果A线程加锁和释放锁期间代码执行时间很长,导致A线程所加锁过期了,其他B线程又可以加锁,当A线程执行完后释放锁会把B线程加的锁释放掉,从而锁乱序了,
解决办法:通过锁续命功能,同时让加锁key对应的值来区分是哪个线程加的锁,释放锁的时候对比该值来判断是否是自己的锁。
锁续命是通过另一个异步线程定时去维持key的过期时间,直到key被删除为止。
f、上面删除key和判断value是否是自己所设置必须原子,目前redis没有相应的原子命令。
解决办法:通过lua脚本来实现,lua脚本提交给redis会进行原子执行
e、当redis是主从架构的时候,由于redis主从同步是异步的,当A线程加锁成功后,此时redis主节点挂了,从节点升级为主节点,在某种情况下B线程也可以加锁成功。
解决办法:用redLock或者redis单节点,redLock性能不太好,redis单节点不能高可用。
redlock实现的原理如下:
redlock是为了解决redisson主从不一致的问题,为了解决这个问题,线程用redlock加锁的时候,会在多个redis实例同时加锁,这些redis实例是没有亲属关系的,只有当过半的实例加锁成功后,才认为加锁成功。