概述
在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源。比如:
object obj = new object();lock (obj) { //操作共享资源 }
利用操作系统提供的锁机制,可以确保多线程或多进程下的并发唯一操作。但如果在多机环境下就不能满足了,当A,B两台机器同时操作C机器的共享资源时,就需要第三方的锁机制来保证在分布式环境下的资源协调,也称分布式锁。
Redis有三个最基本属性来保证分布式锁的有效实现:
- 安全性: 互斥,在任何时候,只有一个客户端能持有锁。
- 活跃性A:没有死锁,即使客户端在持有锁的时候崩溃,最后也会有其他客户端能获得锁,超时机制。
- 活跃性B:故障容忍,只有大多数Redis节点时存活的,客户端仍可以获得锁和释放锁。
分布式锁
由于Redis是单线程模型,命令操作原子性,所以利用这个特性可以很容易的实现分布式锁。 获得一个锁
SET key uuid NX PX timeoutSET resource_name uniqueVal NX PX 30000
命令中的NX表示如果key不存在就添加,存在则直接返回。PX表示以毫秒为单位设置key的过期时间,这里是30000ms。 设置过期时间是防止获得锁的客户端突然崩溃掉或其他异常情况,导致redis中的对象锁一直无法释放,造成死锁。
Key的值需要在所有请求锁服务的客户端中,确保是个唯一值。 这是为了保证拿到锁的客户端能安全释放锁,防止这个锁对象被其他客户端删除。
举个例子:
- A客户端拿到对象锁,但在因为一些原因被阻塞导致无法及时释放锁。
- 因为过期时间已到,Redis中的锁对象被删除。
- B客户端请求获取锁成功。
- A客户端此时阻塞操作完成,删除key释放锁。
- C客户端请求获取锁成功。
- 这时B、C都拿到了锁,因此分布式锁失效。
要避免例子中的情况发生,就要保证key的值是唯一的,只有拿到锁的客户端才能进行删除。 基于这个原因,普通的del命令是不能满足要求的,我们需要一个能判断客户端传过来的value和锁对象的value是否一样的命令。遗憾的是Redis并没有这样的命令,但可以通过Lua脚本来完成:
if redis.call("get