Redis Commands Strings

APPEND Key value

开始版本 2.0.0.
时间复杂度: O(1).均摊时间复杂度是O(1), 因为redis用的动态字符串的库在每次分配空间的时候会增加一倍的可用空闲空间,所以在添加的value较小而且已经存在的 value是任意大小的情况下,均摊时间复杂度是O(1) 。

如果 key 已经存在,并且值为字符串,那么这个命令会把 value 追加到原来值(value)的结尾。 如果 key 不存在,那么它将首先创建一个空字符串的key,再执行追加操作,这种情况 APPEND 将类似于 SET 操作。

返回值

Integer reply: 返回追加到字符串后的长度。

例子

1
2
3
4
5
6
7
8
9
redis> EXISTS mykey
(integer) 0
redis> APPEND mykey "Hello"
(integer) 5
redis> APPEND mykey " World"
(integer) 11
redis> GET mykey
"Hello World"
redis>

模式:Time series

APPEND命令可以用来创建一个比列表更紧凑的固定长度的样例,通常称为time series。任何时间里有新的样例送来我们都可以使用下面的命令存储:

1
APPEND timeseries "fixed-size sample"

在time series里面访问单个元素并不难:

  • STRLEN 能够获取这个样例的长度。
  • GETRANGE 允许随机访问某些元素。如果我们的time series有相关的信息,我们就可以在Redis2.6版本里面通过GETRANGE结合Lua脚本来实现一个二分查找。
  • SETRANGE 能够覆盖一个已存在的time series。

该模式的局限在于只能做追加操作,目前无法裁剪这个time series到一个给定的大小是因为Redis目前缺少这样的命令去修剪字符串对象。但是time series以这种方式存储的空间效率是非常好的。

小贴士: 在键值中组合Unix时间戳, 可以在构建一系列相关键值时缩短键值长度,更优雅地分配Redis实例.

使用定长字符串进行温度采样的例子(在实际使用时,采用二进制格式会更好):

1
2
3
4
5
6
7
8
9
redis> APPEND ts "0043"
(integer) 4
redis> APPEND ts "0035"
(integer) 8
redis> GETRANGE ts 0 3
"0043"
redis> GETRANGE ts 4 7
"0035"
redis>

BITCOUNT key [start end]

开始版本 2.6.0.
时间复杂度: O(N)

计算给定字符串中被设置为1的bit数。

一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。
start 和 end 参数的设置和 GETRANGE 命令类似,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,以此类推。

不存在的 key 被当成是空字符串来处理,因此对一个不存在的 key 进行 BITCOUNT 操作,结果为 0

返回值

Integer reply
被设置为1的bit数量。

例子

1
2
3
4
5
6
7
8
9
redis> SET mykey "foobar"
"OK"
redis> BITCOUNT mykey
(integer) 26
redis> BITCOUNT mykey 0 0
(integer) 4
redis> BITCOUNT mykey 1 1
(integer) 6
redis>

模式: 使用bitmaps方式进行实时的量化

Bitmaps在某种信息的表示中是非常有用的。比如一个web程序需要通过用户的访问历史来确定哪些用户是beta进行测试活动的重点目标对象。
使用SETBIT命令就可以很容易的解决,采用一个很小的渐增的整数来对应每一天,程序上线的第一天用0表示,第二天用1表示,以此类推。
每次用户访问了一个页面,这个程序就可以记录下用户访问站点时候的当天值,使用SETBIT命令来设置bit对应的当天值是多少。
接下来就可以使用BITCOUNT命令来统计用户访问的总天数.

参考文章 “Fast easy realtime metrics using Redis bitmaps“.

性能考虑

前面的上线次数统计例子,即使运行 10 年,占用的空间也只是每个用户 10*365 比特位(bit),也即是每个用户 456 字节。对于这种大小的数据来说, BITCOUNT 的处理速度就像 GETINCR 这种 O(1) 复杂度的操作一样快。
如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:

  • 将一个大的 bitmap 分散到不同的 key 中,作为小的 bitmap 来处理。使用 Lua 脚本可以很方便地完成这一工作。
  • 使用 BITCOUNT 的 start 和 end 参数,每次只对所需的部分位进行计算,将位的累积工作(accumulating)放到客户端进行,并且对结果进行缓存 (caching)。

SET key value

1
SET key value [EX seconds] [PX milliseconds] [NX|XX]

开始版本 1.0.0.
时间复杂度: O(1)

设置一个key包含一个值。如果这个key已经拥有了值,不会管它是什么类型,它都将被覆盖。当SET操作成功后之前任何时候和这个key有关的东西都会被丢弃掉。

选项

从Redis 2.6.12版本开始 SET支持以下选项来修改它的行为:

  • EX seconds – 设置指定的过期时间,单位是秒。
  • PX milliseconds – 设置指定的过期时间,单位是毫秒。
  • NX – 只有这个key不存在的情况下才执行set操作。
  • XX – 只有这个key存在的情况下才执行set操作。

注意: 由于SET命令加上选项已经可以完全取代SETNX, SETEX, PSETEX的功能,所以在将来的版本中,redis可能会不推荐使用并且最终抛弃这几个命令。

返回值

如果SET正确执行了,会返回一个简单的应答:OK。如果SET操作没有执行就会返回一个Null的应答,因为用户指定了NX或者XX选项,但是条件不满足的。

例子

1
2
3
4
5
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis>

模式

注意:下面的模式不赞成使用,参考Redlock algorithm,这是一个更复杂的实现,但是提供了更好的保证和容错。
这个命令 SET resource-name anystring NX EX max-lock-time 是一种用 Redis 来实现锁机制的简单方法。

如果上述命令返回OK,那么客户端就可以获得锁(如果命令返回Nil可以在一段时间后重新尝试),并且可以通过DEL命令来释放锁。这个锁会在到达过期时间后自动释放。
采用下面的解锁模式可以让系统更健壮:

  • 不要设置固定的字符串,而是设置为随机的大字符串,称为令牌。
  • 不要通过DEL命令去直接释放锁,而是发送一个脚本:仅仅在这个key匹配的情况下才执行del。

这样就避免了锁到期后在尝试释放锁的时候删除了另一个客户端创建的锁。解锁脚本的例子如下:

1
2
3
4
5
6
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end

这个脚本可以这样调用:

1
EVAL ...script... 1 resource-name token-value

GET key

开始版本 1.0.0.
时间复杂度: O(1)

获取指定key的值。如果这个key不存在就会返回特定的值nil。如果这个key存储的值不是字符串类型就会返回一个错误,因为GET命令只处理字符串类型值。

返回值

返回一个字符串:如果key不存在就返回nil。

例子

1
2
3
4
5
6
7
redis> GET nonexisting
(nil)
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis>

STRLEN key

开始版本 2.2.0.
时间复杂度: O(1)

返回指定key存储的字符串值的长度。当key没有对应的字符串类型的值时返回一个错误。

返回值

Integer reply:key对应的字符串长度,如果key不存在返回0.

例子

1
2
3
4
5
6
7
redis> SET mykey "Hello world"
"OK"
redis> STRLEN mykey
(integer) 11
redis> STRLEN nonexisting
(integer) 0
redis>