SET
语法
SET key value [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL] [NX|XX] [GET]
SETEX
可以通过SET实现
语法
SETEX key ttl value
返回值:RESP simple string(可以通俗的理解为字符串OK
)
描述
设置key=value,键存活时间ttl秒。
上述命令等价于下面的命令
SET key value
EXPIRE key ttl
SETEX命令是原子的,可以通过在MULTI/EXEC块中使用前两个命令重现。
SETEX是作为序列操作时的更快的解决方案,当Redis用于缓存时,这个命令使用频繁。
存活时间只支持整数秒,否则报错。
例子
redis> SETEX keyName 10 value
"OK"
SETNX
可以通过SET实现
语法
SETNX key value
返回值:成功设置键返回1,否则返回0
描述
如果键不存在,则设置key=value;如果键存在,不进行任何操作。
设计模式:SETNX实现锁
注意:
- Redlock算法实现复杂,通过下面的模式实现还是差点意思,但是下面的模式在实现锁方面能提供很好的保证和容错。
SETNX曾一度被用来作为锁函数,获取锁的命令如下:
GETSET lock <currentUnixTimestamp + lockTimeout + 1>
lock的值为过期点的时间戳
死锁处理
如果客户端因为某种原因无法释放锁,此时如何处理?
正确的操作:
-
用户2通过SETNX获取锁
-
如果获取失败,通过GET查看锁过期时间(因为设置键值对时值表示的过期时间)
-
如果锁没有过期,则休眠若干秒后重新获取
-
如果锁过期了,通过如下命令尝试获取锁
GETSET lock <currentUnixTimestamp + lockTimeout + 1>
-
GETSET会返回用户1设置的旧值,因此可以通过判断旧值是否被修改得出锁是否被其他人获取,如果没有则获取;如果被别人获取了,则重新开始第1步
注意:GETSET会成功设置值,并返回旧值,所以用户2执行GETSET会把用户3设置的值修改了,但是相当于把时间延后几秒,整体来说不打紧。
-
如果用户3先获取了,则旧值已经变成了用户3设置的值,那么用户2放弃获取锁
上述操作对应的伪代码如下
//当前时间戳假设为10
int getLock = SETNX lock 30;
if (getLock == 1) {
System.out.println("成功获取到锁");
} else {
//说明锁被其他客户端持有
int expireTimestamp = GET lock;
int currentTimestamp = 15;
if (currentTimestamp > expireTimestamp) {
//说明锁已经过期了
int getLockAgain = GETSET lock 40;
if (getLockAgain == expireTimestamp) {
System.out.println("成功获取到锁");
} else {
System.out.println("获取锁失败,锁已经被其他人先获取了");
continue; //等待一段时候后,重新执行开始的逻辑
}
}
}
为了让锁算法更加健壮,客户端在没有释放锁前最好定期检查锁过期时间。