redis常见的加锁命令分别是incr、setnx、set
1、incr这种锁的加锁思路是:
key不存在,那么key的值会先被初始化为0,然后再执行incr操作进行加一。
然后其它用户在执行incr操作进行加一时,如果返回的数值大于1,说明这个key正在被锁定使用。
1、 客户端a请求服务器获取key的值为1表示获取了锁
2、 客户端b也去请求服务器获取key的值为2表示获取锁失败
3、 客户端a执行代码完成,删除锁
4、 客户端b在等待一段时间后在去请求的时候获取key的值为1表示获取锁成功
5、 客户端b执行代码完成,删除锁
$redis->incr($key);$redis->expire($key, $ttl); //设置生成时间为1秒
具体使用命令:
127.0.0.1:6379>incr keyname
2、setnx这种加锁的思路是,如果key不存在,将key设置为value,如果key存在,则setnx不做任何动作。
setnx是set if not exists的缩写。
1、 客户端a请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端b也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端a执行代码完成,删除锁
4、 客户端b在等待一段时间后在去请求设置key的值,设置成功
5、 客户端b执行代码完成,删除锁
$redis->setnx($key, $value);$redis->expire($key, $ttl);
具体使用命令:
redis> setnx keyname value(integer) 1
设置成功,返回1;设置失败,返回0
3、set上面两种方法都有一个问题,会发现,都需要设置 key 过期。
那么为什么要设置key过期呢?
如果请求执行因为某些原因意外退出了,导致创建了锁但是没有删除锁,那么这个锁将一直存在,以至于以后缓存再也得不到更新。
于是乎我们需要给锁加一个过期时间以防不测。
但是借助 expire 来设置就不是原子性操作了。
所以还可以通过事务来确保原子性,但是还是有些问题,所以官方就引用了另外一个,使用 set 命令本身已经从版本 2.6.12 开始包含了设置过期时间的功能。
1、 客户端a请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端b也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端a执行代码完成,删除锁
4、 客户端b在等待一段时间后在去请求设置key的值,设置成功
5、 客户端b执行代码完成,删除锁
$redis->set($key, $value, array('nx', 'ex' => $ttl)); //ex表示秒
具体使用:
redis>set key value nx ex max-lock-time 实现加锁
命令解释:
key:key就是redis的key值作为锁的标识,value在这里作为客户端的标识,只有key-value都比配才有删除锁的权利【保证安全性】
max-lock-time:通过max-lock-time设置过期时间保证不会出现死锁【避免死锁】
nx:只有这个key不存才的时候才会进行操作,if not exists;
ex:设置key的过期时间为秒,具体时间由第5个参数决定
加锁代码:
jedis jedis = new jedis("127.0.0.1", 6379); private static final string success = "ok"; /** * 加锁操作 * @param key 锁标识 * @param value 客户端标识 * @param timeout 过期时间 */ public boolean lock(string key,string value,long timeout){ string var1 = jedis.set(key,value,"nx","ex",timeout); if(lock_success.equals(var1)){ return true; } return false; }
解锁代码:
jedis jedis = new jedis("127.0.0.1", 6379); private static final long unlock_success = 1l; /** * 解锁操作 * @param key 锁标识 * @param value 客户端标识 * @return */ public static boolean unlock(string key,string value){ string luascript = "if redis.call(\"get\",keys[1]) == argv[1] then return redis.call(\"del\",keys[1]) else return 0 end"; object var2 = jedis.eval(luascript,collections.singletonlist(key), collections.singletonlist(value)); if (unlock_success == var2) { return true; } return false;}
luascript 这个字符串是个lua脚本,代表的意思是如果根据key拿到的value跟传入的value相同就执行del,否则就返回0【保证安全性】
jedis.eval(string,list,list);这个命令就是去执行lua脚本,keys的集合就是第二个参数,argv的集合就是第三参数【保证解锁的原子操作】
上述就实现了怎么使用redis去正确的实现分布式锁,但是有个小缺陷就是锁过期时间要设置为多少合适,这个其实还是需要去根据业务场景考量一下的。
以上就是redis加锁的方式有哪些的详细内容。