时间:2023-03-09来源:系统城装机大师作者:佚名
redis常见的加锁命令分别是INCR、SETNX、SET
这种锁的加锁思路是:
key不存在,那么key的值会先被初始化为0,然后再执行INCR操作进行加一。
然后其它用户在执行INCR操作进行加一时,如果返回的数值大于1,说明这个key正在被锁定使用。
1、 客户端A请求服务器获取key的值为1表示获取了锁
2、 客户端B也去请求服务器获取key的值为2表示获取锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求的时候获取key的值为1表示获取锁成功
5、 客户端B执行代码完成,删除锁
1 2 |
$redis->incr($ key ); $redis->expire($ key , $ttl); //设置生成时间为1秒 |
具体使用命令:
1 | 127.0.0.1:6379>INCR keyName |
这种加锁的思路是,如果key不存在,将key设置为value,如果key存在,则SETNX不做任何动作。
SETNX是SET if Not eXists的缩写。
1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁
1 2 |
$redis->setNX($ key , $value); $redis->expire($ key , $ttl); |
具体使用命令:
1 2 |
redis> SETNX keyName value ( integer ) 1 |
设置成功,返回1;设置失败,返回0
上面两种方法都有一个问题,会发现,都需要设置 key 过期。
那么为什么要设置key过期呢?
如果请求执行因为某些原因意外退出了,导致创建了锁但是没有删除锁,那么这个锁将一直存在,以至于以后缓存再也得不到更新。
于是乎我们需要给锁加一个过期时间以防不测。
但是借助 Expire 来设置就不是原子性操作了。
所以还可以通过事务来确保原子性,但是还是有些问题,所以官方就引用了另外一个,使用 SET 命令本身已经从版本 2.6.12 开始包含了设置过期时间的功能。
1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁
1 | $redis-> set ($ key , $value, array( 'nx' , 'ex' => $ttl)); //ex表示秒 |
具体使用:
1 | 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个参数决定加锁代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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 ; } |
解锁代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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【保证安全性】
2023-11-01
React中immutable的使用2023-11-01
命令行清除Redis缓存的实现2023-11-01
Redis缓存空间优化实践详解引言大厂很多项目都是部署到多台服务器上,这些服务器在各个地区都存在,当我们访问服务时虽然执行的是同一个服务,但是可能是不同服务器运行的;在我学习项目时遇到这样一个登录情...
2023-11-01
1.多次修改一个redis的String过期键,如何保证他仍然能保留第一次设置时的删除时间 2.修改hash、set、Zset、list的值,会使过期时间重置吗?...
2023-11-01