在多线程环境下,一般会使用锁来保证有且只有一个线程来操做共享资源。好比:git
object obj = new object(); lock (obj) { //操做共享资源 }
利用操做系统提供的锁机制,能够确保多线程或多进程下的并发惟一操做。但若是在多机环境下就不能知足了,当A,B两台机器同时操做C机器的共享资源时,就须要第三方的锁机制来保证在分布式环境下的资源协调,也称分布式锁。github
Redis有三个最基本属性来保证分布式锁的有效实现:redis
因为Redis是单线程模型,命令操做原子性,因此利用这个特性能够很容易的实现分布式锁。 得到一个锁算法
SET key uuid NX PX timeout SET resource_name uniqueVal NX PX 30000
命令中的NX表示若是key不存在就添加,存在则直接返回。PX表示以毫秒为单位设置key的过时时间,这里是30000ms。 设置过时时间是防止得到锁的客户端忽然崩溃掉或其余异常状况,致使redis中的对象锁一直没法释放,形成死锁。 Key的值须要在全部请求锁服务的客户端中,确保是个惟一值。 这是为了保证拿到锁的客户端能安全释放锁,防止这个锁对象被其余客户端删除。 举个例子:安全
要避免例子中的状况发生,就要保证key的值是惟一的,只有拿到锁的客户端才能进行删除。 基于这个缘由,普通的del命令是不能知足要求的,咱们须要一个能判断客户端传过来的value和锁对象的value是否同样的命令。遗憾的是Redis并无这样的命令,但能够经过Lua脚原本完成:多线程
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
逻辑很简单,获取key中的值和参数中的值相比较,相等删除,不相等返回0。 并发
上面是在单个Redis实例实现分布式锁的,这存在一个问题就是,若是这台实例因某些缘由崩溃掉,那么全部客户端的锁服务所有失效。 Redis自己支持Master-Slave结构,能够一主多从,采用高可用方法,能够保证在master挂的时候自动切换到slave。 可是因为主从之间是异步同步数据的,因此redis并不能彻底的实现锁的安全性。 举个例子来讲:异步
在多台master状况下实现这个算法,并保证锁的安全性。 步骤以下:分布式
上面描述可能不方便理解,用代码表示以下:ui
//锁自动释放时间 TimeSpan ttl=new TimeSpan(0,0,0,30000) //获取锁成功的数量 int n = 0; //记录开始时间 var startTime = DateTime.Now; //在每一个实例上获取锁 for_each_redis( redis => { if (LockInstance(redis, resource, val, ttl)) n += 1; } ); //偏移时间是锁自动释放时间的1%,根据上面10s是5-50毫秒推出。 var drift = Convert.ToInt32(ttl.TotalMilliseconds * 0.01); //锁对象的有效时间=锁自动释放时间-(当前时间-开始时间)-偏移时间 var validity_time = ttl - (DateTime.Now - startTime) - new TimeSpan(0, 0, 0, 0, drift); //判断成功的数量和有效时间c值是否大于0 if (n >= (N/2+1) && validity_time.TotalMilliseconds > 0) { }
用Redis作分布式锁相比其余分布式锁(zookeeper)实现更简单,速度更快。 在ServiceStack.Redis客户端组件上是直接支持锁实现的。 或者用stackexchange客户端组件,锁实现及示例代码:https://github.com/kidfashion/redlock-cs。