Redis命令1

Redis命令

Redis 命令用于在 redis 服务上执行操作。

要在 redis 服务上执行命令需要一个 redis 客户端。Redis 客户端在我们之前下载的的 redis 的安装包中。

语法

Redis 客户端的基本语法为:

1
$ redis-cli

实例

以下实例讲解了如何启动 redis 客户端:

启动 redis 客户端,打开终端并输入命令 redis-cli。该命令会连接本地的 redis 服务。连接到本地的 redis 服务并执行 PING 命令,该命令用于检测 redis 服务是否启动。

1
2
3
4
$ redis-cli
127.0.0.1:6379>
127.0.0.1:6379> ping
PONG

在远程服务上执行命令

如果需要在远程 Redis 服务上执行命令,同样我们使用的也是 redis-cli 命令。和上面实例类似,上述命令执行的是默认参数,host127.0.0.1port6379, 无需密码

1
$ redis-cli -h host -p port -a password

实例

以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上。

1
2
3
4
$ redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
127.0.0.1:6379>
127.0.0.1:6379> ping
PONG

语法

1
$ redis-cli -h host -p port -a password

实例

以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上。

1
2
3
4
$ redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
127.0.0.1:6379>
127.0.0.1:6379> ping
PONG

Redis 键(key)相关命令

Redis 键命令用于管理 redis 的键。

语法

Redis 键命令的基本语法如下:

1
127.0.0.1:6379> COMMAND KEY_NAME

实例

1
2
3
4
5
$ # del 是一个命令, name 是一个键。如果键被删除成功,命令执行后输出 (integer) 1,否则将输出 (integer) 0
127.0.0.1:6379> set name redis
OK
127.0.0.1:6379> del name
(integer) 1

Redis键相关的基本命令

命令描述可用版本
del key该命令用于在 key 存在时删除 key。返回被删除 key 的数量>= 1.0.0
dump key该命令用于序列化给定 key 。如果 key 不存在,那么返回 nil 。 否则,返回序列化之后的值。>= 2.6.0
exists key该命令用于检查给定 key 是否存在。若 key 存在返回 1 ,否则返回 0 。>= 1.0.0
expire key seconds1. 该命令用于设置 key 的过期时间,key 过期后将不再可用。单位以秒计。设置成功返回 1 。 当 key 不存在或者不能为 key 设置过期时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的过期时间)返回 0 。在 Redis 2.1.3 之前的版本中,修改一个带有生存时间的 key 会导致整个 key 被删除,这一行为是受当时复制(replication)层的限制而作出的,现在这一限制已经被修复。
2. 在 Redis 2.4 版本中,过期时间的延迟在 1 秒钟之内 —— 也即是,就算 key 已经过期,但它还是可能在过期之后一秒钟之内被访问到,而在新的 Redis 2.6 版本中,延迟被降低到 1 毫秒之内。
>= 1.0.0
pexpire key milliseconds该命令和 expire 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间。>= 2.6.0
expireat key timestamp该命令用于以 UNIX 时间戳(unix timestamp)格式设置 key 的过期时间,单位以秒计。key 过期后将不再可用。设置成功返回 1 。当 key 不存在或者不能为 key 设置过期时间时返回 0 。>= 1.2.0
pexpireat key milliseconds-timestamp该命令和expireat命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳。>= 2.6.0
keys pattern1. 该命令用于查找所有符合给定模式 patternkey。时间复杂度:O(N), N 为数据库中 key 的数量。返回符合给定模式的 key 列表。
2. keys的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的 key ,你最好还是用 Redis 的集合结构(set)来代替。
>= 1.0.0
move key db1. 该命令用于将当前数据库的 key 移动到给定的数据库 db 当中。时间复杂度:O(1)。移动成功返回 1 ,失败则返回 0 。
2. 如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。因此,也可以利用这一特性,将 move当作锁(locking)原语(primitive)。
>= 1.0.0
persist key该命令用于移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。时间复杂度:O(1)。当生存时间移除成功时,返回 1 .如果 key 不存在或 key 没有设置生存时间,返回 0 。>= 2.2.0
ttl key1. 该命令以秒为单位返回 key 的剩余过期时间。当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间。时间复杂度:O(1)
2. 在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回 -1 。
>= 1.0.0
pttl key1. 该命令以毫秒为单位返回 key 的剩余过期时间。当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间。时间复杂度:O(1)
2. 在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回 -1 。
>= 2.6.0
randomkey该命令从当前数据库中随机返回(不删除)一个 key 。时间复杂度:O(1)。当数据库不为空时,返回一个 key 。当数据库为空时,返回 nil 。>= 1.0.0
rename key newkeykey 改名为 newkey 。时间复杂度:O(1)。改名成功时提示 OK ,当 keynewkey 相同,或者 key 不存在时,返回一个错误。当 newkey 已经存在时, raname命令将覆盖旧值。>= 1.0.0
renamenx key newkeykey 改名为 newkey 。时间复杂度:O(1)。当且仅当 newkey 不存在时,将 key 改名为 newkey 。修改成功时,返回 1 。当 newkey 存在时,返回 0 。当 key 不存在时,返回一个错误。>= 1.0.0
type key返回 key 所储存的值的类型。时间复杂度:O(1)。none (key不存在)string (字符串)list (列表)set (集合)zset (有序集)hash (哈希表)>= 1.0.0

Redis键相关的其他命令

sort

语法

sort key [by pattern] [limit offset count] [get pattern [get pattern ...]] [asc | desc] [alpha] [store destination]

返回或保存给定列表、集合、有序集合 key 中经过排序的元素。排序默认以数字作为对象,值被解释为双精度浮点数,然后进行比较。

一般 SORT 用法

最简单的 SORT 使用方法是 SORT key 和 SORT key DESC :

  1. sort key 返回键值从小到大排序的结果。
  2. sort key desc 返回键值从大到小排序的结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> lpush cost 20 18 35 9.3 18
(integer) 5
# 排序
127.0.0.1:6379> sort cost
1) "9.3"
2) "18"
3) "18"
4) "20"
5) "35"
# 逆序排序
127.0.0.1:6379> sort cost desc
1) "35"
2) "20"
3) "18"
4) "18"
5) "9.3"
127.0.0.1:6379>
使用外部 key 进行排序

可以使用外部 key 的数据作为权重,代替默认的直接对比键值的方式来进行排序。
假设现在有用户数据如下:

uiduser_name_{uid}user_level_{uid}
1admin9999
2jack10
3peter25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
127.0.0.1:6379> lpush uid 1
(integer) 1
127.0.0.1:6379> set user_name_1 admin
OK
127.0.0.1:6379> set user_level_1 9999
OK
127.0.0.1:6379> lpush uid 2
(integer) 2
127.0.0.1:6379> set user_name_2 jack
OK
127.0.0.1:6379> set user_level_2 10
OK
127.0.0.1:6379> lpush uid 3
(integer) 3
127.0.0.1:6379> set user_name_3 peter
OK
127.0.0.1:6379> set user_level_3 25
OK
127.0.0.1:6379> sort uid by user_level_*
1) "2"
2) "3"
3) "1"
127.0.0.1:6379>

默认情况下, SORT uid 直接按 uid 中的值排序,通过使用 BY 选项,可以让 uid 按其他键的元素来排序。

user_level_* 是一个占位符, 它先取出 uid 中的值, 然后再用这个值来查找相应的键。

比如在对 uid 列表进行排序时, 程序就会先取出 uid 的值 1 、 2 、 3 , 然后使用 user_level_1 、 user_level_2 、 user_level_3的值作为排序 uid 的权重。

get 选项

使用get选项, 可以根据排序的结果来取出相应的键值。

1
2
3
4
5
127.0.0.1:6379> sort uid get user_name_*
1) "admin"
2) "jack"
3) "peter"
127.0.0.1:6379>
组合使用 by 和 get

通过组合使用byget, 可以让排序结果以更直观的方式显示出来。

比如说, 以下代码先按 user_level_{uid} 来排序 uid 列表, 再取出相应的 user_name_{uid} 的值:

1
2
3
4
127.0.0.1:6379> sort uid by user_level_* get user_name_*
1) "jack"
2) "peter"
3) "mary"
获取多个外部键

可以同时使用多个get选项, 获取多个外部键的值。get有一个额外的参数规则——可以用 # 获取被排序键的值。

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> sort uid get # get user_level_* get user_name_*
1) "1"
2) "9999"
3) "admin"
4) "2"
5) "10"
6) "jack"
7) "3"
8) "25"
9) "peter"
127.0.0.1:6379>
获取外部键,但不进行排序

通过将一个不存在的键作为参数传给by选项, 可以让sort跳过排序操作, 直接返回结果:

1
2
3
4
127.0.0.1:6379> sort uid by not-exists-key
1) "3"
2) "2"
3) "1"

这种用法在单独使用时,没什么实际用处。
不过,通过将这种用法和get选项配合, 就可以在不排序的情况下, 获取多个外部键, 相当于执行一个整合的获取操作(类似于 SQL 数据库的 join 关键字)。

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> sort uid by not-exists-key get # get user_level_* get user_name_*
1) "3"
2) "25"
3) "peter"
4) "2"
5) "10"
6) "jack"
7) "1"
8) "9999"
9) "admin"
127.0.0.1:6379>
保存排序结果

默认情况下, sort操作只是简单地返回排序结果,并不进行任何保存操作。
通过给store选项指定一个 key 参数,可以将排序结果保存到给定的键上。
如果被指定的 key 已存在,那么原有的值将被排序结果覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
127.0.0.1:6379> rpush numbers 1 3 5 7 9
(integer) 5
127.0.0.1:6379> rpush numbers 2 4 6 8 10
(integer) 10
127.0.0.1:6379> lrange numbers 0 -1
1) "1"
2) "3"
3) "5"
4) "7"
5) "9"
6) "2"
7) "4"
8) "6"
9) "8"
10) "10"
127.0.0.1:6379> sort numbers store sorted-numbers
(integer) 10
127.0.0.1:6379> lrange sorted-numbers 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"
10) "10"
127.0.0.1:6379>

可以通过将sort命令的执行结果保存,并用expire为结果设置生存时间,以此来产生一个sort操作的结果缓存。

这样就可以避免对sort操作的频繁调用:只有当结果集过期时,才需要再调用一次sort操作。

另外,为了正确实现这一用法,你可能需要加锁以避免多个客户端同时进行缓存重建(也就是多个客户端,同一时间进行sort操作,并保存为结果集),具体参见setnx命令。

object

object subcommand [arguments [arguments]]

object命令允许从内部察看给定key的 Redis 对象。

它通常用在除错(debugging)或者了解为了节省空间而对 key 使用特殊编码的情况。
当将Redis用作缓存程序时,你也可以通过 OBJECT 命令中的信息,决定 key 的驱逐策略(eviction policies)。

可用版本:>= 2.2.3 ,时间复杂度:O(1)。

object 命令子命令
  1. object refcount key 返回给定 key 引用所储存的值的次数。此命令主要用于除错。
  2. object encoding key 返回给定 key 锁储存的值所使用的内部表示(representation)。
  3. object idletime key 返回给定 key 自储存以来的空转时间(idle, 没有被读取也没有被写入),以秒为单位。
对象编码方式:
  1. 字符串可以被编码为raw(一般字符串)、embstrint(用字符串表示64位数字是为了节约空间)。
  2. 列表可以被编码为ziplistlinkedlistziplist是为节约大小较小的列表空间而作的特殊表示。
  3. 集合可以被编码为intset或者hashtableintset是只储存数字的小集合的特殊表示。
  4. 哈希表可以编码为zipmap或者hashtablezipmap是小哈希表的特殊表示。
    有序集合可以被编码为 ziplist 或者 skiplist 格式。 ziplist 用于表示小的有序集合,而 skiplist 则用于表示任何大小的有序集合。
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
127.0.0.1:6379> set name "redis"
OK
127.0.0.1:6379> object refcount name
(integer) 1 # 只有一个引用
127.0.0.1:6379>
127.0.0.1:6379> object idletime name
(integer) 104 # 等待一阵,然后查看空转时间
127.0.0.1:6379> get name
"redis"
127.0.0.1:6379> object idletime name
(integer) 1 # get之后立即查空转时间
127.0.0.1:6379> object encoding name
"embstr" # 对象编码方式
127.0.0.1:6379> set phone 17608882643
OK
127.0.0.1:6379> object encoding phone
"int"
127.0.0.1:6379> set phone 9223372036854775808
OK # 大于或等于2的64次方,被编码为字符串
127.0.0.1:6379> object encoding phone
"embstr"
127.0.0.1:6379> set phone 111111111111111111111111111111111111111111111
OK # 超过44位之后以 raw 编码
127.0.0.1:6379> object encoding phone
"raw"

具体为什么是44而不是其他数字,原因可参考
为什么redis小等于39字节的字符串是embstr编码,大于39是raw编码? 44和39只是因为版本支持不同,github修改记录可参考commit

restore

restore key ttl serialized-value

反序列化给定的序列化值,并将它和给定的key关联。与dump相对应。可用版本:>= 2.6.0

参数ttl以毫秒为单位为key设置生存时间;如果ttl为 0 ,那么不设置生存时间。

restore在执行反序列化之前会先对序列化值的 RDB 版本和数据校验和进行检查,如果 RDB 版本不相同或者数据不完整的话,那么restore会拒绝进行反序列化,并返回一个错误。

时间复杂度
  1. 查找给定键的复杂度为 O(1) ,对键进行反序列化的复杂度为 O(N*M) ,其中 N 是构成 key 的 Redis 对象的数量,而 M 则是这些对象的平均大小。
  2. 有序集合(sorted set)的反序列化复杂度为 O(NMlog(N)) ,因为有序集合每次插入的复杂度为 O(log(N)) 。
  3. 如果反序列化的对象是比较小的字符串,那么复杂度为 O(1) 。
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> set hi "Hello, World!"
OK

127.0.0.1:6379> dump hi # 序列化
"\x00\rHello, World!\t\x00\xf0\"\x89\x13\xb4\x96\xbeR"

127.0.0.1:6379> restore hi 0 "\x00\rHello, World!\t\x00\xf0\"\x89\x13\xb4\x96\xbeR" # 反序列化时key已存在
(error) BUSYKEY Target key name already exists.

127.0.0.1:6379> restore hi-restore 0 "\x00\rHello, World!\t\x00\xf0\"\x89\x13\xb4\x96\xbeR" # 反序列化
OK

127.0.0.1:6379> restore fake-hi 0 "hello blablabla" # 使用错误的值进行反序列化
(error) ERR DUMP payload version or checksum are wrong

migrate

migrate host port key destination-db timeout [copy] [replace]

将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。可用版本:>= 2.6.0。迁移成功时返回 OK ,否则返回相应的错误。

这个命令是一个原子操作,它在执行的时候会阻塞进行迁移的两个实例,直到以下任意结果发生:迁移成功,迁移失败,等到超时。

可选项:
  1. copy :不移除源实例上的 key 。
  2. replace :替换目标实例上已存在的 key 。
内部实现

它在当前实例对给定 key 执行dump命令 ,将它序列化,然后传送到目标实例,目标实例再使用restore对数据进行反序列化,并将反序列化所得的数据添加到数据库中;当前实例就像目标实例的客户端那样,只要看到restore命令返回 OK ,它就会调用del删除自己数据库上的 key 。

timeout 参数以毫秒为格式,指定当前实例和目标实例进行沟通的最大间隔时间。这说明操作并不一定要在 timeout 毫秒内完成,只是说数据传送的时间不能超过这个 timeout 数。

migrate命令需要在给定的时间规定内完成 IO 操作。如果在传送数据时发生 IO 错误,或者达到了超时时间,那么命令会停止执行,并返回一个特殊的错误: IOERR 。

当 IOERR 出现时,有以下两种可能:

  1. key 可能存在于两个实例
  2. key 可能只存在于当前实例
    唯一不可能发生的情况就是丢失 key ,因此,如果一个客户端执行 MIGRATE 命令,并且不幸遇上 IOERR 错误,那么这个客户端唯一要做的就是检查自己数据库上的 key 是否已经被正确地删除。

如果有其他错误发生,那么 MIGRATE 保证 key 只会出现在当前实例中。(当然,目标实例的给定数据库上可能有和 key 同名的键,不过这和 MIGRATE 命令没有关系)。

时间复杂度:

这个命令在源实例上实际执行 DUMP 命令和 DEL 命令,在目标实例执行 RESTORE 命令,查看以上命令的文档可以看到详细的复杂度说明。
key 数据在两个实例之间传输的复杂度为 O(N) 。

实例
  1. 启动两个Redis实例,一个默认 6379 端口,一个 9999 接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ ./redis-server --port 9999
80619:C 01 Aug 2019 23:21:22.685 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
80619:C 01 Aug 2019 23:21:22.686 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=80619, just started
80619:C 01 Aug 2019 23:21:22.686 # Configuration loaded
80619:M 01 Aug 2019 23:21:22.687 * Increased maximum number of open files to 10032 (it was originally set to 256).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.5 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 9999
| `-._ `._ / _.-' | PID: 80619
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'

80619:M 01 Aug 2019 23:21:22.691 # Server initialized
80619:M 01 Aug 2019 23:21:22.692 * DB loaded from disk: 0.001 seconds
80619:M 01 Aug 2019 23:21:22.692 * Ready to accept connections
  1. 用客户端连上 6379 端口的实例,设置一个键,然后将它迁移到 7777 端口的实例上
1
2
3
4
5
6
7
8
9
10
$ ./redis-cli
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set hello "Hello from 6379 instance"
OK
127.0.0.1:6379> migrate 127.0.0.1 9999 hello 0 1000
OK
127.0.0.1:6379> exists hello
(integer) 0
$ # 迁移成功后 key 被删除
  1. 使用另一个客户端,查看 9999 端口上的实例
1
2
3
$ ./redis-cli -p 9999
127.0.0.1:9999> get hello
"Hello from 6379 instance"

scan

scan cursor [MATCH pattern] [COUNT count]

scan命令是一个基于游标的迭代器(cursor based iterator): scan命令每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数,以此来延续之前的迭代过程。当scan命令的游标参数被设置为 0 时,服务器将开始一次新的迭代,而当服务器向用户返回值为 0 的游标时,表示迭代已结束。

MATCH 选项

keys命令一样,增量式迭代命令也可以通过提供一个 glob 风格的模式参数,让命令只返回和给定模式相匹配的元素,这一点可以通过在执行增量式迭代命令时,通过给定 MATCH参数来实现。

TODO 不是很明白,回头继续

Redis 字符串(String)相关命令

Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下:

语法

1
127.0.0.1:6379> COMMAND KEY_NAME

实例

1
2
3
4
127.0.0.1:6379> set name redis
OK
127.0.0.1:6379> get name
"redis"

Redis字符串(String)相关的基本命令

命令描述时间复杂度可用版本
set key value1. 将字符串值 value 关联到 key 。如果 key 已经持有其他值, set就覆写旧值,无视类型。对于某个原本带有生存时间(TTL)的键来说, 当set命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
2. 在 Redis 2.6.12 以前版本, set 命令总是返回 OK 。从 Redis 2.6.12 版本开始,set 在设置操作成功完成时,才返回 OK 。如果设置了 NX 或者 XX ,但因为条件没达到而造成设置操作未执行,那么命令返回空批量回复(NULL Bulk Reply)。
3. 从 Redis 2.6.12 版本开始, set命令的行为可以通过一系列参数来修改:EXPXNXXXhttp://doc.redisfans.com/string/set.html
O(1)>= 1.0.0
get key返回 key 所关联的字符串值。如果 key 不存在那么返回特殊值 nil 。假如 key 储存的值不是字符串类型,返回一个错误,因为get只能用于处理字符串值。O(1)>= 1.0.0
getrange key start end返回 key 中字符串值的子字符串,字符串的截取范围由 startend 两个偏移量决定(包括 startend 在内)。负数偏移量表示从字符串最后开始计数, -1 表示最后一个字符, -2 表示倒数第二个,以此类推。getrange通过保证子字符串的值域(range)不超过实际字符串的值域来处理超出范围的值域请求。O(N), N 为要返回的字符串的长度。复杂度最终由字符串的返回值长度决定,但因为从已有字符串中取出子字符串的操作非常廉价(cheap),所以对于长度不大的字符串,该操作的复杂度也可看作O(1)。>= 2.4.0
getset key value将给定 key 的值设为 value ,并返回 key 的旧值(old value)。当 key 存在但不是字符串类型时,返回一个错误。key 不存在时,返回 nilO(1)>= 1.0.0
incr key1. 将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行incr操作,返回执行命令之后 key 的值。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
2. 本操作的值限制在 64 位(bit)有符号数字表示之内。这是一个针对字符串的操作,因为 Redis 没有专用的整数类型,所以 key 内储存的字符串被解释为十进制 64 位有符号整数来执行 incr 操作。
3. 限速器与计数器的应用。
4. incrby key incrementincrbyfloat key incrementdecr keydecrby key decrement命令与之类似。http://doc.redisfans.com/string/incrbyfloat.html
O(1)>= 1.0.0
getbit key offsetkey 所储存的字符串值,获取指定偏移量上的位(bit)。当 offset 比字符串值的长度大,或者 key 不存在时,返回 0O(1)>= 2.2.0
setbit key offset valuekey 所储存的字符串值,设置或清除指定偏移量上的位(bit)。位的设置或清除取决于 value 参数,可以是 0 也可以是 1 。当 key 不存在时,自动生成一个新的字符串值。字符串会进行伸展(grown)以确保它可以将 value 保存在指定的偏移量上。当字符串值进行伸展时,空白位置以 0 填充。
2. offset 参数必须大于或等于 0 ,小于 2^32 (bit 映射被限制在 512 MB 之内)。对使用大的 offsetsetbit操作来说,内存分配可能造成 Redis 服务器被阻塞。具体参考setrange命令,warning(警告)部分。
O(1)>= 2.2.0
strlen key返回 key 所储存的字符串值的长度。当 key 不存在时,返回 0 。当 key 储存的不是字符串值时,返回一个错误。O(1)>= 2.2.0
mget key [key ...]返回所有(一个或多个)给定 key 的值。如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。O(N) , N 为给定 key 的数量。>= 1.0.0
mset key value [key value ...]1. 同时设置一个或多个 key-value 对。如果某个给定 key 已经存在,那么mset会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用msetnx命令:它只会在所有给定 key 都不存在的情况下进行设置操作。
2. mset是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况,不可能发生。总是返回 OK (因为 MSET 不可能失败)
O(N), N 为要设置的 key 数量。>= 1.0.1
msetnx key value [key value ...]1. 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。即使只有一个给定 key 已存在, msetnx也会拒绝执行所有给定 key 的设置操作。当所有 key 都成功设置,返回 1 。如果所有给定 key 都设置失败(至少有一个 key 已经存在),那么返回 0 。
2. msetnx是原子性的,因此它可以用作设置多个不同 key 表示不同字段(field)的唯一性逻辑对象(unique logic object),所有字段要么全被设置,要么全不被设置。
O(N), N 为要设置的 key 数量。>= 1.0.1
append key value1. 如果 key 已经存在并且是一个字符串, append命令将 value 追加到 key 原来的值的末尾。如果 key 不存在, append就简单地将给定 key 设为 value ,就像执行 set key value 一样。
2. 返回追加 value 之后, key 中字符串的长度。
平摊O(1)>= 2.0.0

Redis字符串(String)相关的其他命令

bitcount

bitcount key [start] [end]

计算给定字符串中,被设置为 1 的比特位的数量。

一般情况下,给定的整个字符串都会被进行计数,通过指定额外的startend参数,可以让计数只在特定的位上进行。

startend参数的设置和getrange命令类似,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,以此类推。

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

可用版本:>= 2.6.0,时间复杂度:O(N)

示例
1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> bitcount bits
(integer) 0
127.0.0.1:6379> setbit bits 0 1
(integer) 0
127.0.0.1:6379> bitcount bits
(integer) 1
127.0.0.1:6379> setbit bits 3 1
(integer) 0
127.0.0.1:6379> bitcount bits
(integer) 2
127.0.0.1:6379>
使用 bitmap 实现用户上线次数统计

Bitmap 对于一些特定类型的计算非常有效。

假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户 A 上线了多少天,用户 B 上线了多少天,诸如此类,以此作为数据,从而决定让哪些用户参加 beta 测试等活动 —— 这个模式可以使用 SETBIT 和 BITCOUNT 来实现。

比如说,每当用户在某一天上线的时候,我们就使用 SETBIT ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的为设置为 1 。

举个例子,如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令 SETBIT peter 100 1 ;如果明天 peter 也继续阅览网站,那么执行命令 SETBIT peter 101 1 ,以此类推。

当要计算 peter 总共以来的上线次数时,就使用 BITCOUNT 命令:执行 BITCOUNT peter ,得出的结果就是 peter 上线的总天数。

更详细的实现可以参考博文(墙外) Fast, easy, realtime metrics using Redis bitmaps 。

性能
前面的上线次数统计例子,即使运行 10 年,占用的空间也只是每个用户 10*365 比特位(bit),也即是每个用户 456 字节。对于这种大小的数据来说, BITCOUNT 的处理速度就像 GET 和 INCR 这种 O(1) 复杂度的操作一样快。

如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:

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

更详细的实现可以参考博文 REDIS BITMAPS – FAST, EASY, REALTIME METRICS

bitop

bitop operation destkey key [key ...]

对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。

operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种:

  1. bitop add destkey key [key ...] ,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
  2. bitop or destkey key [key ...] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
  3. bitop xor destkey key [key ...] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
  4. bitop not destkey key ,对给定 key 求逻辑非,并将结果保存到 destkey 。
    除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。
处理不同长度的字符串

bitop处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0 。空的 key 也被看作是包含 0 的字符串序列。

可用版本:>= 2.6.0,时间复杂度:O(N),返回值:保存到 destkey 的字符串的长度,和输入 key 中最长的字符串长度相等。

bitop 的复杂度为 O(N) ,当处理大型矩阵(matrix)或者进行大数据量的统计时,最好将任务指派到附属节点(slave)进行,避免阻塞主节点。

示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> setbit bits-1 0 1 # 1001
(integer) 1
127.0.0.1:6379> setbit bits-1 3 1
(integer) 0
127.0.0.1:6379> setbit bits-2 0 1 # 1011
(integer) 0
127.0.0.1:6379> setbit bits-2 1 1
(integer) 0
127.0.0.1:6379> setbit bits-2 3 1
(integer) 1
127.0.0.1:6379> bitop and and-result bits-1 bits-2 # 1001
(integer) 1
127.0.0.1:6379> getbit and-result 2
(integer) 0
127.0.0.1:6379> getbit and-result 3
(integer) 1
127.0.0.1:6379>

setrange

setrange key offset value

用 value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始。可用版本:>= 2.2.0。返回被setrange修改之后,字符串的长度。

setrange命令会确保字符串足够长以便将 value 设置在指定的偏移量上,不存在的 key 当作空白字符串处理。如果给定 key 原来储存的字符串长度比偏移量小,那么原字符和偏移量之间的空白将用零字节(zerobytes, “\x00” )来填充。

注意

你能使用的最大偏移量是 2^29-1(536870911) ,因为 Redis 字符串的大小被限制在 512 兆(megabytes)以内。如果你需要使用比这更大的空间,你可以使用多个 key 。

时间复杂度:

对小(small)的字符串,平摊复杂度O(1)(关于什么字符串是”小”的,请参考 append命令)。否则为O(M), M 为 value 参数的长度。

实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ # 对非空字符串进行 setrange
127.0.0.1:6379> set hello "hello world"
OK
127.0.0.1:6379> setrange hello 6 "Redis"
(integer) 11
127.0.0.1:6379> get hello
"hello Redis"

$ # 对空字符串/不存在的 key 进行 setrange
127.0.0.1:6379> exists empty_string
(integer) 0
127.0.0.1:6379> setrange empty_string 5 "Redis!"
(integer) 11
127.0.0.1:6379> get empty_string
"\x00\x00\x00\x00\x00Redis!"
模式

因为有了setrangegetrange命令,你可以将 Redis 字符串用作具有O(1)随机访问时间的线性数组,这在很多真实用例中都是非常快速且高效的储存方式,具体请参考append命令的『模式:时间序列』部分。

append

append key value

append key value之后,返回 key 中字符串的长度。如果 key 已经存在并且是一个字符串,append命令将 value 追加到 key 原来的值的末尾。如果 key 不存在,append就简单地将给定 key 设为 value ,就像执行 set key value 一样。可用版本:>= 2.0.0。时间复杂度:平摊O(1)。

实例
1
2
3
4
5
6
7
127.0.0.1:6379> exists money
(integer) 0
127.0.0.1:6379> append money "five cent"
(integer) 9
127.0.0.1:6379> append money "one dollor"
(integer) 19
127.0.0.1:6379>
时间序列(Time series)

append可以为一系列定长(fixed-size)数据(sample)提供一种紧凑的表示方式,通常称之为时间序列。

每当一个新数据到达的时候,执行命令:append timeseries "fixed-size sample"

然后可以通过以下的方式访问时间序列的各项属性:

  1. strlen 给出时间序列中数据的数量
  2. getrange 可以用于随机访问。只要有相关的时间信息的话,我们就可以在 Redis 2.6 中使用 Lua 脚本和 GETRANGE 命令实现二分查找。
  3. setrange 可以用于覆盖或修改已存在的的时间序列。

这个模式的唯一缺陷是我们只能增长时间序列,而不能对时间序列进行缩短,因为 Redis 目前还没有对字符串进行修剪(tirm)的命令,但是,不管怎么说,这个模式的储存方式还是可以节省下大量的空间。

可以考虑使用 UNIX 时间戳作为时间序列的键名,这样一来,可以避免单个 key 因为保存过大的时间序列而占用大量内存,另一方面,也可以节省下大量命名空间。

set

set key value [EX seconds] [PX milliseconds] [NX|XX]

  1. 将字符串值 value 关联到 key 。如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
  2. 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
可选参数

从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改:

  1. EX second :设置键的过期时间为 second 秒。 set key value EX second 效果等同于 setex key second value
  2. PX millisecond :设置键的过期时间为 millisecond 毫秒。 set key value PX millisecond 效果等同于 psetex key millisecond value
  3. NX :只在键不存在时,才对键进行设置操作。 set key value NX 效果等同于 setnx key value
  4. XX :只在键已经存在时,才对键进行设置操作。
返回值
  1. 在 Redis 2.6.12 版本以前, SET 命令总是返回 OK 。
  2. 从 Redis 2.6.12 版本开始, SET 在设置操作成功完成时,才返回 OK 。如果设置了 NX 或者 XX ,但因为条件没达到而造成设置操作未执行,那么命令返回空批量回复(NULL Bulk Reply)。
使用模式

命令 set resource-name anystring NX EX max-lock-time 是一种在 Redis 中实现锁的简单方法。

客户端执行以上的命令:

  1. 如果服务器返回 OK ,那么这个客户端获得锁。
    如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。
  2. 设置的过期时间到达之后,锁将自动释放。

可以通过以下修改,让这个锁实现更健壮:

不使用固定的字符串作为键的值,而是设置一个不可猜测(non-guessable)的长随机字符串,作为口令串(token)。
不使用 DEL 命令来释放锁,而是发送一个 Lua 脚本,这个脚本只在客户端传入的值和键的口令串相匹配时,才对键进行删除。
这两个改动可以防止持有过期锁的客户端误删现有锁的情况出现。

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