分布式应用进行逻辑处理的时候,会遇到并发问题。
比如修改一个用户的状态,需要先读取用户的状态,在内存中进行修改,修改完后再放回去。如果这样的操作同时进行,就会出现并发的问题。以为读取和写入操作都不是原子级别的。
(原子操作:是指不会被线程调度机制打断的操作,一旦操作开始,就一直运行到结束,中间不会有线程转换。)
这个时候就考虑分布式锁来限制程序的并发。
分布式锁
- 加锁,执行,释放锁
> setnx lock:codehole true
OK
... do something critical ...
> del lock:codehole
(integer) 1
缺点:在执行的时候出现异常,导致不能执行del lock。从而死锁。
- 加锁,设置时间,执行,时间到释放锁
> setnx lock:codehole true
OK
> expire lock:codehole 5
... do something critical ...
> del lock:codehole
(integer) 1
缺点:如果setnx expire 之间的服务器进程挂调了,会导致expire不执行,死锁。
- Redis 2.8加入了 set 指令的扩展参数,使得 setnx 和expire 指令可以一起执行,彻底解决了分布式锁的乱象。
> set lock:codehole true ex 5 nx
OK
... do something critical ...
> del lock:codehole
超时问题
Redis分布式锁不能解决超时问题。如果加锁和释放锁之间处理的时间太长,超过了锁的时间限制,就出现问题了。这时锁过期了,第二个线程拿到了锁。等第一个线程执行完释放锁,第三个线程会在第二个线程没执行完的时候拿到锁。
为了避免这个问题,Redis分布式锁不要用于较长时间的任务,偶尔出现这种情况请人工干预一下子。
可重入性
指的是,在线程持有锁的时候再次请求加锁,如果一个锁支持同一个线程多次加锁,那么这个锁就是可重入的。
java中的ReentrantLock就是可重入锁,Redis分布式锁如果要支持可重入性,需要对客户端的set进行重新包装,使用线程的 Threadlocal 变量
存储当前持有锁的计数。