原创作者:现实理想主义中的猿猴
花了不少时间从官网等等地方总结下来这些资料,包括自己实际场景中遇到过的应用场景
希望大家点点赞,收藏收藏,也是最我最大的支持了,感谢
Redis 作为内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它的 value 支持多种类型的数据结构,基本数据结构包含:字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)五种。这五种数据结构在我们工作中经常使用到,面试过程中经常被问到,因此熟练掌握这 5 种基本数据结构的使用和应用场景是 Redis 知识最基础也是最重要的部分。
字符串是 Redis 最简单的储存类型,它存储的值可以是字符串、整数或者浮点数,对整个字符串或者字符串的其中一部分执行操作;对整数或者浮点数执行自增(increment)或者自减(decrement)操作。
Redis 的字符串是一个由字节组成的序列,跟 java 里面的 ArrayList 有点类似,采用预分配冗余空间的方式来减少内存的频繁分配,内部为当前字符串实际分配的空间 capacity 一般要高于实际字符串长度 len。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。需要注意的是字符串最大长度为 512M。
字符串类型在工作中使用广泛,主要用于缓存数据,提高查询性能。比如存储登录用户信息、电商中存储商品信息、可以做计数器(想知道什么时候封锁一个 IP 地址(访问超过几次))等等。
命令格式:
- SET key value [EX seconds] [PX milliseconds] [NX|XX]
可用版本:
- >= 1.0.0
时间复杂度:
- O(1)
将字符串值 value
关联到 key
。
如果 key
已经持有其他值, SET
就覆写旧值, 无视类型。
当 SET
命令对一个带有生存时间(TTL)的键进行设置之后, 该键原有的 TTL 将被清除。
该SET
命令支持一组修改其行为的选项:
EX seconds : 将键的过期时间设置为 seconds 秒。 执行 SET key value EX seconds 的效果等同于执行 SETEX key seconds value 。
PX milliseconds : 将键的过期时间设置为 milliseconds 毫秒。 执行 SET key value PX milliseconds
的效果等同于执行 PSETEX key milliseconds value
。
NX : 只在键不存在时, 才对键进行设置操作。 执行 SET key value NX 的效果等同于执行 SETNX key value 。
XX : 只在键已经存在时, 才对键进行设置操作。
Note:因为 SET
命令可以通过参数来实现 SETNX
、 SETEX
以及 PSETEX
命令的效果, 所以 Redis 将来的版本可能会移除并废弃 SETNX
、 SETEX
和 PSETEX
这三个命令。
SET
命令总是返回 OK
。SET
命令只在设置操作成功完成时才返回 OK
; 如果命令使用了 NX
或者 XX
选项, 但是因为条件没达到而造成设置操作未执行, 那么命令将返回空批量回复(NULL Bulk Reply)。对不存在的键进行设置:
redis> SET key "value"
OK
redis> GET key
"value"
对已存在的键进行设置:
redis> SET key "new-value"
OK
redis> GET key
"new-value"
使用 EX
选项:
redis> SET key-with-expire-time "hello" EX 10086
OK
redis> GET key-with-expire-time
"hello"
redis> TTL key-with-expire-time
(integer) 10069
使用 PX
选项:
redis> SET key-with-pexpire-time "moto" PX 123321
OK
redis> GET key-with-pexpire-time
"moto"
redis> PTTL key-with-pexpire-time
(integer) 111939
使用 NX
选项:
redis> SET not-exists-key "value" NX
OK # 键不存在,设置成功
redis> GET not-exists-key
"value"
redis> SET not-exists-key "new-value" NX
(nil) # 键已经存在,设置失败
redis> GEt not-exists-key
"value" # 维持原值不变
使用 XX
选项:
redis> EXISTS exists-key
(integer) 0
redis> SET exists-key "value" XX
(nil) # 因为键不存在,设置失败
redis> SET exists-key "value"
OK # 先给键设置一个值
redis> SET exists-key "new-value" XX
OK # 设置新值成功
redis> GET exists-key
"new-value"
命令格式:
- GET key
可用版本:
- >= 1.0.0
时间复杂度: O(1)
返回与键 key
相关联的字符串值。
如果键 key
不存在, 那么返回特殊值 nil
; 否则, 返回键 key
的值。
如果键 key
的值并非字符串类型, 那么返回一个错误, 因为 GET
命令只能用于字符串值。
对不存在的键 key
或是字符串类型的键 key
执行 GET
命令:
redis> GET db
(nil)
redis> SET db redis
OK
redis> GET db
"redis"
对不是字符串类型的键 key
执行 GET
命令:
redis> DEL db
(integer) 1
redis> LPUSH db redis mongodb mysql
(integer) 3
redis> GET db
(error) ERR Operation against a key holding the wrong kind of value
- 命令格式:
- DECR key
- 可用版本:
- >= 1.0.0
- 时间复杂度:
- O(1)
为键 key
储存的数字值减去一。
如果键 key
不存在, 那么键 key
的值会先被初始化为 0
, 然后再执行 DECR
操作。
如果键 key
储存的值不能被解释为数字, 那么 DECR
命令将返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。
关于递增(increment) / 递减(decrement)操作的更多信息, 请参见 INCR
命令的文档。
DECR
命令会返回键 key
在执行减一操作之后的值。
对储存数字值的键 key
执行 DECR
命令:
redis> SET failure_times 10
OK
redis> DECR failure_times
(integer) 9
对不存在的键执行 DECR
命令:
redis> EXISTS count
(integer) 0
redis> DECR count
(integer) -1
- 命令格式:
- INCR key
- 可用版本:
- >= 1.0.0
- 时间复杂度:
- O(1)
为键 key
储存的数字值加上一。
如果键 key
不存在, 那么它的值会先被初始化为 0
, 然后再执行 INCR
命令。
如果键 key
储存的值不能被解释为数字, 那么 INCR
命令将返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。
Note:INCR
命令是一个针对字符串的操作。 因为 Redis 并没有专用的整数类型, 所以键 key
储存的值在执行 INCR
命令时会被解释为十进制 64 位有符号整数。
INCR
命令会返回键 key
在执行加一操作之后的值。
redis> SET page_view 20
OK
redis> INCR page_view
(integer) 21
redis> GET page_view # 数字值在 Redis 中以字符串的形式保存
"21"
- 命令格式:
- MGET key [key …]
- 可用版本:
- >= 1.0.0
- 时间复杂度:
- O(N) ,其中 N 为给定键的数量。
返回给定的一个或多个字符串键的值。
如果给定的字符串键里面, 有某个键不存在, 那么这个键的值将以特殊值 nil
表示。
MGET
命令将返回一个列表, 列表中包含了所有给定键的值。
redis> SET redis redis.com
OK
redis> SET mongodb mongodb.org
OK
redis> MGET redis mongodb
1) "redis.com"
2) "mongodb.org"
redis> MGET redis mongodb mysql # 不存在的 mysql 返回 nil
1) "redis.com"
2) "mongodb.org"
3) (nil)
- 命令格式:
- MSET key value [key value …]
- 可用版本:
- >= 1.0.1
- 时间复杂度:
- O(N),其中 N 为被设置的键数量。
同时为多个键设置值。
如果某个给定键已经存在, 那么 MSET
将使用新值去覆盖旧值, 如果这不是你所希望的效果, 请考虑使用 MSETNX
命令, 这个命令只会在所有给定键都不存在的情况下进行设置。
MSET
是一个原子性(atomic)操作, 所有给定键都会在同一时间内被设置, 不会出现某些键被设置了但是另一些键没有被设置的情况。
MSET
命令总是返回 OK
。
同时对多个键进行设置:
redis> MSET date "2012.3.30" time "11:00 a.m." weather "sunny"
OK
redis> MGET date time weather
1) "2012.3.30"
2) "11:00 a.m."
3) "sunny"
覆盖已有的值:
redis> MGET k1 k2
1) "hello"
2) "world"
redis> MSET k1 "good" k2 "bye"
OK
redis> MGET k1 k2
1) "good"
2) "bye"
- 命令格式:
- DECRBY key decrement
- 可用版本:
- >= 1.0.0
- 时间复杂度:
- O(1)
将键 key
储存的整数值减去减量 decrement
。
如果键 key
不存在, 那么键 key
的值会先被初始化为 0
, 然后再执行 DECRBY
命令。
如果键 key
储存的值不能被解释为数字, 那么 DECRBY
命令将返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。
关于更多递增(increment) / 递减(decrement)操作的更多信息, 请参见 INCR
命令的文档。
DECRBY
命令会返回键在执行减法操作之后的值。
对已经存在的键执行 DECRBY
命令:
redis> SET count 100
OK
redis> DECRBY count 20
(integer) 80
对不存在的键执行 DECRBY
命令:
redis> EXISTS pages
(integer) 0
redis> DECRBY pages 10
(integer) -10
- 命令格式:
- INCRBY key increment
- 可用版本:
- >= 1.0.0
- 时间复杂度:
- O(1)
为键 key
储存的数字值加上增量 increment
。
如果键 key
不存在, 那么键 key
的值会先被初始化为 0
, 然后再执行 INCRBY
命令。
如果键 key
储存的值不能被解释为数字, 那么 INCRBY
命令将返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。
关于递增(increment) / 递减(decrement)操作的更多信息, 请参见 INCR
命令的文档。
在加上增量 increment
之后, 键 key
当前的值。
键存在,并且值为数字:
redis> SET rank 50
OK
redis> INCRBY rank 20
(integer) 70
redis> GET rank
"70"
键不存在:
redis> EXISTS counter
(integer) 0
redis> INCRBY counter 30
(integer) 30
redis> GET counter
"30"
键存在,但值无法被解释为数字:
redis> SET book "long long ago..."
OK
redis> INCRBY book 200
(error) ERR value is not an integer or out of range
- 命令格式:
- STRLEN key
- 可用版本:
- >= 2.2.0
- 复杂度:
- O(1)
返回键 key
储存的字符串值的长度。
STRLEN
命令返回字符串值的长度。
当键 key
不存在时, 命令返回 0
。
当 key
储存的不是字符串值时, 返回一个错误。
获取字符串值的长度:
redis> SET mykey "Hello world"
OK
redis> STRLEN mykey
(integer) 11
不存在的键的长度为 0
:
redis> STRLEN nonexisting
(integer) 0
- 命令格式:
- INCRBYFLOAT key increment
- 可用版本:
- >= 2.6.0
- 时间复杂度:
- O(1)
为键 key
储存的值加上浮点数增量 increment
。
如果键 key
不存在, 那么 INCRBYFLOAT
会先将键 key
的值设为 0
, 然后再执行加法操作。
如果命令执行成功, 那么键 key
的值会被更新为执行加法计算之后的新值, 并且新值会以字符串的形式返回给调用者。
无论是键 key
的值还是增量 increment
, 都可以使用像 2.0e7
、 3e5
、 90e-2
那样的指数符号(exponential notation)来表示, 但是, ==执行 INCRBYFLOAT 命令之后的值==总是以同样的形式储存, 也即是, 它们总是由一个数字, 一个(可选的)小数点和一个任意长度的小数部分组成(比如 3.14
、 69.768
,诸如此类), 小数部分尾随的 0
会被移除, 如果可能的话, 命令还会将浮点数转换为整数(比如 3.0
会被保存成 3
)。
此外, 无论加法计算所得的浮点数的实际精度有多长, INCRBYFLOAT
命令的计算结果最多只保留小数点的后十七位。
当以下任意一个条件发生时, 命令返回一个错误:
key
的值不是字符串类型(因为 Redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型);key
当前的值或者给定的增量 increment
不能被解释(parse)为双精度浮点数。在加上增量 increment
之后, 键 key
的值。
redis> GET decimal
"3.0"
redis> INCRBYFLOAT decimal 2.56
"5.56"
redis> GET decimal
"5.56"
散列相当于 Java 中的 HashMap,内部是无序字典。实现原理跟 HashMap 一致。一个哈希表有多个节点,每个节点保存一个键值对。
与 Java 中的 HashMap 不同的是, rehash 的方式不一样,因为 Java 的 HashMap 在字典很大时,rehash 是个耗时的操作,需要一次性全部 rehash。 Redis 为了高性能,不能堵塞服务,所以采用了渐进式 rehash 策略。
渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 结构,查询时会同时查询两个 hash 结构,然后在后续的定时任务中以及 hash 操作指令中,循序渐进地将旧 hash 的内容一点点迁移到新的 hash 结构中。当搬迁完成了,就会使用新的 hash 结构取而代之。
当 hash 移除了最后一个元素之后,该数据结构自动被删除,内存被回收。
Hash 也可以同于对象存储,**比如存储用户信息,与字符串不一样的是,字符串是需要将对象进行序列化(比如 json 序列化)之后才能保存,而 Hash 则可以讲用户对象的每个字段单独存储,这样就能节省序列化和反序列的时间。**如下:
此外还可以保存用户的购买记录,比如 key 为用户 id, field 为商品 id, value 为商品数量。同样还可以用于购物车数据的存储,比如 key 为用户 id, field 为商品 id, value 为购买数量等等。
命令格式:
- HDEL key field [field …]
可用版本:
>= 2.0.0
时间复杂度:
O(N),
N
为要删除的域的数量。
删除哈希表 key
中的一个或多个指定域,不存在的域将被忽略。
Note:在Redis2.4以下的版本里, HDEL 每次只能删除单个域,如果你需要在一个原子时间内删除多个域,请将命令包含在 MULTI / EXEC 块内。
被成功移除的域的数量,不包括被忽略的域。
# 测试数据
redis> HGETALL abbr
1) "a"
2) "apple"
3) "b"
4) "banana"
5) "c"
6) "cat"
7) "d"
8) "dog"
# 删除单个域
redis> HDEL abbr a
(integer) 1
# 删除不存在的域
redis> HDEL abbr not-exists-field
(integer) 0
# 删除多个域
redis> HDEL abbr b c
(integer) 2
redis> HGETALL abbr
1) "d"
2) "dog"
命令格式:HGET hash field
可用版本: >= 2.0.0
时间复杂度: O(1)
返回哈希表中给定域的值。
HGET
命令在默认情况下返回给定域的值。
如果给定域不存在于哈希表中, 又或者给定的哈希表并不存在, 那么命令返回 nil
。
域存在的情况:
redis> HSET homepage redis redis.com
(integer) 1
redis> HGET homepage redis
"redis.com"
域不存在的情况:
redis> HGET site mysql
(nil)
命令格式:HLEN key
时间复杂度:O(1)
返回哈希表 key
中域的数量。
哈希表中域的数量。
当 key
不存在时,返回 0
。
redis> HSET db redis redis.com
(integer) 1
redis> HSET db mysql mysql.com
(integer) 1
redis> HLEN db
(integer) 2
redis> HSET db mongodb mongodb.org
(integer) 1
redis> HLEN db
(integer) 3
命令格式:HSET hash field value
可用版本: >= 2.0.0
时间复杂度: O(1)
将哈希表 hash
中域 field
的值设置为 value
。
如果给定的哈希表并不存在, 那么一个新的哈希表将被创建并执行 HSET
操作。
如果域 field
已经存在于哈希表中, 那么它的旧值将被新值 value
覆盖。
当 HSET
命令在哈希表中新创建 field
域并成功为它设置值时, 命令返回 1
; 如果域 field
已经存在于哈希表, 并且 HSET
命令成功使用新值覆盖了它的旧值, 那么命令返回 0
。
设置一个新域:
redis> HSET website google "www.g.cn"
(integer) 1
redis> HGET website google
"www.g.cn"
对一个已存在的域进行更新:
redis> HSET website google "www.google.com"
(integer) 0
redis> HGET website google
"www.google.com"
命令格式:HKEYS key
可用版本:>= 2.0.0
**时间复杂度:**O(N),
N
为哈希表的大小。
返回哈希表 key
中的所有域。
一个包含哈希表中所有域的表。
当 key
不存在时,返回一个空表。
# 哈希表非空
redis> HMSET website google www.google.com yahoo www.yahoo.com
OK
redis> HKEYS website
1) "google"
2) "yahoo"
# 空哈希表/key不存在
redis> EXISTS fake_key
(integer) 0
redis> HKEYS fake_key
(empty list or set)
命令格式:HMGET key field [field …]
可用版本:>= 2.0.0
时间复杂度:O(N), N 为给定域的数量。
返回哈希表 key
中,一个或多个给定域的值。
如果给定的域不存在于哈希表,那么返回一个 nil
值。
因为不存在的 key
被当作一个空哈希表来处理,所以对一个不存在的 key
进行 HMGET 操作将返回一个只带有 nil
值的表。
一个包含多个给定域的关联值的表,表值的排列顺序和给定域参数的请求顺序一样。
redis> HMSET pet dog "doudou" cat "nounou" # 一次设置多个域
OK
redis> HMGET pet dog cat fake_pet # 返回值的顺序和传入参数的顺序一样
1) "doudou"
2) "nounou"
3) (nil) # 不存在的域返回nil值
命令格式:HMSET key field value [field value …]
可用版本:>= 2.0.0
时间复杂度:O(N), N 为 field-value 对的数量。
如果命令执行成功,返回 OK
。
当 key
不是哈希表(hash)类型时,返回一个错误。
redis> HMSET website google www.google.com yahoo www.yahoo.com
OK
redis> HGET website google
"www.google.com"
redis> HGET website yahoo
"www.yahoo.com"
命令格式:HSCAN key cursor [MATCH pattern] [COUNT count]
可用版本:>= 2.8.0
时间复杂度:每次调用 O(1)。O(N) 用于完整的迭代,包括足够的命令调用以使光标返回 0。N 是集合内的元素数。
具体信息请参考 SCAN cursor [MATCH pattern] [COUNT count] 命令。
命令格式:HVALS key
可用版本:>= 2.0.0
**时间复杂度:**O(N),
N
为哈希表的大小。
返回哈希表 key
中所有域的值。
一个包含哈希表中所有值的表。
当 key
不存在时,返回一个空表。
# 非空哈希表
redis> HMSET website google www.google.com yahoo www.yahoo.com
OK
redis> HVALS website
1) "www.google.com"
2) "www.yahoo.com"
# 空哈希表/不存在的key
redis> EXISTS not_exists
(integer) 0
redis> HVALS not_exists
(empty list or set)
命令格式:HEXISTS hash field
可用版本: >= 2.0.0
时间复杂度: O(1)
检查给定域 field
是否存在于哈希表 hash
当中。
HEXISTS
命令在给定域存在时返回 1
, 在给定域不存在时返回 0
。
给定域不存在:
redis> HEXISTS phone myphone
(integer) 0
给定域存在:
redis> HSET phone myphone nokia-1110
(integer) 1
redis> HEXISTS phone myphone
(integer) 1
命令格式:HGETALL key
可用版本:>= 2.0.0
**时间复杂度:**O(N),
N
为哈希表的大小。
返回哈希表 key
中,所有的域和值。
在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
以列表形式返回哈希表的域和域的值。
若 key
不存在,返回空列表。
redis> HSET people jack "Jack Sparrow"
(integer) 1
redis> HSET people gump "Forrest Gump"
(integer) 1
redis> HGETALL people
1) "jack" # 域
2) "Jack Sparrow" # 值
3) "gump"
4) "Forrest Gump"
命令格式:HINCRBY key field increment
可用版本:>= 2.0.0
**时间复杂度:**O(1)
为哈希表 key
中的域 field
的值加上增量 increment
。
增量也可以为负数,相当于对给定域进行减法操作。
如果 key
不存在,一个新的哈希表被创建并执行 HINCRBY 命令。
如果域 field
不存在,那么在执行命令前,域的值被初始化为 0
。
对一个储存字符串值的域 field
执行 HINCRBY 命令将造成一个错误。
本操作的值被限制在 64 位(bit)有符号数字表示之内。
执行 HINCRBY 命令之后,哈希表 key
中域 field
的值。
# increment 为正数
redis> HEXISTS counter page_view # 对空域进行设置
(integer) 0
redis> HINCRBY counter page_view 200
(integer) 200
redis> HGET counter page_view
"200"
# increment 为负数
redis> HGET counter page_view
"200"
redis> HINCRBY counter page_view -50
(integer) 150
redis> HGET counter page_view
"150"
# 尝试对字符串值的域执行HINCRBY命令
redis> HSET myhash string hello,world # 设定一个字符串值
(integer) 1
redis> HGET myhash string
"hello,world"
redis> HINCRBY myhash string 1 # 命令执行失败,错误。
(error) ERR hash value is not an integer
redis> HGET myhash string # 原值不变
"hello,world"
命令格式:HSTRLEN key field
可用版本:>= 3.2.0
**时间复杂度:**O(1)
返回哈希表 key
中, 与给定域 field
相关联的值的字符串长度(string length)。
如果给定的键或者域不存在, 那么命令返回 0
。
一个整数。
redis> HMSET myhash f1 "HelloWorld" f2 "99" f3 "-256"
OK
redis> HSTRLEN myhash f1
(integer) 10
redis> HSTRLEN myhash f2
(integer) 2
redis> HSTRLEN myhash f3
(integer) 4
命令格式:HINCRBYFLOAT key field increment
可用版本:>= 2.6.0
**时间复杂度:**O(1)
为哈希表 key
中的域 field
加上浮点数增量 increment
。
如果哈希表中没有域 field
,那么 HINCRBYFLOAT 会先将域 field
的值设为 0
,然后再执行加法操作。
如果键 key
不存在,那么 HINCRBYFLOAT 会先创建一个哈希表,再创建域 field
,最后再执行加法操作。
当以下任意一个条件发生时,返回一个错误:
field
的值不是字符串类型(因为 redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型)field
当前的值或给定的增量 increment
不能解释(parse)为双精度浮点数(double precision floating point number)HINCRBYFLOAT 命令的详细功能和 INCRBYFLOAT key increment 命令类似,请查看 INCRBYFLOAT key increment 命令获取更多相关信息。
执行加法操作之后 field
域的值。
# 值和增量都是普通小数
redis> HSET mykey field 10.50
(integer) 1
redis> HINCRBYFLOAT mykey field 0.1
"10.6"
# 值和增量都是指数符号
redis> HSET mykey field 5.0e3
(integer) 0
redis> HINCRBYFLOAT mykey field 2.0e2
"5200"
# 对不存在的键执行 HINCRBYFLOAT
redis> EXISTS price
(integer) 0
redis> HINCRBYFLOAT price milk 3.5
"3.5"
redis> HGETALL price
1) "milk"
2) "3.5"
# 对不存在的域进行 HINCRBYFLOAT
redis> HGETALL price
1) "milk"
2) "3.5"
redis> HINCRBYFLOAT price coffee 4.5 # 新增 coffee 域
"4.5"
redis> HGETALL price
1) "milk"
2) "3.5"
3) "coffee"
4) "4.5"
Redis 中的 lists 相当于 Java 中的 LinkedList, 实现原理是一个双向链表(其底层是一个快速列表), 即可以支持反向查找和遍历,更方便操作。插入和删除操作非常快,时间复杂度为 O(1), 但是索引定位很慢,时间复杂度为 O(n)。
lists 的应用场景非常多,可以利用它轻松实现热销榜;可以实现工作队列(利用 lists 的 push 操作,将任务存在 lists 中,然后工作线程再用 pop 操作将任务取出进行执行); 可以实现最新列表,比如最新评论等。
命令格式:LLEN key
可用版本: >= 1.0.0
时间复杂度: O(1)
返回列表 key
的长度。
如果 key
不存在,则 key
被解释为一个空列表,返回 0
.
如果 key
不是列表类型,返回一个错误。
列表 key
的长度。
# 空列表
redis> LLEN job
(integer) 0
# 非空列表
redis> LPUSH job "cook food"
(integer) 1
redis> LPUSH job "have lunch"
(integer) 2
redis> LLEN job
(integer) 2
命令格式:LLEN key
可用版本: >= 1.0.0
时间复杂度: O(1)
移除并返回列表 key
的头元素。
列表的头元素。 当 key
不存在时,返回 nil
。
redis> LLEN course
(integer) 0
redis> RPUSH course algorithm001
(integer) 1
redis> RPUSH course c++101
(integer) 2
redis> LPOP course # 移除头元素
"algorithm001"
命令格式:LREM key count value
可用版本: >= 1.0.0
时间复杂度: O(N),
N
为列表的长度。
根据参数 count
的值,移除列表中与参数 value
相等的元素。
count
的值可以是以下几种:
count > 0
: 从表头开始向表尾搜索,移除与 value
相等的元素,数量为 count
。count < 0
: 从表尾开始向表头搜索,移除与 value
相等的元素,数量为 count
的绝对值。count = 0
: 移除表中所有与 value
相等的值。被移除元素的数量。 因为不存在的 key
被视作空表(empty list),所以当 key
不存在时, LREM 命令总是返回 0
。
# 先创建一个表,内容排列是
# morning hello morning helllo morning
redis> LPUSH greet "morning"
(integer) 1
redis> LPUSH greet "hello"
(integer) 2
redis> LPUSH greet "morning"
(integer) 3
redis> LPUSH greet "hello"
(integer) 4
redis> LPUSH greet "morning"
(integer) 5
redis> LRANGE greet 0 4 # 查看所有元素
1) "morning"
2) "hello"
3) "morning"
4) "hello"
5) "morning"
redis> LREM greet 2 morning # 移除从表头到表尾,最先发现的两个 morning
(integer) 2 # 两个元素被移除
redis> LLEN greet # 还剩 3 个元素
(integer) 3
redis> LRANGE greet 0 2
1) "hello"
2) "hello"
3) "morning"
redis> LREM greet -1 morning # 移除从表尾到表头,第一个 morning
(integer) 1
redis> LLEN greet # 剩下两个元素
(integer) 2
redis> LRANGE greet 0 1
1) "hello"
2) "hello"
redis> LREM greet 0 hello # 移除表中所有 hello
(integer) 2 # 两个 hello 被移除
redis> LLEN greet
(integer) 0
命令格式:LSET key index value
可用版本: >= 1.0.0
时间复杂度:对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。其他情况下,为 O(N),
N
为列表的长度。
将列表 key
下标为 index
的元素的值设置为 value
。
当 index
参数超出范围,或对一个空列表( key
不存在)进行 LSET 时,返回一个错误。
关于列表下标的更多信息,请参考 LINDEX key index 命令。
操作成功返回 ok
,否则返回错误信息。
# 对空列表(key 不存在)进行 LSET
redis> EXISTS list
(integer) 0
redis> LSET list 0 item
(error) ERR no such key
# 对非空列表进行 LSET
redis> LPUSH job "cook food"
(integer) 1
redis> LRANGE job 0 0
1) "cook food"
redis> LSET job 0 "play game"
OK
redis> LRANGE job 0 0
1) "play game"
# index 超出范围
redis> LLEN list # 列表长度为 1
(integer) 1
redis> LSET list 3 'out of range'
(error) ERR index out of range
命令格式:RPOP key
可用版本: >= 1.0.0
时间复杂度: O(1)
移除并返回列表 key
的尾元素。
列表的尾元素。 当 key
不存在时,返回 nil
。
redis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> RPOP mylist # 返回被弹出的元素
"three"
redis> LRANGE mylist 0 -1 # 列表剩下的元素
1) "one"
2) "two"
命令格式:BLPOP key [key …] timeout
可用版本: >= 2.0.0
时间复杂度: O(1)
BLPOP 是列表的阻塞式(blocking)弹出原语。
它是 LPOP key 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。
当给定多个 key
参数时,按参数 key
的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。
当 BLPOP 被调用时,如果给定 key
内至少有一个非空列表,那么弹出遇到的第一个非空列表的头元素,并和被弹出元素所属的列表的名字一起,组成结果返回给调用者。
当存在多个给定 key
时, BLPOP 按给定 key
参数排列的先后顺序,依次检查各个列表。
假设现在有 job
、 command
和 request
三个列表,其中 job
不存在, command
和 request
都持有非空列表。考虑以下命令:
BLPOP job command request 0
BLPOP 保证返回的元素来自 command
,因为它是按”查找 job
-> 查找 command
-> 查找 request
“这样的顺序,第一个找到的非空列表。
redis> DEL job command request # 确保key都被删除
(integer) 0
redis> LPUSH command "update system..." # 为command列表增加一个值
(integer) 1
redis> LPUSH request "visit page" # 为request列表增加一个值
(integer) 1
redis> BLPOP job command request 0 # job 列表为空,被跳过,紧接着 command 列表的第一个元素被弹出。
1) "command" # 弹出元素所属的列表
2) "update system..." # 弹出元素所属的值
如果所有给定 key
都不存在或包含空列表,那么 BLPOP 命令将阻塞连接,直到等待超时,或有另一个客户端对给定 key
的任意一个执行 LPUSH key value value … 或 RPUSH key value value … 命令为止。
超时参数 timeout
接受一个以秒为单位的数字作为值。超时参数设为 0
表示阻塞时间可以无限期延长(block indefinitely) 。
redis> EXISTS job # 确保两个 key 都不存在
(integer) 0
redis> EXISTS command
(integer) 0
redis> BLPOP job command 300 # 因为key一开始不存在,所以操作会被阻塞,直到另一客户端对 job 或者 command 列表进行 PUSH 操作。
1) "job" # 这里被 push 的是 job
2) "do my home work" # 被弹出的值
(26.26s) # 等待的秒数
redis> BLPOP job command 5 # 等待超时的情况
(nil)
(5.66s) # 等待的秒数
相同的 key
可以被多个客户端同时阻塞。
不同的客户端被放进一个队列中,按『先阻塞先服务』(first-BLPOP,first-served)的顺序为 key
执行 BLPOP 命令。
BLPOP 可以用于流水线(pipline,批量地发送多个命令并读入多个回复),但把它用在 MULTI / EXEC 块当中没有意义。因为这要求整个服务器被阻塞以保证块执行时的原子性,该行为阻止了其他客户端执行 LPUSH key value value … 或 RPUSH key value value … 命令。
因此,一个被包裹在 MULTI / EXEC 块内的 BLPOP 命令,行为表现得就像 LPOP key 一样,对空列表返回 nil
,对非空列表弹出列表元素,不进行任何阻塞操作。
# 对非空列表进行操作
redis> RPUSH job programming
(integer) 1
redis> MULTI
OK
redis> BLPOP job 30
QUEUED
redis> EXEC # 不阻塞,立即返回
1) 1) "job"
2) "programming"
# 对空列表进行操作
redis> LLEN job # 空列表
(integer) 0
redis> MULTI
OK
redis> BLPOP job 30
QUEUED
redis> EXEC # 不阻塞,立即返回
1) (nil)
如果列表为空,返回一个 nil
。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key
,第二个元素是被弹出元素的值。
有时候,为了等待一个新元素到达数据中,需要使用轮询的方式对数据进行探查。
另一种更好的方式是,使用系统提供的阻塞原语,在新元素到达时立即进行处理,而新元素还没到达时,就一直阻塞住,避免轮询占用资源。
对于 Redis ,我们似乎需要一个阻塞版的 SPOP key 命令,但实际上,使用 BLPOP 或者 BRPOP key [key …] timeout 就能很好地解决这个问题。
使用元素的客户端(消费者)可以执行类似以下的代码:
LOOP forever
WHILE SPOP(key) returns elements
... process elements ...
END
BRPOP helper_key
END
添加元素的客户端(生产者)则执行以下代码:
MULTI
SADD key element
LPUSH helper_key x
EXEC
命令格式:BRPOP key [key …] timeout
可用版本: >= 2.0.0
时间复杂度: O(1)
BRPOP 是列表的阻塞式(blocking)弹出原语。
它是 RPOP key 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。
当给定多个 key
参数时,按参数 key
的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。
关于阻塞操作的更多信息,请查看 BLPOP key key …] timeout 命令, BRPOP 除了弹出元素的位置和 [BLPOP key [key …] timeout 不同之外,其他表现一致。
假如在指定时间内没有任何元素被弹出,则返回一个 nil
和等待时长。 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key
,第二个元素是被弹出元素的值。
redis> LLEN course
(integer) 0
redis> RPUSH course algorithm001
(integer) 1
redis> RPUSH course c++101
(integer) 2
redis> BRPOP course 30
1) "course" # 被弹出元素所属的列表键
2) "c++101" # 被弹出的元素
命令格式:LPUSH key value [value …]
可用版本: >= 1.0.0
时间复杂度: O(1)
将一个或多个值 value
插入到列表 key
的表头
如果有多个 value
值,那么各个 value
值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist
执行命令 LPUSH mylist a b c
,列表的值将是 c b a
,这等同于原子性地执行 LPUSH mylist a
、 LPUSH mylist b
和 LPUSH mylist c
三个命令。
如果 key
不存在,一个空列表会被创建并执行 LPUSH 操作。
当 key
存在但不是列表类型时,返回一个错误。
Note
在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value
值。
执行 LPUSH 命令后,列表的长度。
# 加入单个元素
redis> LPUSH languages python
(integer) 1
# 加入重复元素
redis> LPUSH languages python
(integer) 2
redis> LRANGE languages 0 -1 # 列表允许重复元素
1) "python"
2) "python"
# 加入多个元素
redis> LPUSH mylist a b c
(integer) 3
redis> LRANGE mylist 0 -1
1) "c"
2) "b"
3) "a"
命令格式:LPUSH key value [value …]
可用版本: >= 1.0.0
时间复杂度: O(N),
N
为被移除的元素的数量。
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
举个例子,执行命令 LTRIM list 0 2
,表示只保留列表 list
的前三个元素,其余元素全部删除。
下标(index)参数 start
和 stop
都以 0
为底,也就是说,以 0
表示列表的第一个元素,以 1
表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1
表示列表的最后一个元素, -2
表示列表的倒数第二个元素,以此类推。
当 key
不是列表类型时,返回一个错误。
LTRIM 命令通常和 LPUSH key value value …] 命令或 [RPUSH key value [value …] 命令配合使用,举个例子:
LPUSH log newest_log
LTRIM log 0 99
这个例子模拟了一个日志程序,每次将最新日志 newest_log
放到 log
列表中,并且只保留最新的 100
项。注意当这样使用 LTRIM
命令时,时间复杂度是O(1),因为平均情况下,每次只有一个元素被移除。
假如你有一个包含一百个元素的列表 list
,对该列表执行 LTRIM list 0 10
,结果是一个包含11个元素的列表,这表明 stop
下标也在 LTRIM 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new
、 Array#slice
和Python的 range()
函数。
超出范围的下标值不会引起错误。
如果 start
下标比列表的最大下标 end
( LLEN list
减去 1
)还要大,或者 start > stop
, LTRIM 返回一个空列表(因为 LTRIM 已经将整个列表清空)。
如果 stop
下标比 end
下标还要大,Redis将 stop
的值设置为 end
。
命令执行成功时,返回 ok
。
# 情况 1: 常见情况, start 和 stop 都在列表的索引范围之内
redis> LRANGE alpha 0 -1 # alpha 是一个包含 5 个字符串的列表
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"
redis> LTRIM alpha 1 -1 # 删除 alpha 列表索引为 0 的元素
OK
redis> LRANGE alpha 0 -1 # "h" 被删除了
1) "e"
2) "l"
3) "l"
4) "o"
# 情况 2: stop 比列表的最大下标还要大
redis> LTRIM alpha 1 10086 # 保留 alpha 列表索引 1 至索引 10086 上的元素
OK
redis> LRANGE alpha 0 -1 # 只有索引 0 上的元素 "e" 被删除了,其他元素还在
1) "l"
2) "l"
3) "o"
# 情况 3: start 和 stop 都比列表的最大下标要大,并且 start < stop
redis> LTRIM alpha 10086 123321
OK
redis> LRANGE alpha 0 -1 # 列表被清空
(empty list or set)
# 情况 4: start 和 stop 都比列表的最大下标要大,并且 start > stop
redis> RPUSH new-alpha "h" "e" "l" "l" "o" # 重新建立一个新列表
(integer) 5
redis> LRANGE new-alpha 0 -1
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"
redis> LTRIM new-alpha 123321 10086 # 执行 LTRIM
OK
redis> LRANGE new-alpha 0 -1 # 同样被清空
(empty list or set)
命令格式:RPUSH key value [value …]
可用版本: >= 1.0.0
时间复杂度: O(1)
将一个或多个值 value
插入到列表 key
的表尾(最右边)。
如果有多个 value
值,那么各个 value
值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist
执行 RPUSH mylist a b c
,得出的结果列表为 a b c
,等同于执行命令 RPUSH mylist a
、 RPUSH mylist b
、 RPUSH mylist c
。
如果 key
不存在,一个空列表会被创建并执行 RPUSH 操作。
当 key
存在但不是列表类型时,返回一个错误。
Note
在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value
值。
执行 RPUSH 操作后,表的长度。
# 添加单个元素
redis> RPUSH languages c
(integer) 1
# 添加重复元素
redis> RPUSH languages c
(integer) 2
redis> LRANGE languages 0 -1 # 列表允许重复元素
1) "c"
2) "c"
# 添加多个元素
redis> RPUSH mylist a b c
(integer) 3
redis> LRANGE mylist 0 -1
1) "a"
2) "b"
3) "c"
命令格式:LPUSHX key value
可用版本: >= 2.2.0
时间复杂度: O(1)
将值 value
插入到列表 key
的表头,当且仅当 key
存在并且是一个列表。
和 [LPUSH key value value …] 命令相反,当 key
不存在时, LPUSHX 命令什么也不做。
LPUSHX 命令执行之后,表的长度。
# 对空列表执行 LPUSHX
redis> LLEN greet # greet 是一个空列表
(integer) 0
redis> LPUSHX greet "hello" # 尝试 LPUSHX,失败,因为列表为空
(integer) 0
# 对非空列表执行 LPUSHX
redis> LPUSH greet "hello" # 先用 LPUSH 创建一个有一个元素的列表
(integer) 1
redis> LPUSHX greet "good morning" # 这次 LPUSHX 执行成功
(integer) 2
redis> LRANGE greet 0 -1
1) "good morning"
2) "hello"
命令格式:LPUSHX key value
可用版本: >= 1.0.0
时间复杂度: O(S+N),
S
为偏移量start
,N
为指定区间内元素的数量。
返回列表 key
中指定区间内的元素,区间以偏移量 start
和 stop
指定。
下标(index)参数 start
和 stop
都以 0
为底,也就是说,以 0
表示列表的第一个元素,以 1
表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1
表示列表的最后一个元素, -2
表示列表的倒数第二个元素,以此类推。
假如你有一个包含一百个元素的列表,对该列表执行 LRANGE list 0 10
,结果是一个包含11个元素的列表,这表明 stop
下标也在 LRANGE 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new
、 Array#slice
和Python的 range()
函数。
超出范围的下标值不会引起错误。
如果 start
下标比列表的最大下标 end
( LLEN list
减去 1
)还要大,那么 LRANGE 返回一个空列表。
如果 stop
下标比 end
下标还要大,Redis将 stop
的值设置为 end
。
一个列表,包含指定区间内的元素。
redis> RPUSH fp-language lisp
(integer) 1
redis> LRANGE fp-language 0 0
1) "lisp"
redis> RPUSH fp-language scheme
(integer) 2
redis> LRANGE fp-language 0 1
1) "lisp"
2) "scheme"
命令格式:RPUSHX key value
可用版本: >= 2.2.0
时间复杂度: O(1)
将值 value
插入到列表 key
的表尾,当且仅当 key
存在并且是一个列表。
和 [RPUSH key value value …] 命令相反,当 key
不存在时, RPUSHX 命令什么也不做。
RPUSHX 命令执行之后,表的长度。
# key不存在
redis> LLEN greet
(integer) 0
redis> RPUSHX greet "hello" # 对不存在的 key 进行 RPUSHX,PUSH 失败。
(integer) 0
# key 存在且是一个非空列表
redis> RPUSH greet "hi" # 先用 RPUSH 插入一个元素
(integer) 1
redis> RPUSHX greet "hello" # greet 现在是一个列表类型,RPUSHX 操作成功。
(integer) 2
redis> LRANGE greet 0 -1
1) "hi"
2) "hello"
命令格式:LINSERT key BEFORE|AFTER pivot value
可用版本: >= 2.2.0
时间复杂度: O(N),
N
为寻找pivot
过程中经过的元素数量。
将值 value
插入到列表 key
当中,位于值 pivot
之前或之后。
当 pivot
不存在于列表 key
时,不执行任何操作。
当 key
不存在时, key
被视为空列表,不执行任何操作。
如果 key
不是列表类型,返回一个错误。
如果命令执行成功,返回插入操作完成之后,列表的长度。 如果没有找到 pivot
,返回 -1
。 如果 key
不存在或为空列表,返回 0
。
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
# 对一个非空列表插入,查找一个不存在的 pivot
redis> LINSERT mylist BEFORE "go" "let's"
(integer) -1 # 失败
# 对一个空列表执行 LINSERT 命令
redis> EXISTS fake_list
(integer) 0
redis> LINSERT fake_list BEFORE "nono" "gogogog"
(integer) 0 # 失败
命令格式:RPOPLPUSH source destination
可用版本: >= 1.2.0
时间复杂度: O(1)
命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作:
source
中的最后一个元素(尾元素)弹出,并返回给客户端。source
弹出的元素插入到列表 destination
,作为 destination
列表的的头元素。举个例子,你有两个列表 source
和 destination
, source
列表有元素 a, b, c
, destination
列表有元素 x, y, z
,执行 RPOPLPUSH source destination
之后, source
列表包含元素 a, b
, destination
列表包含元素 c, x, y, z
,并且元素 c
会被返回给客户端。
如果 source
不存在,值 nil
被返回,并且不执行其他动作。
如果 source
和 destination
相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。
被弹出的元素。
# source 和 destination 不同
redis> LRANGE alpha 0 -1 # 查看所有元素
1) "a"
2) "b"
3) "c"
4) "d"
redis> RPOPLPUSH alpha reciver # 执行一次 RPOPLPUSH 看看
"d"
redis> LRANGE alpha 0 -1
1) "a"
2) "b"
3) "c"
redis> LRANGE reciver 0 -1
1) "d"
redis> RPOPLPUSH alpha reciver # 再执行一次,证实 RPOP 和 LPUSH 的位置正确
"c"
redis> LRANGE alpha 0 -1
1) "a"
2) "b"
redis> LRANGE reciver 0 -1
1) "c"
2) "d"
# source 和 destination 相同
redis> LRANGE number 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
redis> RPOPLPUSH number number
"4"
redis> LRANGE number 0 -1 # 4 被旋转到了表头
1) "4"
2) "1"
3) "2"
4) "3"
redis> RPOPLPUSH number number
"3"
redis> LRANGE number 0 -1 # 这次是 3 被旋转到了表头
1) "3"
2) "4"
3) "1"
4) "2"
Redis的列表经常被用作队列(queue),用于在不同程序之间有序地交换消息(message)。一个客户端通过 [LPUSH key value value …] 命令将消息放入队列中,而另一个客户端通过 RPOP key 或者 [BRPOP key key …] timeout 命令取出队列中等待时间最长的消息。
不幸的是,上面的队列方法是『不安全』的,因为在这个过程中,一个客户端可能在取出一个消息之后崩溃,而未处理完的消息也就因此丢失。
使用 RPOPLPUSH 命令(或者它的阻塞版本 BRPOPLPUSH source destination timeout )可以解决这个问题:因为它不仅返回一个消息,同时还将这个消息添加到另一个备份列表当中,如果一切正常的话,当一个客户端完成某个消息的处理之后,可以用 LREM key count value 命令将这个消息从备份表删除。
最后,还可以添加一个客户端专门用于监视备份表,它自动地将超过一定处理时限的消息重新放入队列中去(负责处理该消息的客户端可能已经崩溃),这样就不会丢失任何消息了。
通过使用相同的 key
作为 RPOPLPUSH 命令的两个参数,客户端可以用一个接一个地获取列表元素的方式,取得列表的所有元素,而不必像 LRANGE key start stop 命令那样一下子将所有列表元素都从服务器传送到客户端中(两种方式的总复杂度都是 O(N))。
以上的模式甚至在以下的两个情况下也能正常工作:
这个模式使得我们可以很容易实现这样一类系统:有 N 个客户端,需要连续不断地对一些元素进行处理,而且处理的过程必须尽可能地快。一个典型的例子就是服务器的监控程序:它们需要在尽可能短的时间内,并行地检查一组网站,确保它们的可访问性。
注意,使用这个模式的客户端是易于扩展(scala)且安全(reliable)的,因为就算接收到元素的客户端失败,元素还是保存在列表里面,不会丢失,等到下个迭代来临的时候,别的客户端又可以继续处理这些元素了。
命令格式:BRPOPLPUSH source destination timeout
可用版本: >= 2.2.0
时间复杂度: O(1)
BRPOPLPUSH 是 RPOPLPUSH source destination 的阻塞版本,当给定列表 source
不为空时, BRPOPLPUSH 的表现和 RPOPLPUSH source destination 一样。
当列表 source
为空时, BRPOPLPUSH 命令将阻塞连接,直到等待超时,或有另一个客户端对 source
执行 LPUSH key value [value …] 或 RPUSH key value [value …] 命令为止。
超时参数 timeout
接受一个以秒为单位的数字作为值。超时参数设为 0
表示阻塞时间可以无限期延长(block indefinitely) 。
更多相关信息,请参考 RPOPLPUSH source destination 命令。
假如在指定时间内没有任何元素被弹出,则返回一个 nil
和等待时长。 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素的值,第二个元素是等待时长。
# 非空列表
redis> BRPOPLPUSH msg reciver 500
"hello moto" # 弹出元素的值
(3.38s) # 等待时长
redis> LLEN reciver
(integer) 1
redis> LRANGE reciver 0 0
1) "hello moto"
# 空列表
redis> BRPOPLPUSH msg reciver 1
(nil)
(1.34s)
参考 RPOPLPUSH source destination 命令的《安全队列》一节。
参考 RPOPLPUSH source destination 命令的《循环列表》一节。
集合类似 Java 中的 HashSet, 内部实现是一个 value 永远为 null 的 HashMap, 实际就是通过计算 hash 的方式来快速排重的,这也是 set 能提供判断一个成员是否在集合内的原因。
redis 的 sets 类型是使用哈希表构造的,因此复杂度是 0(1), 它支持集合内的增删改查,并且支持多个集合间的交集、并集、差集操作。可以利用这些集合操作,解决程序开发过程当中很多数据集合间的问题。比如计算网站独立 ip, 用户画像中的用户标签,共同好友等功能
命令格式:SADD key member [member …]
可用版本: >= 1.0.0
时间复杂度: O(N),
N
是被添加的元素的数量。
将一个或多个 member
元素加入到集合 key
当中,已经存在于集合的 member
元素将被忽略。
假如 key
不存在,则创建一个只包含 member
元素作成员的集合。
当 key
不是集合类型时,返回一个错误。
Note
在Redis2.4版本以前, SADD 只接受单个 member
值。
被添加到集合中的新元素的数量,不包括被忽略的元素。
# 添加单个元素
redis> SADD bbs "discuz.net"
(integer) 1
# 添加重复元素
redis> SADD bbs "discuz.net"
(integer) 0
# 添加多个元素
redis> SADD bbs "tianya.cn" "groups.google.com"
(integer) 2
redis> SMEMBERS bbs
1) "discuz.net"
2) "groups.google.com"
3) "tianya.cn"
命令格式:SPOP key
可用版本: >= 1.0.0
时间复杂度: O(1)
移除并返回集合中的一个随机元素。
如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 [SRANDMEMBER key count] 命令。
被移除的随机元素。 当 key
不存在或 key
是空集时,返回 nil
。
redis> SMEMBERS db
1) "MySQL"
2) "MongoDB"
3) "Redis"
redis> SPOP db
"Redis"
redis> SMEMBERS db
1) "MySQL"
2) "MongoDB"
redis> SPOP db
"MySQL"
redis> SMEMBERS db
1) "MongoDB"
命令格式:SREM key member [member …]
可用版本: >= 1.0.0
时间复杂度: O(N),
N
为给定member
元素的数量。
移除集合 key
中的一个或多个 member
元素,不存在的 member
元素会被忽略。
当 key
不是集合类型,返回一个错误。
Note
在 Redis 2.4 版本以前, SREM 只接受单个 member
值。
被成功移除的元素的数量,不包括被忽略的元素。
# 测试数据
redis> SMEMBERS languages
1) "c"
2) "lisp"
3) "python"
4) "ruby"
# 移除单个元素
redis> SREM languages ruby
(integer) 1
# 移除不存在元素
redis> SREM languages non-exists-language
(integer) 0
# 移除多个元素
redis> SREM languages lisp python c
(integer) 3
redis> SMEMBERS languages
(empty list or set)
命令格式:SCARD key
可用版本: >= 1.0.0
时间复杂度: O(1)
返回集合 key
的基数(集合中元素的数量)。
集合的基数。 当 key
不存在时,返回 0
。
redis> SADD tool pc printer phone
(integer) 3
redis> SCARD tool # 非空集合
(integer) 3
redis> DEL tool
(integer) 1
redis> SCARD tool # 空集合
(integer) 0
命令格式:SDIFF
可用版本: >= 1.0.0
时间复杂度: O(N),
N
是所有给定集合的成员数量之和。
返回一个集合的全部成员,该集合是所有给定集合之间的差集。
不存在的 key
被视为空集。
一个包含差集成员的列表。
redis> SMEMBERS peter's_movies
1) "bet man"
2) "start war"
3) "2012"
redis> SMEMBERS joe's_movies
1) "hi, lady"
2) "Fast Five"
3) "2012"
redis> SDIFF peter's_movies joe's_movies
1) "bet man"
2) "start war"
命令格式:SMOVE source destination member
可用版本: >= 1.0.0
时间复杂度: O(1)
将 member
元素从 source
集合移动到 destination
集合。
SMOVE 是原子性操作。
如果 source
集合不存在或不包含指定的 member
元素,则 SMOVE 命令不执行任何操作,仅返回 0
。否则, member
元素从 source
集合中被移除,并添加到 destination
集合中去。
当 destination
集合已经包含 member
元素时, SMOVE 命令只是简单地将 source
集合中的 member
元素删除。
当 source
或 destination
不是集合类型时,返回一个错误。
如果 member
元素被成功移除,返回 1
。 如果 member
元素不是 source
集合的成员,并且没有任何操作对 destination
集合执行,那么返回 0
。
redis> SMEMBERS songs
1) "Billie Jean"
2) "Believe Me"
redis> SMEMBERS my_songs
(empty list or set)
redis> SMOVE songs my_songs "Believe Me"
(integer) 1
redis> SMEMBERS songs
1) "Billie Jean"
redis> SMEMBERS my_songs
1) "Believe Me"
命令格式:SSCAN key cursor [MATCH pattern] [COUNT count]
详细信息请参考 SCAN cursor [MATCH pattern] [COUNT count] 命令。
命令格式:SINTER key [key …]
可用版本: >= 1.0.0
时间复杂度: O(N * M),
N
为给定集合当中基数最小的集合,M
为给定集合的个数。
返回一个集合的全部成员,该集合是所有给定集合的交集。
不存在的 key
被视为空集。
当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。
交集成员的列表。
redis> SMEMBERS group_1
1) "LI LEI"
2) "TOM"
3) "JACK"
redis> SMEMBERS group_2
1) "HAN MEIMEI"
2) "JACK"
redis> SINTER group_1 group_2
1) "JACK"
命令格式:SUNION key [key …]
可用版本: >= 1.0.0
时间复杂度: O(N),
N
是所有给定集合的成员数量之和。
返回一个集合的全部成员,该集合是所有给定集合的并集。
不存在的 key
被视为空集。
并集成员的列表。
redis> SMEMBERS songs
1) "Billie Jean"
redis> SMEMBERS my_songs
1) "Believe Me"
redis> SUNION songs my_songs
1) "Billie Jean"
2) "Believe Me"
命令格式:SMEMBERS key
可用版本: >= 1.0.0
时间复杂度: O(N),
N
为集合的基数。
返回集合 key
中的所有成员。
不存在的 key
被视为空集合。
集合中的所有成员。
# key 不存在或集合为空
redis> EXISTS not_exists_key
(integer) 0
redis> SMEMBERS not_exists_key
(empty list or set)
# 非空集合
redis> SADD language Ruby Python Clojure
(integer) 3
redis> SMEMBERS language
1) "Python"
2) "Ruby"
3) "Clojure"
命令格式:SISMEMBER key member
可用版本: >= 1.0.0
时间复杂度: O(1)
判断 member
元素是否集合 key
的成员。
如果 member
元素是集合的成员,返回 1
。 如果 member
元素不是集合的成员,或 key
不存在,返回 0
。
redis> SMEMBERS joe's_movies
1) "hi, lady"
2) "Fast Five"
3) "2012"
redis> SISMEMBER joe's_movies "bet man"
(integer) 0
redis> SISMEMBER joe's_movies "Fast Five"
(integer) 1
命令格式:SDIFFSTORE destination key [key …]
可用版本: >= 1.0.0
时间复杂度: O(N),
N
是所有给定集合的成员数量之和。
这个命令的作用和 [SDIFF key key …] 类似,但它将结果保存到 destination
集合,而不是简单地返回结果集。
如果 destination
集合已经存在,则将其覆盖。
destination
可以是 key
本身。
结果集中的元素数量。
redis> SMEMBERS joe's_movies
1) "hi, lady"
2) "Fast Five"
3) "2012"
redis> SMEMBERS peter's_movies
1) "bet man"
2) "start war"
3) "2012"
redis> SDIFFSTORE joe_diff_peter joe's_movies peter's_movies
(integer) 2
redis> SMEMBERS joe_diff_peter
1) "hi, lady"
2) "Fast Five"
命令格式:SINTERSTORE destination key [key …]
可用版本: >= 1.0.0
时间复杂度: O(N * M),
N
为给定集合当中基数最小的集合,M
为给定集合的个数。
这个命令类似于 SINTER key [key …] 命令,但它将结果保存到 destination
集合,而不是简单地返回结果集。
如果 destination
集合已经存在,则将其覆盖。
destination
可以是 key
本身。
结果集中的成员数量。
redis> SMEMBERS songs
1) "good bye joe"
2) "hello,peter"
redis> SMEMBERS my_songs
1) "good bye joe"
2) "falling"
redis> SINTERSTORE song_interset songs my_songs
(integer) 1
redis> SMEMBERS song_interset
1) "good bye joe"
命令格式:SRANDMEMBER key [count]
可用版本: >= 1.0.0
时间复杂度: 只提供
key
参数时为 O(1) 。如果提供了count
参数,那么为 O(N) ,N 为返回数组的元素个数。
如果命令执行时,只提供了 key
参数,那么返回集合中的一个随机元素。
从 Redis 2.6 版本开始, SRANDMEMBER 命令接受可选的 count
参数:
count
为正数,且小于集合基数,那么命令返回一个包含 count
个元素的数组,数组中的元素各不相同。如果 count
大于等于集合基数,那么返回整个集合。count
为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count
的绝对值。该操作和 SPOP key 相似,但 SPOP key 将随机元素从集合中移除并返回,而 SRANDMEMBER 则仅仅返回随机元素,而不对集合进行任何改动。
只提供 key
参数时,返回一个元素;如果集合为空,返回 nil
。 如果提供了 count
参数,那么返回一个数组;如果集合为空,返回空数组。
# 添加元素
redis> SADD fruit apple banana cherry
(integer) 3
# 只给定 key 参数,返回一个随机元素
redis> SRANDMEMBER fruit
"cherry"
redis> SRANDMEMBER fruit
"apple"
# 给定 3 为 count 参数,返回 3 个随机元素
# 每个随机元素都不相同
redis> SRANDMEMBER fruit 3
1) "apple"
2) "banana"
3) "cherry"
# 给定 -3 为 count 参数,返回 3 个随机元素
# 元素可能会重复出现多次
redis> SRANDMEMBER fruit -3
1) "banana"
2) "cherry"
3) "apple"
redis> SRANDMEMBER fruit -3
1) "apple"
2) "apple"
3) "cherry"
# 如果 count 是整数,且大于等于集合基数,那么返回整个集合
redis> SRANDMEMBER fruit 10
1) "apple"
2) "banana"
3) "cherry"
# 如果 count 是负数,且 count 的绝对值大于集合的基数
# 那么返回的数组的长度为 count 的绝对值
redis> SRANDMEMBER fruit -10
1) "banana"
2) "apple"
3) "banana"
4) "cherry"
5) "apple"
6) "apple"
7) "cherry"
8) "apple"
9) "apple"
10) "banana"
# SRANDMEMBER 并不会修改集合内容
redis> SMEMBERS fruit
1) "apple"
2) "cherry"
3) "banana"
# 集合为空时返回 nil 或者空数组
redis> SRANDMEMBER not-exists
(nil)
redis> SRANDMEMBER not-eixsts 10
(empty list or set)
命令格式:SUNIONSTORE destination key [key …]
可用版本: >= 1.0.0
时间复杂度: O(N),
N
是所有给定集合的成员数量之和。
这个命令类似于 [SUNION key key …] 命令,但它将结果保存到 destination
集合,而不是简单地返回结果集。
如果 destination
已经存在,则将其覆盖。
destination
可以是 key
本身。
结果集中的元素数量。
redis> SMEMBERS NoSQL
1) "MongoDB"
2) "Redis"
redis> SMEMBERS SQL
1) "sqlite"
2) "MySQL"
redis> SUNIONSTORE db NoSQL SQL
(integer) 4
redis> SMEMBERS db
1) "MySQL"
2) "sqlite"
3) "MongoDB"
4) "Redis"
sorted sets 是 Redis 类似于 SortedSet 和 HashMap 的结合体,一方面它是一个 set, 保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score, 代表这个 value 的排序权重。内部使用 HashMap 和跳跃表 (SkipList) 来保证数据的存储和有序,HashMap 里放的是成员到 score 的映射,而跳跃表里存放的是所有的成员,排序依据是 HashMap 里存的 score, 使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。sorted sets 中最后一个 value 被移除后,数据结构自动删除,内存被回收。
主要应用于根据某个权重进行排序的队列的场景,比如游戏积分排行榜,设置优先级的任务列表,学生成绩表等。
命令格式:ZADD key score member [[score member] [score member] …]
可用版本: >= 1.2.0
时间复杂度: O(M*log(N)),
N
是有序集的基数,M
为成功添加的新成员的数量。
将一个或多个 member
元素及其 score
值加入到有序集 key
当中。
如果某个 member
已经是有序集的成员,那么更新这个 member
的 score
值,并通过重新插入这个 member
元素,来保证该 member
在正确的位置上。
score
值可以是整数值或双精度浮点数。
如果 key
不存在,则创建一个空的有序集并执行 ZADD 操作。
当 key
存在但不是有序集类型时,返回一个错误。
对有序集的更多介绍请参见 sorted set 。
Note
在 Redis 2.4 版本以前, ZADD 每次只能添加一个元素。
被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。
# 添加单个元素
redis> ZADD page_rank 10 google.com
(integer) 1
# 添加多个元素
redis> ZADD page_rank 9 baidu.com 8 bing.com
(integer) 2
redis> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
5) "google.com"
6) "10"
# 添加已存在元素,且 score 值不变
redis> ZADD page_rank 10 google.com
(integer) 0
redis> ZRANGE page_rank 0 -1 WITHSCORES # 没有改变
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
5) "google.com"
6) "10"
# 添加已存在元素,但是改变 score 值
redis> ZADD page_rank 6 bing.com
(integer) 0
redis> ZRANGE page_rank 0 -1 WITHSCORES # bing.com 元素的 score 值被改变
1) "bing.com"
2) "6"
3) "baidu.com"
4) "9"
5) "google.com"
6) "10"
命令格式:ZREM key member [member …]
可用版本: >= 1.2.0
时间复杂度: O(M*log(N)),
N
为有序集的基数,M
为被成功移除的成员的数量。
移除有序集 key
中的一个或多个成员,不存在的成员将被忽略。
当 key
存在但不是有序集类型时,返回一个错误。
Note
在 Redis 2.4 版本以前, ZREM 每次只能删除一个元素。
被成功移除的成员的数量,不包括被忽略的成员。
# 测试数据
redis> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
5) "google.com"
6) "10"
# 移除单个元素
redis> ZREM page_rank google.com
(integer) 1
redis> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
# 移除多个元素
redis> ZREM page_rank baidu.com bing.com
(integer) 2
redis> ZRANGE page_rank 0 -1 WITHSCORES
(empty list or set)
# 移除不存在元素
redis> ZREM page_rank non-exists-element
(integer) 0
命令格式:ZCARD key
可用版本: >= 1.2.0
时间复杂度: O(1)
返回有序集 key
的基数。
当 key
存在且是有序集类型时,返回有序集的基数。 当 key
不存在时,返回 0
。
redis > ZADD salary 2000 tom # 添加一个成员
(integer) 1
redis > ZCARD salary
(integer) 1
redis > ZADD salary 5000 jack # 再添加一个成员
(integer) 1
redis > ZCARD salary
(integer) 2
redis > EXISTS non_exists_key # 对不存在的 key 进行 ZCARD 操作
(integer) 0
redis > ZCARD non_exists_key
(integer) 0
命令格式:ZRANK key member
可用版本: >= 2.0.0
时间复杂度: O(log(N))
返回有序集 key
中成员 member
的排名。其中有序集成员按 score
值递增(从小到大)顺序排列。
排名以 0
为底,也就是说, score
值最小的成员排名为 0
。
使用 ZREVRANK key member 命令可以获得成员按 score
值递减(从大到小)排列的排名。
如果 member
是有序集 key
的成员,返回 member
的排名。 如果 member
不是有序集 key
的成员,返回 nil
。
redis> ZRANGE salary 0 -1 WITHSCORES # 显示所有成员及其 score 值
1) "peter"
2) "3500"
3) "tom"
4) "4000"
5) "jack"
6) "5000"
redis> ZRANK salary tom # 显示 tom 的薪水排名,第二
(integer) 1
命令格式:ZSCAN key cursor [MATCH pattern] [COUNT count]
详细信息请参考 [SCAN cursor MATCH pattern] [COUNT count] 命令。
命令格式:ZCOUNT key min max
可用版本: >= 2.0.0
时间复杂度: O(log(N)),
N
为有序集的基数。
返回有序集 key
中, score
值在 min
和 max
之间(默认包括 score
值等于 min
或 max
)的成员的数量。
关于参数 min
和 max
的详细使用方法,请参考 [ZRANGEBYSCORE key min max WITHSCORES] [LIMIT offset count] 命令。
score
值在 min
和 max
之间的成员的数量。
redis> ZRANGE salary 0 -1 WITHSCORES # 测试数据
1) "jack"
2) "2000"
3) "peter"
4) "3500"
5) "tom"
6) "5000"
redis> ZCOUNT salary 2000 5000 # 计算薪水在 2000-5000 之间的人数
(integer) 3
redis> ZCOUNT salary 3000 5000 # 计算薪水在 3000-5000 之间的人数
(integer) 2
命令格式:ZRANGE key start stop [WITHSCORES]
可用版本: >= 1.2.0
时间复杂度: O(log(N)+M),
N
为有序集的基数,而M
为结果集的基数。
返回有序集 key
中,指定区间内的成员。
其中成员的位置按 score
值递增(从小到大)来排序。
具有相同 score
值的成员按字典序(lexicographical order )来排列。
如果你需要成员按 score
值递减(从大到小)来排列,请使用 [ZREVRANGE key start stop WITHSCORES] 命令。
下标参数 start
和 stop
都以 0
为底,也就是说,以 0
表示有序集第一个成员,以 1
表示有序集第二个成员,以此类推。 你也可以使用负数下标,以 -1
表示最后一个成员, -2
表示倒数第二个成员,以此类推。
超出范围的下标并不会引起错误。 比如说,当 start
的值比有序集的最大下标还要大,或是 start > stop
时, ZRANGE 命令只是简单地返回一个空列表。 另一方面,假如 stop
参数的值比有序集的最大下标还要大,那么 Redis 将 stop
当作最大下标来处理。
可以通过使用 WITHSCORES
选项,来让成员和它的 score
值一并返回,返回列表以 value1,score1, ..., valueN,scoreN
的格式表示。 客户端库可能会返回一些更复杂的数据类型,比如数组、元组等。
指定区间内,带有 score
值(可选)的有序集成员的列表。
redis > ZRANGE salary 0 -1 WITHSCORES # 显示整个有序集成员
1) "jack"
2) "3500"
3) "tom"
4) "5000"
5) "boss"
6) "10086"
redis > ZRANGE salary 1 2 WITHSCORES # 显示有序集下标区间 1 至 2 的成员
1) "tom"
2) "5000"
3) "boss"
4) "10086"
redis > ZRANGE salary 0 200000 WITHSCORES # 测试 end 下标超出最大下标时的情况
1) "jack"
2) "3500"
3) "tom"
4) "5000"
5) "boss"
6) "10086"
redis > ZRANGE salary 200000 3000000 WITHSCORES # 测试当给定区间不存在于有序集时的情况
(empty list or set)
命令格式:ZSCORE key member
可用版本: >= 1.2.0
时间复杂度: O(1)
返回有序集 key
中,成员 member
的 score
值。
如果 member
元素不是有序集 key
的成员,或 key
不存在,返回 nil
。
member
成员的 score
值,以字符串形式表示。
redis> ZRANGE salary 0 -1 WITHSCORES # 测试数据
1) "tom"
2) "2000"
3) "peter"
4) "3500"
5) "jack"
6) "5000"
redis> ZSCORE salary peter # 注意返回值是字符串
"3500"
命令格式:ZINCRBY key increment member
可用版本: >= 1.2.0
时间复杂度: O(log(N))
为有序集 key
的成员 member
的 score
值加上增量 increment
。
可以通过传递一个负数值 increment
,让 score
减去相应的值,比如 ZINCRBY key -5 member
,就是让 member
的 score
值减去 5
。
当 key
不存在,或 member
不是 key
的成员时, ZINCRBY key increment member
等同于 ZADD key increment member
。
当 key
不是有序集类型时,返回一个错误。
score
值可以是整数值或双精度浮点数。
member
成员的新 score
值,以字符串形式表示。
redis> ZSCORE salary tom
"2000"
redis> ZINCRBY salary 2000 tom # tom 加薪啦!
"4000"
命令格式:ZREVRANK key member
可用版本: >= 2.0.0
时间复杂度: O(log(N))
返回有序集 key
中成员 member
的排名。其中有序集成员按 score
值递减(从大到小)排序。
排名以 0
为底,也就是说, score
值最大的成员排名为 0
。
使用 ZRANK key member 命令可以获得成员按 score
值递增(从小到大)排列的排名。
如果 member
是有序集 key
的成员,返回 member
的排名。 如果 member
不是有序集 key
的成员,返回 nil
。
redis 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES # 测试数据
1) "jack"
2) "2000"
3) "peter"
4) "3500"
5) "tom"
6) "5000"
redis> ZREVRANK salary peter # peter 的工资排第二
(integer) 1
redis> ZREVRANK salary tom # tom 的工资最高
(integer) 0
命令格式:ZREVRANGE key start stop [WITHSCORES]
可用版本: >= 1.2.0
时间复杂度: O(log(N)+M),
N
为有序集的基数,而M
为结果集的基数。
返回有序集 key
中,指定区间内的成员。
其中成员的位置按 score
值递减(从大到小)来排列。 具有相同 score
值的成员按字典序的逆序(reverse lexicographical order)排列。
除了成员按 score
值递减的次序排列这一点外, ZREVRANGE 命令的其他方面和 [ZRANGE key start stop WITHSCORES] 命令一样。
指定区间内,带有 score
值(可选)的有序集成员的列表。
redis> ZRANGE salary 0 -1 WITHSCORES # 递增排列
1) "peter"
2) "3500"
3) "tom"
4) "4000"
5) "jack"
6) "5000"
redis> ZREVRANGE salary 0 -1 WITHSCORES # 递减排列
1) "jack"
2) "5000"
3) "tom"
4) "4000"
5) "peter"
6) "3500"
命令格式:ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
可用版本: >= 2.0.0
时间复杂度: O(NK)+O(Mlog(M)),
N
为给定key
中基数最小的有序集,K
为给定有序集的数量,M
为结果集的基数。
计算给定的一个或多个有序集的交集,其中给定 key
的数量必须以 numkeys
参数指定,并将该交集(结果集)储存到 destination
。
默认情况下,结果集中某个成员的 score
值是所有给定集下该成员 score
值之和.
关于 WEIGHTS
和 AGGREGATE
选项的描述,参见 ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] 命令。
保存到 destination
的结果集的基数。
redis > ZADD mid_test 70 "Li Lei"
(integer) 1
redis > ZADD mid_test 70 "Han Meimei"
(integer) 1
redis > ZADD mid_test 99.5 "Tom"
(integer) 1
redis > ZADD fin_test 88 "Li Lei"
(integer) 1
redis > ZADD fin_test 75 "Han Meimei"
(integer) 1
redis > ZADD fin_test 99.5 "Tom"
(integer) 1
redis > ZINTERSTORE sum_point 2 mid_test fin_test
(integer) 3
redis > ZRANGE sum_point 0 -1 WITHSCORES # 显示有序集内所有成员及其 score 值
1) "Han Meimei"
2) "145"
3) "Li Lei"
4) "158"
5) "Tom"
6) "199"
命令格式:ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
可用版本:>= 2.0.0
时间复杂度: O(N)+O(M log(M)),
N
为给定有序集基数的总和,M
为结果集的基数。
计算给定的一个或多个有序集的并集,其中给定 key
的数量必须以 numkeys
参数指定,并将该并集(结果集)储存到 destination
。
默认情况下,结果集中某个成员的 score
值是所有给定集下该成员 score
值之 和 。
使用 WEIGHTS
选项,你可以为 每个 给定有序集 分别 指定一个乘法因子(multiplication factor),每个给定有序集的所有成员的 score
值在传递给聚合函数(aggregation function)之前都要先乘以该有序集的因子。
如果没有指定 WEIGHTS
选项,乘法因子默认设置为 1
。
使用 AGGREGATE
选项,你可以指定并集的结果集的聚合方式。
默认使用的参数 SUM
,可以将所有集合中某个成员的 score
值之 和 作为结果集中该成员的 score
值;使用参数 MIN
,可以将所有集合中某个成员的 最小 score
值作为结果集中该成员的 score
值;而参数 MAX
则是将所有集合中某个成员的 最大 score
值作为结果集中该成员的 score
值。
保存到 destination
的结果集的基数。
redis> ZRANGE programmer 0 -1 WITHSCORES
1) "peter"
2) "2000"
3) "jack"
4) "3500"
5) "tom"
6) "5000"
redis> ZRANGE manager 0 -1 WITHSCORES
1) "herry"
2) "2000"
3) "mary"
4) "3500"
5) "bob"
6) "4000"
redis> ZUNIONSTORE salary 2 programmer manager WEIGHTS 1 3 # 公司决定加薪。。。除了程序员。。。
(integer) 6
redis> ZRANGE salary 0 -1 WITHSCORES
1) "peter"
2) "2000"
3) "jack"
4) "3500"
5) "tom"
6) "5000"
7) "herry"
8) "6000"
9) "mary"
10) "10500"
11) "bob"
12) "12000"
命令格式:ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
可用版本: >= 1.0.5
时间复杂度: O(log(N)+M),
N
为有序集的基数,M
为被结果集的基数。
返回有序集 key
中,所有 score
值介于 min
和 max
之间(包括等于 min
或 max
)的成员。有序集成员按 score
值递增(从小到大)次序排列。
具有相同 score
值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的,不需要额外的计算)。
可选的 LIMIT
参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count
),注意当 offset
很大时,定位 offset
的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。
可选的 WITHSCORES
参数决定结果集是单单返回有序集的成员,还是将有序集成员及其 score
值一起返回。 该选项自 Redis 2.0 版本起可用。
min
和 max
可以是 -inf
和 +inf
,这样一来,你就可以在不知道有序集的最低和最高 score
值的情况下,使用 ZRANGEBYSCORE 这类命令。
默认情况下,区间的取值使用闭区间 (小于等于或大于等于),你也可以通过给参数前增加 (
符号来使用可选的开区间 (小于或大于)。
举个例子:
ZRANGEBYSCORE zset (1 5
返回所有符合条件 1 < score <= 5
的成员,而
ZRANGEBYSCORE zset (5 (10
则返回所有符合条件 5 < score < 10
的成员。
指定区间内,带有 score
值(可选)的有序集成员的列表。
redis> ZADD salary 2500 jack # 测试数据
(integer) 0
redis> ZADD salary 5000 tom
(integer) 0
redis> ZADD salary 12000 peter
(integer) 0
redis> ZRANGEBYSCORE salary -inf +inf # 显示整个有序集
1) "jack"
2) "tom"
3) "peter"
redis> ZRANGEBYSCORE salary -inf +inf WITHSCORES # 显示整个有序集及成员的 score 值
1) "jack"
2) "2500"
3) "tom"
4) "5000"
5) "peter"
6) "12000"
redis> ZRANGEBYSCORE salary -inf 5000 WITHSCORES # 显示工资 <=5000 的所有成员
1) "jack"
2) "2500"
3) "tom"
4) "5000"
redis> ZRANGEBYSCORE salary (5000 400000 # 显示工资大于 5000 小于等于 400000 的成员
1) "peter"
命令格式:ZREMRANGEBYRANK key start stop
可用版本: >= 2.0.0
时间复杂度: O(log(N)+M),
N
为有序集的基数,而M
为被移除成员的数量。
移除有序集 key
中,指定排名(rank)区间内的所有成员。
区间分别以下标参数 start
和 stop
指出,包含 start
和 stop
在内。
下标参数 start
和 stop
都以 0
为底,也就是说,以 0
表示有序集第一个成员,以 1
表示有序集第二个成员,以此类推。 你也可以使用负数下标,以 -1
表示最后一个成员, -2
表示倒数第二个成员,以此类推。
被移除成员的数量。
redis> ZADD salary 2000 jack
(integer) 1
redis> ZADD salary 5000 tom
(integer) 1
redis> ZADD salary 3500 peter
(integer) 1
redis> ZREMRANGEBYRANK salary 0 1 # 移除下标 0 至 1 区间内的成员
(integer) 2
redis> ZRANGE salary 0 -1 WITHSCORES # 有序集只剩下一个成员
1) "tom"
2) "5000"
命令格式:ZREMRANGEBYSCORE key min max
可用版本: >= 1.2.0
时间复杂度: O(log(N)+M),
N
为有序集的基数,而M
为被移除成员的数量。
移除有序集 key
中,所有 score
值介于 min
和 max
之间(包括等于 min
或 max
)的成员。
自版本2.1.6开始, score
值等于 min
或 max
的成员也可以不包括在内,详情请参见 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 命令。
被移除成员的数量。
redis> ZRANGE salary 0 -1 WITHSCORES # 显示有序集内所有成员及其 score 值
1) "tom"
2) "2000"
3) "peter"
4) "3500"
5) "jack"
6) "5000"
redis> ZREMRANGEBYSCORE salary 1500 3500 # 移除所有薪水在 1500 到 3500 内的员工
(integer) 2
redis> ZRANGE salary 0 -1 WITHSCORES # 剩下的有序集成员
1) "jack"
2) "5000"
命令格式:ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
可用版本: >= 2.2.0
时间复杂度: O(log(N)+M),
N
为有序集的基数,M
为结果集的基数。
返回有序集 key
中, score
值介于 max
和 min
之间(默认包括等于 max
或 min
)的所有的成员。有序集成员按 score
值递减(从大到小)的次序排列。
具有相同 score
值的成员按字典序的逆序(reverse lexicographical order )排列。
除了成员按 score
值递减的次序排列这一点外, ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 命令一样。
指定区间内,带有 score
值(可选)的有序集成员的列表。
redis > ZADD salary 10086 jack
(integer) 1
redis > ZADD salary 5000 tom
(integer) 1
redis > ZADD salary 7500 peter
(integer) 1
redis > ZADD salary 3500 joe
(integer) 1
redis > ZREVRANGEBYSCORE salary +inf -inf # 逆序排列所有成员
1) "jack"
2) "peter"
3) "tom"
4) "joe"
redis > ZREVRANGEBYSCORE salary 10000 2000 # 逆序排列薪水介于 10000 和 2000 之间的成员
1) "peter"
2) "tom"
3) "joe"
命令格式:AUTH password
可用版本: >= 1.0.0
时间复杂度: O(1)
通过设置配置文件中 requirepass
项的值(使用命令 CONFIG SET requirepass password
),可以使用密码来保护 Redis 服务器。
如果开启了密码保护的话,在每次连接 Redis 服务器之后,就要使用 AUTH
命令解锁,解锁之后才能使用其他 Redis 命令。
如果 AUTH
命令给定的密码 password
和配置文件中的密码相符的话,服务器会返回 OK
并开始接受命令输入。
另一方面,假如密码不匹配的话,服务器将返回一个错误,并要求客户端需重新输入密码。
Warning
因为 Redis 高性能的特点,在很短时间内尝试猜测非常多个密码是有可能的,因此请确保使用的密码足够复杂和足够长,以免遭受密码猜测攻击。
密码匹配时返回 OK
,否则返回一个错误。
# 设置密码
redis> CONFIG SET requirepass secret_password # 将密码设置为 secret_password
OK
redis> QUIT # 退出再连接,让新密码对客户端生效
[huangz@mypad]$ redis
redis> PING # 未验证密码,操作被拒绝
(error) ERR operation not permitted
redis> AUTH wrong_password_testing # 尝试输入错误的密码
(error) ERR invalid password
redis> AUTH secret_password # 输入正确的密码
OK
redis> PING # 密码验证成功,可以正常操作命令了
PONG
# 清空密码
redis> CONFIG SET requirepass "" # 通过将密码设为空字符来清空密码
OK
redis> QUIT
$ redis # 重新进入客户端
redis> PING # 执行命令不再需要密码,清空密码操作成功
PONG
命令格式:DEL key [key …]
可用版本: >= 1.0.0
时间复杂度:O(N),
N
为被删除的key
的数量,其中删除单个字符串类型的key
,时间复杂度为O(1);删除单个列表、集合、有序集合或哈希表类型的key
,时间复杂度为O(M),M
为以上数据结构内的元素数量。
删除给定的一个或多个 key
。
不存在的 key
会被忽略。
被删除 key
的数量。
# 删除单个 key
redis> SET name huangz
OK
redis> DEL name
(integer) 1
# 删除一个不存在的 key
redis> EXISTS phone
(integer) 0
redis> DEL phone # 失败,没有 key 被删除
(integer) 0
# 同时删除多个 key
redis> SET name "redis"
OK
redis> SET type "key-value store"
OK
redis> SET website "redis.com"
OK
redis> DEL name type website
(integer) 3
命令格式:MOVE
可用版本: >= 1.0.0
时间复杂度: O(1)
将当前数据库的 key
移动到给定的数据库 db
当中。
如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key
,或者 key
不存在于当前数据库,那么 MOVE
没有任何效果。
因此,也可以利用这一特性,将 MOVE 当作锁(locking)原语(primitive)。
移动成功返回 1
,失败则返回 0
。
# key 存在于当前数据库
redis> SELECT 0 # redis默认使用数据库 0,为了清晰起见,这里再显式指定一次。
OK
redis> SET song "secret base - Zone"
OK
redis> MOVE song 1 # 将 song 移动到数据库 1
(integer) 1
redis> EXISTS song # song 已经被移走
(integer) 0
redis> SELECT 1 # 使用数据库 1
OK
redis:1> EXISTS song # 证实 song 被移到了数据库 1 (注意命令提示符变成了"redis:1",表明正在使用数据库 1)
(integer) 1
# 当 key 不存在的时候
redis:1> EXISTS fake_key
(integer) 0
redis:1> MOVE fake_key 0 # 试图从数据库 1 移动一个不存在的 key 到数据库 0,失败
(integer) 0
redis:1> select 0 # 使用数据库0
OK
redis> EXISTS fake_key # 证实 fake_key 不存在
(integer) 0
# 当源数据库和目标数据库有相同的 key 时
redis> SELECT 0 # 使用数据库0
OK
redis> SET favorite_fruit "banana"
OK
redis> SELECT 1 # 使用数据库1
OK
redis:1> SET favorite_fruit "apple"
OK
redis:1> SELECT 0 # 使用数据库0,并试图将 favorite_fruit 移动到数据库 1
OK
redis> MOVE favorite_fruit 1 # 因为两个数据库有相同的 key,MOVE 失败
(integer) 0
redis> GET favorite_fruit # 数据库 0 的 favorite_fruit 没变
"banana"
redis> SELECT 1
OK
redis:1> GET favorite_fruit # 数据库 1 的 favorite_fruit 也是
"apple"
命令格式:SCAN cursor [MATCH pattern] [COUNT count]
可用版本: >= 2.8.0
时间复杂度:增量式迭代命令每次执行的复杂度为 O(1) , 对数据集进行一次完整迭代的复杂度为 O(N) , 其中 N 为数据集中的元素数量。
SCAN
命令及其相关的 SSCAN
命令、 HSCAN
命令和 ZSCAN
命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements):
SCAN
命令用于迭代当前数据库中的数据库键。SSCAN
命令用于迭代集合键中的元素。HSCAN
命令用于迭代哈希键中的键值对。ZSCAN
命令用于迭代有序集合中的元素(包括元素成员和元素分值)。以上列出的四个命令都支持增量式迭代, 它们每次执行都只会返回少量元素, 所以这些命令可以用于生产环境, 而不会出现像 KEYS
命令、 SMEMBERS
命令带来的问题 —— 当 KEYS
命令被用于处理一个大的数据库时, 又或者 SMEMBERS
命令被用于处理一个大的集合键时, 它们可能会阻塞服务器达数秒之久。
不过, 增量式迭代命令也不是没有缺点的: 举个例子, 使用 SMEMBERS
命令可以返回集合键当前包含的所有元素, 但是对于 SCAN
这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证 (offer limited guarantees about the returned elements)。
因为 SCAN
、 SSCAN
、 HSCAN
和 ZSCAN
四个命令的工作方式都非常相似, 所以这个文档会一并介绍这四个命令, 但是要记住:
SSCAN
命令、 HSCAN
命令和 ZSCAN
命令的第一个参数总是一个数据库键。SCAN
命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。SCAN
命令是一个基于游标的迭代器(cursor based iterator): SCAN
命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN
命令的游标参数, 以此来延续之前的迭代过程。
当 SCAN
命令的游标参数被设置为 0
时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0
的游标时, 表示迭代已结束。
以下是一个 SCAN
命令的迭代过程示例:
redis 127.0.0.1:6379> scan 0
1) "17"
2) 1) "key:12"
2) "key:8"
3) "key:4"
4) "key:14"
5) "key:16"
6) "key:17"
7) "key:15"
8) "key:10"
9) "key:3"
10) "key:7"
11) "key:1"
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
2) "key:18"
3) "key:0"
4) "key:2"
5) "key:19"
6) "key:13"
7) "key:6"
8) "key:9"
9) "key:11"
在上面这个例子中, 第一次迭代使用 0
作为游标, 表示开始一次新的迭代。
第二次迭代使用的是第一次迭代时返回的游标, 也即是命令回复第一个元素的值 —— 17
。
从上面的示例可以看到, SCAN
命令的回复是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素。
在第二次调用 SCAN
命令时, 命令返回了游标 0
, 这表示迭代已经结束, 整个数据集(collection)已经被完整遍历过了。
以 0
作为游标开始一次新的迭代, 一直调用 SCAN
命令, 直到命令返回游标 0
, 我们称这个过程为一次完整遍历(full iteration)。
SCAN
命令, 以及其他增量式迭代命令, 在进行完整遍历的情况下可以为用户带来以下保证: 从完整遍历开始直到完整遍历结束期间, 一直存在于数据集内的所有元素都会被完整遍历返回; 这意味着, 如果有一个元素, 它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中, 那么 SCAN
命令总会在某次迭代中将这个元素返回给用户。
然而因为增量式命令仅仅使用游标来记录迭代状态, 所以这些命令带有以下缺点:
增量式迭代命令并不保证每次执行都返回某个给定数量的元素。
增量式命令甚至可能会返回零个元素, 但只要命令返回的游标不是 0
, 应用程序就不应该将迭代视作结束。
不过命令返回的元素数量总是符合一定规则的, 在实际中:
最后, 用户可以通过增量式迭代命令提供的 COUNT
选项来指定每次迭代返回元素的最大值。
虽然增量式迭代命令不保证每次迭代所返回的元素数量, 但我们可以使用 COUNT
选项, 对命令的行为进行一定程度上的调整。
基本上, COUNT
选项的作用就是让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素。
虽然 COUNT
选项只是对增量式迭代命令的一种提示(hint), 但是在大多数情况下, 这种提示都是有效的。
COUNT
参数的默认值为 10
。MATCH
选项, 那么命令返回的元素数量通常和 COUNT
选项指定的一样, 或者比 COUNT
选项指定的数量稍多一些。COUNT
选项指定的值, 在第一次迭代就将数据集包含的所有元素都返回给用户。Note
并非每次迭代都要使用相同的 COUNT
值。
用户可以在每次迭代中按自己的需要随意改变 COUNT
值, 只要记得将上次迭代返回的游标用到下次迭代里面就可以了。
和 KEYS
命令一样, 增量式迭代命令也可以通过提供一个 glob 风格的模式参数, 让命令只返回和给定模式相匹配的元素, 这一点可以通过在执行增量式迭代命令时, 通过给定 MATCH
参数来实现。
以下是一个使用 MATCH
选项进行迭代的示例:
redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood
(integer) 6
redis 127.0.0.1:6379> sscan myset 0 match f*
1) "0"
2) 1) "foo"
2) "feelsgood"
3) "foobar"
需要注意的是, 对元素的模式匹配工作是在命令从数据集中取出元素之后, 向客户端返回元素之前的这段时间内进行的, 所以如果被迭代的数据集中只有少量元素和模式相匹配, 那么迭代命令或许会在多次执行中都不返回任何元素。
以下是这种情况的一个例子:
redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2) 1) "key:611"
2) "key:711"
3) "key:118"
4) "key:117"
5) "key:311"
6) "key:112"
7) "key:111"
8) "key:110"
9) "key:113"
10) "key:211"
11) "key:411"
12) "key:115"
13) "key:116"
14) "key:114"
15) "key:119"
16) "key:811"
17) "key:511"
18) "key:11"
如你所见, 以上的大部分迭代都不返回任何元素。
在最后一次迭代, 我们通过将 COUNT
选项的参数设置为 1000
, 强制命令为本次迭代扫描更多元素, 从而使得命令返回的元素也变多了。
在同一时间, 可以有任意多个客户端对同一数据集进行迭代, 客户端每次执行迭代都需要传入一个游标, 并在迭代执行之后获得一个新的游标, 而这个游标就包含了迭代的所有状态, 因此, 服务器无须为迭代记录任何状态。
因为迭代的所有状态都保存在游标里面, 而服务器无须为迭代保存任何状态, 所以客户端可以在中途停止一个迭代, 而无须对服务器进行任何通知。
即使有任意数量的迭代在中途停止, 也不会产生任何问题。
使用间断的(broken)、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃, 但可能会让命令产生未定义的行为。
未定义行为指的是, 增量式命令对返回值所做的保证可能会不再为真。
只有两种游标是合法的:
0
。增量式迭代命令所使用的算法只保证在数据集的大小有界(bounded)的情况下, 迭代才会停止, 换句话说, 如果被迭代数据集的大小不断地增长的话, 增量式迭代命令可能永远也无法完成一次完整迭代。
从直觉上可以看出, 当一个数据集不断地变大时, 想要访问这个数据集中的所有元素就需要做越来越多的工作, 能否结束一个迭代取决于用户执行迭代的速度是否比数据集增长的速度更快。
SCAN
命令、 SSCAN
命令、 HSCAN
命令和 ZSCAN
命令都返回一个包含两个元素的 multi-bulk 回复: 回复的第一个元素是字符串表示的无符号 64 位整数(游标), 回复的第二个元素是另一个 multi-bulk 回复, 这个 multi-bulk 回复包含了本次被迭代的元素。
SCAN
命令返回的每个元素都是一个数据库键。
SSCAN
命令返回的每个元素都是一个集合成员。
HSCAN
命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
ZSCAN
命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。
命令格式:SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination]
可用版本: >= 1.0.0
时间复杂度: O(N+M*log(M)),
N
为要排序的列表或集合内的元素数量,M
为要返回的元素数量。如果只是使用 SORT 命令的GET
选项获取数据而没有进行排序,时间复杂度 O(N)。
返回或保存给定列表、集合、有序集合 key
中经过排序的元素。
排序默认以数字作为对象,值被解释为双精度浮点数,然后进行比较。
最简单的 SORT 使用方法是 SORT key
和 SORT key DESC
:
SORT key
返回键值从小到大排序的结果。SORT key DESC
返回键值从大到小排序的结果。假设 today_cost
列表保存了今日的开销金额, 那么可以用 SORT 命令对它进行排序:
# 开销金额列表
redis> LPUSH today_cost 30 1.5 10 8
(integer) 4
# 排序
redis> SORT today_cost
1) "1.5"
2) "8"
3) "10"
4) "30"
# 逆序排序
redis 127.0.0.1:6379> SORT today_cost DESC
1) "30"
2) "10"
3) "8"
4) "1.5"
因为 SORT 命令默认排序对象为数字, 当需要对字符串进行排序时, 需要显式地在 SORT 命令之后添加 ALPHA
修饰符:
# 网址
redis> LPUSH website "www.reddit.com"
(integer) 1
redis> LPUSH website "www.slashdot.com"
(integer) 2
redis> LPUSH website "www.infoq.com"
(integer) 3
# 默认(按数字)排序
redis> SORT website
1) "www.infoq.com"
2) "www.slashdot.com"
3) "www.reddit.com"
# 按字符排序
redis> SORT website ALPHA
1) "www.infoq.com"
2) "www.reddit.com"
3) "www.slashdot.com"
如果系统正确地设置了 LC_COLLATE
环境变量的话,Redis能识别 UTF-8
编码。
排序之后返回元素的数量可以通过 LIMIT
修饰符进行限制, 修饰符接受 offset
和 count
两个参数:
offset
指定要跳过的元素数量。count
指定跳过 offset
个指定的元素之后,要返回多少个对象。以下例子返回排序结果的前 5 个对象( offset
为 0
表示没有元素被跳过)。
# 添加测试数据,列表值为 1 指 10
redis 127.0.0.1:6379> RPUSH rank 1 3 5 7 9
(integer) 5
redis 127.0.0.1:6379> RPUSH rank 2 4 6 8 10
(integer) 10
# 返回列表中最小的 5 个值
redis 127.0.0.1:6379> SORT rank LIMIT 0 5
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
可以组合使用多个修饰符。以下例子返回从大到小排序的前 5 个对象。
redis 127.0.0.1:6379> SORT rank LIMIT 0 5 DESC
1) "10"
2) "9"
3) "8"
4) "7"
5) "6"
可以使用外部 key
的数据作为权重,代替默认的直接对比键值的方式来进行排序。
假设现在有用户数据如下:
以下代码将数据输入到 Redis 中:
# admin
redis 127.0.0.1:6379> LPUSH uid 1
(integer) 1
redis 127.0.0.1:6379> SET user_name_1 admin
OK
redis 127.0.0.1:6379> SET user_level_1 9999
OK
# jack
redis 127.0.0.1:6379> LPUSH uid 2
(integer) 2
redis 127.0.0.1:6379> SET user_name_2 jack
OK
redis 127.0.0.1:6379> SET user_level_2 10
OK
# peter
redis 127.0.0.1:6379> LPUSH uid 3
(integer) 3
redis 127.0.0.1:6379> SET user_name_3 peter
OK
redis 127.0.0.1:6379> SET user_level_3 25
OK
# mary
redis 127.0.0.1:6379> LPUSH uid 4
(integer) 4
redis 127.0.0.1:6379> SET user_name_4 mary
OK
redis 127.0.0.1:6379> SET user_level_4 70
OK
默认情况下, SORT uid
直接按 uid
中的值排序:
redis 127.0.0.1:6379> SORT uid
1) "1" # admin
2) "2" # jack
3) "3" # peter
4) "4" # mary
通过使用 BY
选项,可以让 uid
按其他键的元素来排序。
比如说, 以下代码让 uid
键按照 user_level_{uid}
的大小来排序:
redis 127.0.0.1:6379> SORT uid BY user_level_*
1) "2" # jack , level = 10
2) "3" # peter, level = 25
3) "4" # mary, level = 70
4) "1" # admin, level = 9999
user_level_*
是一个占位符, 它先取出 uid
中的值, 然后再用这个值来查找相应的键。
比如在对 uid
列表进行排序时, 程序就会先取出 uid
的值 1
、 2
、 3
、 4
, 然后使用 user_level_1
、 user_level_2
、 user_level_3
和 user_level_4
的值作为排序 uid
的权重。
使用 GET
选项, 可以根据排序的结果来取出相应的键值。
比如说, 以下代码先排序 uid
, 再取出键 user_name_{uid}
的值:
redis 127.0.0.1:6379> SORT uid GET user_name_*
1) "admin"
2) "jack"
3) "peter"
4) "mary"
通过组合使用 BY
和 GET
, 可以让排序结果以更直观的方式显示出来。
比如说, 以下代码先按 user_level_{uid}
来排序 uid
列表, 再取出相应的 user_name_{uid}
的值:
redis 127.0.0.1:6379> SORT uid BY user_level_* GET user_name_*
1) "jack" # level = 10
2) "peter" # level = 25
3) "mary" # level = 70
4) "admin" # level = 9999
现在的排序结果要比只使用 SORT uid BY user_level_*
要直观得多。
可以同时使用多个 GET
选项, 获取多个外部键的值。
以下代码就按 uid
分别获取 user_level_{uid}
和 user_name_{uid}
:
redis 127.0.0.1:6379> SORT uid GET user_level_* GET user_name_*
1) "9999" # level
2) "admin" # name
3) "10"
4) "jack"
5) "25"
6) "peter"
7) "70"
8) "mary"
GET
有一个额外的参数规则,那就是 —— 可以用 #
获取被排序键的值。
以下代码就将 uid
的值、及其相应的 user_level_*
和 user_name_*
都返回为结果:
redis 127.0.0.1:6379> SORT uid GET # GET user_level_* GET user_name_*
1) "1" # uid
2) "9999" # level
3) "admin" # name
4) "2"
5) "10"
6) "jack"
7) "3"
8) "25"
9) "peter"
10) "4"
11) "70"
12) "mary"
通过将一个不存在的键作为参数传给 BY
选项, 可以让 SORT
跳过排序操作, 直接返回结果:
redis 127.0.0.1:6379> SORT uid BY not-exists-key
1) "4"
2) "3"
3) "2"
4) "1"
这种用法在单独使用时,没什么实际用处。
不过,通过将这种用法和 GET
选项配合, 就可以在不排序的情况下, 获取多个外部键, 相当于执行一个整合的获取操作(类似于 SQL 数据库的 join
关键字)。
以下代码演示了,如何在不引起排序的情况下,使用 SORT
、 BY
和 GET
获取多个外部键:
redis 127.0.0.1:6379> SORT uid BY not-exists-key GET # GET user_level_* GET user_name_*
1) "4" # id
2) "70" # level
3) "mary" # name
4) "3"
5) "25"
6) "peter"
7) "2"
8) "10"
9) "jack"
10) "1"
11) "9999"
12) "admin"
除了可以将字符串键之外, 哈希表也可以作为 GET
或 BY
选项的参数来使用。
比如说,对于前面给出的用户信息表:
我们可以不将用户的名字和级别保存在 user_name_{uid}
和 user_level_{uid}
两个字符串键中, 而是用一个带有 name
域和 level
域的哈希表 user_info_{uid}
来保存用户的名字和级别信息:
redis 127.0.0.1:6379> HMSET user_info_1 name admin level 9999
OK
redis 127.0.0.1:6379> HMSET user_info_2 name jack level 10
OK
redis 127.0.0.1:6379> HMSET user_info_3 name peter level 25
OK
redis 127.0.0.1:6379> HMSET user_info_4 name mary level 70
OK
之后, BY
和 GET
选项都可以用 key->field
的格式来获取哈希表中的域的值, 其中 key
表示哈希表键, 而 field
则表示哈希表的域:
redis 127.0.0.1:6379> SORT uid BY user_info_*->level
1) "2"
2) "3"
3) "4"
4) "1"
redis 127.0.0.1:6379> SORT uid BY user_info_*->level GET user_info_*->name
1) "jack"
2) "peter"
3) "mary"
4) "admin"
默认情况下, SORT 操作只是简单地返回排序结果,并不进行任何保存操作。
通过给 STORE
选项指定一个 key
参数,可以将排序结果保存到给定的键上。
如果被指定的 key
已存在,那么原有的值将被排序结果覆盖。
# 测试数据
redis 127.0.0.1:6379> RPUSH numbers 1 3 5 7 9
(integer) 5
redis 127.0.0.1:6379> RPUSH numbers 2 4 6 8 10
(integer) 10
redis 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"
redis 127.0.0.1:6379> SORT numbers STORE sorted-numbers
(integer) 10
# 排序后的结果
redis 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"
可以通过将 SORT 命令的执行结果保存,并用 EXPIRE key seconds 为结果设置生存时间,以此来产生一个 SORT 操作的结果缓存。
这样就可以避免对 SORT 操作的频繁调用:只有当结果集过期时,才需要再调用一次 SORT 操作。
另外,为了正确实现这一用法,你可能需要加锁以避免多个客户端同时进行缓存重建(也就是多个客户端,同一时间进行 SORT 操作,并保存为结果集),具体参见 SETNX key value 命令。
没有使用 STORE
参数,返回列表形式的排序结果。 使用 STORE
参数,返回排序结果的元素数量。
命令格式:TYPE key
可用版本: >= 1.0.0
时间复杂度: O(1)
返回 key
所储存的值的类型。
none
(key不存在)string
(字符串)list
(列表)set
(集合)zset
(有序集)hash
(哈希表)stream
(流)# 字符串
redis> SET weather "sunny"
OK
redis> TYPE weather
string
# 列表
redis> LPUSH book_list "programming in scala"
(integer) 1
redis> TYPE book_list
list
# 集合
redis> SADD pat "dog"
(integer) 1
redis> TYPE pat
set
命令格式:DBSIZE
可用版本: >= 1.0.0
时间复杂度: O(1)
返回当前数据库的 key 的数量。
当前数据库的 key 的数量。
redis> DBSIZE
(integer) 5
redis> SET new_key "hello_moto" # 增加一个 key 试试
OK
redis> DBSIZE
(integer) 6
命令格式:EXISTS key
可用版本: >= 1.0.0
时间复杂度: O(1)
检查给定 key
是否存在。
若 key
存在,返回 1
,否则返回 0
。
redis> SET db "redis"
OK
redis> EXISTS db
(integer) 1
redis> DEL db
(integer) 1
redis> EXISTS db
(integer) 0
命令格式:RENAME key newkey
可用版本: >= 1.0.0
时间复杂度: O(1)
将 key
改名为 newkey
。
当 key
和 newkey
相同,或者 key
不存在时,返回一个错误。
当 newkey
已经存在时, RENAME 命令将覆盖旧值。
改名成功时提示 OK
,失败时候返回一个错误。
# key 存在且 newkey 不存在
redis> SET message "hello world"
OK
redis> RENAME message greeting
OK
redis> EXISTS message # message 不复存在
(integer) 0
redis> EXISTS greeting # greeting 取而代之
(integer) 1
# 当 key 不存在时,返回错误
redis> RENAME fake_key never_exists
(error) ERR no such key
# newkey 已存在时, RENAME 会覆盖旧 newkey
redis> SET pc "lenovo"
OK
redis> SET personal_computer "dell"
OK
redis> RENAME pc personal_computer
OK
redis> GET pc
(nil)
redis:1> GET personal_computer # 原来的值 dell 被覆盖了
"lenovo"
命令格式:SELECT index
可用版本: >= 1.0.0
时间复杂度: O(1)
切换到指定的数据库,数据库索引号 index
用数字值指定,以 0
作为起始索引值。
默认使用 0
号数据库。
OK
redis> SET db_number 0 # 默认使用 0 号数据库
OK
redis> SELECT 1 # 使用 1 号数据库
OK
redis[1]> GET db_number # 已经切换到 1 号数据库,注意 Redis 现在的命令提示符多了个 [1]
(nil)
redis[1]> SET db_number 1
OK
redis[1]> GET db_number
"1"
redis[1]> SELECT 3 # 再切换到 3 号数据库
OK
redis[3]> # 提示符从 [1] 改变成了 [3]
命令格式:FLUSHDB
可用版本: >= 1.0.0
时间复杂度: O(1)
清空当前数据库中的所有 key。
此命令从不失败。
总是返回 OK
。
redis> DBSIZE # 清空前的 key 数量
(integer) 4
redis> FLUSHDB
OK
redis> DBSIZE # 清空后的 key 数量
(integer) 0
命令格式:FLUSHALL
可用版本: >= 1.0.0
时间复杂度: O(N)
清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。
此命令从不失败。
总是返回 OK
。
redis> DBSIZE # 0 号数据库的 key 数量
(integer) 9
redis> SELECT 1 # 切换到 1 号数据库
OK
redis[1]> DBSIZE # 1 号数据库的 key 数量
(integer) 6
redis[1]> flushall # 清空所有数据库的所有 key
OK
redis[1]> DBSIZE # 不但 1 号数据库被清空了
(integer) 0
redis[1]> SELECT 0 # 0 号数据库(以及其他所有数据库)也一样
OK
redis> DBSIZE
(integer) 0
命令格式:RENAMENX key newkey
可用版本: >= 1.0.0
时间复杂度: O(1)
当且仅当 newkey
不存在时,将 key
改名为 newkey
。
当 key
不存在时,返回一个错误。
修改成功时,返回 1
; 如果 newkey
已经存在,返回 0
。
# newkey 不存在,改名成功
redis> SET player "MPlyaer"
OK
redis> EXISTS best_player
(integer) 0
redis> RENAMENX player best_player
(integer) 1
# newkey存在时,失败
redis> SET animal "bear"
OK
redis> SET favorite_animal "butterfly"
OK
redis> RENAMENX animal favorite_animal
(integer) 0
redis> get animal
"bear"
redis> get favorite_animal
"butterfly"
命令格式:RANDOMKEY
可用版本: >= 1.0.0
时间复杂度: O(1)
从当前数据库中随机返回(不删除)一个 key
。
当数据库不为空时,返回一个 key
。 当数据库为空时,返回 nil
。
# 数据库不为空
redis> MSET fruit "apple" drink "beer" food "cookies" # 设置多个 key
OK
redis> RANDOMKEY
"fruit"
redis> RANDOMKEY
"food"
redis> KEYS * # 查看数据库内所有key,证明 RANDOMKEY 并不删除 key
1) "food"
2) "drink"
3) "fruit"
# 数据库为空
redis> FLUSHDB # 删除当前数据库所有 key
OK
redis> RANDOMKEY
(nil)