腔棘鱼现在已经灭绝,但曾经是一种奇怪的动物,徜徉于早二叠纪的海洋。它的体型和外形或多或少与当今的大白鲨类似。它很可能是一头可怕的海中掠食者。它与众不同的一点是长有一个“齿圈”,有点像一个类似圆锯的下颌内长着鲨鱼牙齿。这似乎是个好主意,但进化另有想法,我们现在也没有任何类似的动物。一条进化死胡同。
在某些方面,Redis SET 命令就像腔棘鱼,但它仍然畅游于全球各地的 Redis 服务器中。它是一个非常早期的命令,具有一些不寻常的功能,这些功能看起来像个好主意,但如果正确使用,可能会被证明是危险的,但又非常有用。
另一方面,SET 命令看似非常普通和简单。我们在学习时会用它作为第一个命令,并且我们用它来进行一个简单的测试,以确保 Redis 正常运行。我无法告诉你我输入多少次该命令
> SET foo bar
因此,表面上看没什么特别的。但它隐藏着什么吗?
回到我们简单的 SET 示例。让我们添加更多上下文
> UNLINK foo
(整型) 1
> HSET foo bar 123
(整型) 1
> SET foo bar
确定
你是否注意到 SET 中这里的不寻常之处?现有的键 foo 是散列类型(由于 HSET),但当我立即运行 SET 时,它仍然接受该命令。与其他 Redis 命令相比,这实际上非常奇怪。让我们采用相同的命令,但颠倒最后两个命令的顺序
> UNLINK foo
(整型) 1
> SET foo bar
确定
> HSET foo bar 123
(错误) WRONGTYPE 操作对保存有错误类型值的键
你可以看到,SET 忽略键的存在或类型,并且始终写入。另一方面,当面对不同类型的非空键时,散列会抛出错误。除 String 以及明确的 SET 命令以及一些变体 (PSETEX、SETEX、MSET) 之外,所有数据类型都满足相同的条件。例如,此命令
> HSET foo bar 123
(整型) 1
> APPEND foo bar
(错误) WRONGTYPE 操作对保存有错误类型值的键
> INCR foo
(错误) WRONGTYPE 操作对保存有错误类型值的键
> SETBIT foo 1 1
(错误) WRONGTYPE 操作对保存有错误类型值的键
> BITFIELD foo SET u8 0 1
(错误) WRONGTYPE 操作对保存有错误类型值的键
> INCRBY foo 1
(错误) WRONGTYPE 操作对保存有错误类型值的键
> INCRBYFLOAT foo 1
(错误) WRONGTYPE 操作对保存有错误类型值的键
> SETRANGE foo 1 barbar
(错误) WRONGTYPE 操作对保存有错误类型值的键
SETNX 和 SET...NX(稍后将详细介绍)是一个有趣的旁注,如果该键不存在,它们将设置为 SET,如果设置成功,则返回 1,否则返回 0。因此,它不进行类型检查,而是进行存在检查。
总之,SET 并不关心类型。它总是写入,很少有东西能阻挡它的路径。
如果您在 Redis 中盘旋了几次,您便会知道您可以使用 TYPE 命令检索存储在键中的数据类型。例如,让我们回到foo
> SET foo bar
确定
> TYPE foo
string
足够简单。您在键foo处设置值“bar”。现在,我们看看其他一些东西
> SET foo 1234
确定
> TYPE foo
String
> GETRANGE foo 2 3
"34"
因此,您可能认为数字 1234 存储为字符,并且您或多或少是正确的。然而,这个故事还有更多
> INCR foo
(integer) 1235
> GETRANGE foo 2 3
"35"
这说明 Redis 将字符理解为文本和数字——可以将其视为一种松散的类型。但它变糟了
> SET foo "hello world"
确定
> INCR foo
(error) ERR value is not an integer or out of range
很明显,Redis 不能对非数字进行增量。但还有更多细节需要涵盖。Redis 还理解浮点值。以这个示例为例
> SET foo 1.2
确定
> INCR foo
(error) ERR value is not an integer or out of range
> INCRBYFLOAT foo 0.8
"2"
> INCR foo
(integer) 3
您可以看到初始值被作为浮点数输入,因此 INCR(用于整数)不起作用。但是,INCRBYFLOAT 确实起作用。这将值更改为可以使用先前不允许的 INCR 命令的整数。
该命令的另一个特点是能够提供两类可选参数:一类用于过期,另一类用于存在性检查。我们来看看第一个类别:过期参数。
对于大多数命令,如果您想立即使一个键过期,您需要立即发出 EXPIRE 或 PEXPIRE,通常在 MULTI / EXEC 事务中。例如
> MULTI
确定
> SADD baz alpha beta gamma
QUEUED
> EXPIRE baz 10
QUEUED
> EXEC
1) (integer) 3
2) (integer) 1
这样可以确保您不会在 SADD 和 EXPIRE 命令之间被打断。您知道,在执行 EXEC 之后,即可获得一组将在 10 秒后过期的集合。然而,使用 SET,您可以在没有事务的情况下执行此操作。
> SET foo bar EX 10
确定
或者,可以使用 PX 代替 EX 以毫秒为单位过期而不是以秒为单位过期。这是一个方便的速记,也可以用 SETEX 和 PSETEX 表示。我认为这些命令只是快捷方式——它们在您的应用程序中交换了几个按键和与服务器之间传输的几个字节,以降低可读性和灵活性。
另一种论证类别 NX / XX 控制 SET 如何使用现存或不存在的数据。NX 键只在键不存在的情况下设置值。因此,请注意以下示例
> UNLINK foo
(integer) 0
> SET foo 1234 NX
确定
> GET foo
"1234"
> SET foo 5678 NX
(nil)
> GET foo
"1234"
您可以看到,第四个命令实际上没有执行任何操作,因为键 foo 已经存在。这有多种用途:设置默认值并且不覆盖现有的数据、防止在用户输入是键的组成部分时意外执行 SETS 等。
与此相反的是 XX 命令。这仅在键已经存在时设置值。
> UNLINK foo
(整型) 1
> SET foo 1234 XX
(nil)
> set foo 1234
确定
> SET foo 5678 XX
确定
这可用于将写入限制到明确定义的键。这项工作无法执行类型检查。因此,只要键存在,XX 仍然会覆盖另一种类型的键。
完全不是。SET 是 Redis 中许多优秀模式操作的基础。但是,它含有多项与 Redis 其他部分大不相同的功能。了解这些功能的工作方式非常重要,才能对如何构建键空间并使 Redis 在您的应用程序中运行做出正确的假设。