分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁的实现。
如果不同的系统或同一个系统的主机之间共享了某个资源时,往往需要互斥来防止彼此干扰,进而保证一致性。
分布式锁需要解决的问题:
1.互斥性:任意时刻只能有一个客户端获取锁,不能同时有两个客户端获取到锁;
2.安全性:锁只能被持有的客户端删除,不能由其他客户端删除;
3.死锁:获取锁的客户端因为某些原因而宕机而未能释放锁,其他客户端再也无法获取到该锁导致的死锁,此时需要有机制来避免问题的发生;
4.容错:当部分节点如redis某些节点宕机时,客户端仍然能够获取锁和释放锁
SETNX key value
如果key不存在,则创建并赋值(set if not exist)
时间复杂度:O(1)
返回值:设置成功,返回1;设置失败,返回0
setnx是原子性的,初期时候被人们用来实现分布式锁。在执行某段代码时候可以先尝试使用setnx,为某个key设值。如果设置成功,则证明此时没有别的线程在执行该段代码,或者说占用该独占资源,这个时候我们线程就可以顺利地去执行该段代码逻辑,如果设置失败,则证明此时有别的程序或者线程占用该资源。那么当前线程就需要等待,直至setnx成功。
EXPIRE key seconds
为给定key设置生存时间,当key过期时(生存时间为0),会被自动删除
缺点:原子性得不到满足
假设setnx执行成功后就直接宕机,来不及expire,此时key就会被一直占用着,意味着其他线程永远也执行不了独占的资源逻辑了,这样就违背了利用redis操作的原子性。
127.0.0.1:6379> get lock-setnx
(nil)
127.0.0.1:6379> setnx lock-setnx test
(integer) 1
127.0.0.1:6379> setnx lock-setnx test2
(integer) 0
127.0.0.1:6379> get lock-setnx
"test"
127.0.0.1:6379> expire lock-setnx 2
(integer) 1
127.0.0.1:6379> setnx lock-setnx test2
(integer) 1
127.0.0.1:6379> get lock-setnx
"test2"
SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX second:设置键的过期时间为second秒
PX millisecond:设置键的过期时间为millisecond毫秒
NX:只在键不存在时,才对键进行设置操作
XX:只在键已经存在时,才对键进行设置操作
SET操作成功完成时,返回OK,否则返回(nil)
127.0.0.1:6379> set locktarget test ex 50 nx
OK
127.0.0.1:6379> set locktarget test2 ex 50 nx
(nil)
127.0.0.1:6379> set locktarget test2 ex 50 nx
OK
大量的key同时过期的注意事项:
如果大量的key过期时间设置的过于集中,到过期的时间点,由于删除key是需要时间的,redis可能会因为一次性大量删除key出现短暂卡顿现象,面对这种情况解决方法为:在设置key的过期时间上加一个随机值,使过期时间分散一些,这样可在很大程度上避免卡顿的发生。