1.redis 命令 set/SETBIT/SETEX/SETNX
设置key的字符串值
#语法
SET key value [NX | XX] [GET] [EX seconds | PX milliseconds |
EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]
可用版本:
1.0.0
时间复杂度:
O(1)
ACL 类别:
@write, @string, @slow
设置key为保存字符串value。如果key已经保存了一个值,则无论其类型如何,都会将其覆盖。
任何先前与该键关联的生存时间在成功SET操作时都将被丢弃。
#选项
该SET命令支持一组修改其行为的选项:
EX seconds -- 设置指定的过期时间,以秒为单位。
PX 毫秒——设置指定的过期时间,以毫秒为单位。
EXAT timestamp-seconds -- 设置key过期的指定 Unix 时间,以秒为单位。
PXAT timestamp-milliseconds -- 设置key过期的指定 Unix 时间,以毫秒为单位。
NX-- 仅当key不存在时才设置它。
XX-- 仅当key已存在时才设置它。
KEEPTTL-- 保留与key关联的生存时间。
GET-- 返回存储在 key 中的旧字符串,如果 key 不存在,则返回 nil。SET如果存储在 key 的值不是字符串,则返回并中止错误。
注意:由于SET命令选项可以替换 SETNX, SETEX, PSETEX, GETSET,因此在未来的 Redis 版本中,这些命令可能会被弃用并最终被删除。
#返回
简单字符串回复 :OK如果SET正确执行。
Null 回复 :(nil)如果SET由于用户指定了NXorXX选项但不满足条件而未执行操作。
如果命令与GET选项一起发出,则上述内容不适用。它将改为如下回复,无论是否SET实际执行:
批量字符串回复 :存储在键中的旧字符串值。
空回复 :(nil)如果key不存在。
#例子
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis> SET anotherkey "will expire in a minute" EX 60
"OK"
redis>
#模式
**注意:**不鼓励使用以下模式,而支持 Redlock 算法,该算法 实现起来稍微复杂一些,但提供更好的保证并且具有容错性。
该命令SET resource-name anystring NX EX max-lock-time是使用 Redis 实现锁定系统的简单方法。
如果上述命令返回,客户端可以获取锁OK(或者如果命令返回 Nil,则在一段时间后重试),并仅使用 DEL.
达到过期时间后,锁会自动释放。
修改解锁模式可以使这个系统更加健壮,如下所示:
不是设置固定字符串,而是设置一个不可猜测的大随机字符串,称为令牌。
不要使用 释放锁,而是 DEL 发送一个仅在值匹配时删除键的脚本。
这样可以避免客户端在过期时间后尝试释放锁,删除稍后获得锁的另一个客户端创建的key。
#解锁脚本的示例类似于以下内容:
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end
#该脚本应调用EVAL ...script... 1 resource-name token-value
#历史
从 Redis 版本 2.6.12 开始:添加了、EX和PX选项。NX``XX
从 Redis 版本 6.0.0 开始:添加了该KEEPTTL选项。
从 Redis 版本 6.2.0 开始:添加了GET,EXAT和PXAT选项。
从 Redis 版本 7.0.0 开始:允许NX和GET选项一起使用
SETBIT
设置或清除存储在key处的字符串值中偏移量处的位
语法
SETBIT key offset value
可用版本:
2.2.0
时间复杂度:
O(1)
ACL 类别:
@write, @bitmap, @slow
设置或清除存储在key的字符串值中**offset的位。
该位根据value设置或清除, value可以是 0 或 1。
当key不存在时,创建一个新的字符串值。字符串被增长以确保它可以在offset保持一点。偏移量参数必须大于或等于 0,
并且小于 2^32(这将位图限制为 512MB)。当key处的字符串增长时,添加的位设置为 0。
警告:当设置最后一个可能的位(偏移量等于 2^32 -1)并且存储在key的字符串值还没有保存字符串值,或者保存一个小字符串值时,
Redis 需要分配所有可以阻塞的中间内存服务器一段时间。在 2010 款 MacBook Pro 上,设置位数 2^32 -1(512MB 分配)大约需要 300 毫秒,
设置位数 2^30 -1(128MB 分配)大约需要 80 毫秒,设置位数 2^28 -1(32MB 分配)需要约 30 毫秒,设置位号 2^26 -1(8MB 分配)需要约 8 毫秒。
请注意,一旦完成第一次分配,SETBIT对同一键的后续调用将不会产生分配开销。
#返回
整数:存储在偏移处的原始位值。
#例子
redis> SETBIT mykey 7 1 #偏移7的位置设置位;
(integer) 0
redis> SETBIT mykey 7 0 #偏移量为7的位置删除位。
(integer) 1
redis> GET mykey
""
redis>
#模式:访问整个位图
在某些情况下,您需要一次设置单个位图的所有位,例如将其初始化为默认的非零值时。可以通过多次调用SETBIT命令来执行此操作,
每个需要设置的位一个。但是,作为一种优化,您可以使用单个 SET 命令来设置整个位图。
位图不是一种实际的数据类型,而是在字符串类型上定义的一组面向位的操作。
这意味着位图可以与字符串命令一起使用,最重要的是与 SET and 一起使用 GET。
因为 Redis 的字符串是二进制安全的,所以位图被简单地编码为字节流。字符串的第一个字节对应于位图的偏移量 0..7,
第二个字节对应于 8..15 范围,依此类推。
例如,设置几个位后,获取位图的字符串值将如下所示:
> SETBIT bitmapsarestrings 2 1
> SETBIT bitmapsarestrings 3 1
> SETBIT bitmapsarestrings 5 1
> SETBIT bitmapsarestrings 10 1
> SETBIT bitmapsarestrings 11 1
> SETBIT bitmapsarestrings 14 1
> GET bitmapsarestrings
"42"
通过获取位图的字符串表示,客户端可以通过在其本机编程语言中使用本机位操作提取位值来解析响应的字节。
对称地,也可以通过在客户端执行位到字节的编码并 SET 使用结果字符串调用来设置整个位图。
#模式:设置多个位
SETBIT擅长设置单个位,需要设置多个位时可以多次调用。要优化此操作,您可以用SETBIT对
variadic 命令的单个调用 BITFIELD 和使用 type 的字段来替换多个调用u1。
例如,上面的示例可以替换为:
> BITFIELD bitsinabitmap SET u1 2 1 SET u1 3 1 SET u1 5 1 SET u1 10 1 SET u1 11 1 SET u1 14 1
#高级模式:访问位图范围
也可以使用 GETRANGE 和 SETRANGE 字符串命令来有效地访问位图中的位偏移范围
设置key的值和过期时间
设置key的值和过期时间
语法
SETEX key seconds value
可用版本:
2.0.0
时间复杂度:
O(1)
ACL 类别:
@write, @string, @slow
设置key为保存字符串value并key在给定的秒数后设置为超时。该命令相当于执行以下命令:
SET mykey value
EXPIRE mykey seconds
SETEX是原子的,可以通过在 MULTI / EXEC 块中使用前两个命令来重现。它是作为给定操作序列的更快替代方案提供的,
因为当 Redis 用作缓存时,此操作非常常见。
无效时返回seconds错误。
#返回
简单的字符串
#例子
redis> SETEX mykey 10 "Hello"
"OK"
redis> TTL mykey
(integer) 10
redis> GET mykey
"Hello"
redis>
SETNX
仅当key不存在时,才设置key的值
语法
SETNX key value
可用版本:
1.0.0
时间复杂度:
O(1)
ACL 类别:
@write, @string, @fast
如果key不存在,则设置key为保存字符串value。在这种情况下,它等于SET。当key已经持有一个值时,不执行任何操作。
SETNX是“ SET if Not eXists ”的缩写。
#返回
整数,具体来说:
1:如果设置了key
0:如果未设置key
#例子
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
redis>
#设计模式:锁定与SETNX
请注意:
不鼓励使用以下模式以支持 Redlock 算法,该算法 实现起来稍微复杂一些,但提供了更好的保证并且具有容错性。
无论如何,我们都会记录旧模式,因为某些现有实现链接到此页面作为参考。此外,这是一个有趣的例子,说明了如何使用 Redis 命令来挂载编程原语。
无论如何,即使假设一个单实例锁定原语,从 2.6.12 开始,也可以创建一个更简单的锁定原语,相当于这里讨论的那个,
使用 SET 获取锁的命令和一个简单的 Lua 脚本来释放锁. 该模式记录在 SET 命令页面中。
也就是说,SETNX可以使用并且在历史上被用作锁定原语。例如,要获取 key 的锁foo,客户端可以尝试以下操作:
SETNX lock.foo <current Unix time + lock timeout + 1>
如果SETNX返回1客户端获取锁,则将lock.foo key设置为不再认为锁有效的 Unix 时间。客户端稍后将使用DEL lock.foo以释放锁。
如果SETNX返回0,则key已被其他客户端锁定。如果它是一个非阻塞锁,我们可以返回给调用者,或者进入一个循环重试持有锁,直到我们成功或某种超时到期。
#处理死锁
在上述锁定算法中存在一个问题:如果客户端失败、崩溃或无法释放锁定会发生什么?可以检测到这种情况,
因为锁定键包含 UNIX 时间戳。如果这样的时间戳等于当前的 Unix 时间,则锁不再有效。
当这种情况发生时,我们不能只调用 DEL key来移除锁,然后尝试发出 a SETNX,因为这里有一个竞争条件,当多个客户端检测到一个过期的锁并试图释放它时。
C1 和 C2 读取lock.foo以检查时间戳,因为它们都是 在执行SETNX后收到的0,因为锁仍然由持有锁后崩溃的 C3 持有。
C1 发送DEL lock.foo
C1发送SETNX lock.foo并成功
C2 发送DEL lock.foo
C2发送SETNX lock.foo并成功
错误:C1 和 C2 都因为竞争条件而获得了锁。
幸运的是,使用以下算法可以避免这个问题。让我们看看我们理智的客户端 C4 是如何使用好的算法的:
C4 发送SETNX lock.foo以获取锁
崩溃的客户端 C3 仍然持有它,因此 Redis 将回复0C4。
C4 发送GET lock.foo检查锁是否过期。如果不是,它将休眠一段时间并从头开始重试。
相反,如果锁因 Unix 时间lock.foo早于当前 Unix 时间而过期,C4 会尝试执行:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>
由于 GETSET 语义,C4 可以检查存储的旧值 key是否仍然是过期的时间戳。如果是,则获得了锁。
如果另一个客户端(例如 C5)比 C4 快并通过该 GETSET 操作获得了锁,则 C4 GETSET 操作将返回一个未过期的时间戳。
C4 将简单地从第一步重新开始。请注意,即使 C4 在未来几秒钟内设置key,这也不是问题。
为了使这种锁定算法更加健壮,持有锁的客户端在解锁key之前应该始终检查超时没有过期, DEL 因为客户端故障可能很复杂,
不仅崩溃而且还会阻塞大量时间来执行某些操作和尝试 DEL 在很长一段时间后发出(当 LOCK 已被另一个客户端持有时)。