Redis是一个主要由Salvatore Sanfilippo(Antirez)开发的开源内存数据结构存储器,经常用作数据库、缓存以及消息代理等。
Redis 是完全开源免费的,遵守 BSD 协议,是一个灵活的高性能 key-value 数据结构存储,可以用来作为数据库、缓存和消息队列。
Redis 比其他 key-value 缓存产品有以下三个特点:
下载地址:https://redis.com.cn/download.html
可以下载安装版的或者redis服务压缩包
服务端:打开压缩包目录所在的命令行,执行redis-server
客户端:重新在打开一个压缩包目录所在的命令行,执行redis-cli
测试客户端和服务器之间的连接是否正常,在客户端执行命令:ping
,那么服务器在连接正常的情况下,将向客户端返回PONG作为回复
127.0.0.1:6379> PING
PONG
如果用户给定了可选的消息,那么服务器将原封不动地向客户端返回该消息:
127.0.0.1:6379> PING "hello world"
"hello world"
另外,如果服务器与客户端的连接不正常,那么客户端将返回一个错误:
客户端未能连接服务器,返回一个连接错误
127.0.0.1:6379> PING
Could not connect to Redis at 127.0.0.1:6379: Connection refused
或者:
127.0.0.1:6379> ping
Could not connect to Redis at 127.0.0.1:6379: 由于目标计算机积极拒绝,无法连接。
(2.05s)
not connected>
字符串(string)键是Redis
最基本的键值对类型,这种类型的键值对会在数据库中把单独的一个键和单独的一个值关联起来,被关联的键和值既可以是普通的文字数据,也可以是图片、视频、音频、压缩文件等更为复杂的二进制数据。
创建字符串键最常用的方法就是使用SET
命令,这个命令可以为一个字符串键设置相应的值。SET key value
redis> SET number "10086"
OK
用户可以通过向SET
命令提供可选的NX
选项或者XX
选项来指示SET
命令是否要覆盖一个已经存在的值:SET key value [NX|XX]
,如果用户在执行SET
命令时给定了NX
选项,那么SET
命令只会在键没有值的情况下执行设置操作,并返回OK
表示设置成功;如果键已经存在,那么SET
命令将放弃执行设置操作,并返回空值nil
表示设置失败。
以下代码展示了带有NX选项的SET
命令的行为:
redis> SET password "123456" NX
OK -- 对尚未有值的password键进行设置,成功
redis> SET password "999999" NX
(nil) -- password键已经有了值,设置失败
如果我们对一个没有值的键mongodb-homepage执行以下SET
命令,那么命令将因为XX
选项的作用而放弃执行设置操作,相反,如果我们对一个已经有值的键执行带有XX
选项的SET
命令,那么命令将使用新值去覆盖已有的旧值.
redis> SET mongodb-homepage "mongodb.com" XX
(nil)
redis> SET mysql-homepage "mysql.org"
OK -- 为键mysql-homepage设置一个值
redis> SET mysql-homepage "mysql.com" XX
OK -- 对键的值进行更新
用户可以使用GET
命令从数据库中获取指定字符串键的值:GET key
redis> GET number
"10086"
GETSET
命令就像GET
命令和SET
命令的组合版本,GETSET
首先获取字符串键目前已有的值,接着为键设置新值,最后把之前获取到的旧值返回给用户:GETSET key new_value
redis> GET number -- number键现在的值为"10086"
"10086"
redis> GETSET number "12345"
"10086" -- 返回旧值
redis> GET number -- number键的值已被更新为"12345"
"12345"
除了SET
命令和GETSET
命令之外,Redis
还提供了MSET
命令用于对字符串键进行设置。与SET
命令和GETSET
命令只能设置单个字符串键不同,MSET
命令可以一次为多个字符串键设置值:MSET key value [key value ...]
redis> MSET message "hello world" number "10086" homepage "redis.io"
OK
redis> GET message
"hello world"
redis> GET number
"10086"
redis> GET homepage
"redis.io"
MGET
命令就是一个多键版本的GET
命令,MGET
接受一个或多个字符串键作为参数,并返回这些字符串键的值:MGET key [key ...]
redis> MGET message number homepage
1) "hello world" -- message键的值
2) "10086" -- number键的值
3) "redis.io" -- homepage键的值
MSETNX
命令与MSET
命令一样,都可以对多个字符串键进行设置:MSETNX key value [key value ...]
redis> mget num1 num2 num3 msg
1) (nil)
2) (nil)
3) (nil)
4) "hello world" -- 键msg已存在
redis> msetnx num1 1 num2 2 num3 3 msg "hello"
(integer) 0 -- 因为键msg已存在,所以MSETNX未能执行设置操作
redis> MGET k1 k2 k3 -- 各个键的值没有变化
1) (nil)
2) (nil)
3) (nil)
redis> MSETNX k1 "one" k2 "two" k3 "three"
(integer) 1 -- 所有给定键都不存在,成功执行设置操作
通过对字符串键执行STRLEN
命令,用户可以取得字符串键存储的值的字节长度:STRLEN key
redis> GET number
"10086"
redis> STRLEN number -- number键的值长5字节
(integer) 5
redis> GET msg
"hello world"
redis> STRLEN msg -- msg键的值长11字节
(integer) 11
通过使用GETRANGE
命令,用户可以获取字符串值从start
索引开始,直到end
索引为止的所有内容:GETRANGE key start end
redis> GETRANGE msg 0 4 -- 获取字符串值索引0至索引4上的内容
"hello"
通过使用SETRANGE
命令,用户可以将字符串键的值从索引index
开始的部分替换为指定的新内容,被替换内容的长度取决于新内容的长度:SETRANGE key index substitute
redis> GET message
"hello world"
redis> SETRANGE message 6 "Redis"
(integer) 11 -- 字符串值当前的长度为11字节
redis> GET message
"hello Redis"
redis> SETRANGE message 5 ", this is a message send from peter."
(integer) 41 -- 替换部分比原字符串长
redis> GET message
"hello, this is a message send from peter."
redis> GET greeting
"hello"
redis> SETRANGE greeting 10 "world"
(integer) 15 -- 替换开始的索引超出原字符串的长度
redis> GET greeting
"hello\x00\x00\x00\x00\x00world"
通过调用APPEND
命令,用户可以将给定的内容追加到字符串键已有值的末尾:APPEND key suffix
redis> GET description
"Redis"
redis> APPEND description " is a database"
(integer) 19 -- 追加操作执行完毕之后,值的长度
redis> GET description
"Redis is a database"
每当用户将一个值存储到字符串键里面的时候,Redis都会对这个值进行检测,如果这个值能够被解释为以下两种类型的其中一种,那么Redis就会把这个值当作数字来处理:
当字符串键存储的值能够被Redis
解释为整数时,用户就可以通过INCRBY
命令和DECRBY
命令对被存储的整数值执行加法或减法操作。INCRBY
命令用于为整数值加上指定的整数增量,并返回键在执行加法操作之后的值:INCRBY key increment
redis> SET number 100
OK
redis> INCRBY number 300 -- 将键的值加上300
(integer) 400
redis> GET number
"400"
与INCRBY
命令的作用正好相反,DECRBY
命令用于为整数值减去指定的整数减量,并返回键在执行减法操作之后的值:DECRBY key increment
redis> SET number 10086
OK
redis> DECRBY number 300 -- 将键的值减去300
(integer) 9786
redis> GET number
"9786"
因为对整数值执行加1操作或减1操作的场景经常会出现,所以为了能够更方便地执行这两个操作,Redis分别提供了用于执行加1操作的INCR命令以及用于执行减1操作的DECR命令。INCR key
,DECR key
redis> SET counter 100
OK
redis> INCR counter -- 对整数值执行加1操作
(integer) 101
redis> DECR counter -- 对整数值执行减1操作
(integer) 100
除了用于执行整数加法操作的INCR
命令以及INCRBY
命令之外,Redis
还提供了用于执行浮点数加法操作的INCRBYFLOAT
命令:INCRBYFLOAT key increment
redis> SET decimal 3.14 -- 一个存储着浮点数值的键
OK
redis> GET decimal
"3.14"
redis> INCRBYFLOAT decimal 2.55 -- 将键decimal的值加上2.55
"5.69"
redis> GET decimal
"5.69"
INCRBYFLOAT
命令传入负数增量来执行浮点数减法操作。INCRBYFLOAT
命令处理浮点数的时候,命令最多只会保留计算结果小数点后的17位数字,超过这个范围的小数将被截断.Redis的散列键会将一个键和一个散列在数据库里关联起来,用户可以在散列中为任意多个字段(field)设置值。与字符串键一样,散列的字段和值既可以是文本数据,也可以是二进制数据。
用户可以通过执行HSET
命令为散列中的指定字段设置值:HSET hash field value
redis> HSET article::10086 title "greeting"
(integer) 1
HSETNX
命令的作用和HSET
命令的作用非常相似,它们之间的区别在于,HSETNX
命令只会在指定字段不存在的情况下执行设置操作:HSETNX hash field value
redis> HSETNX article::10086 view_count 100
(integer) 1
HGET
命令可以根据用户给定的字段,从散列中获取该字段的值:HGET hash field
redis> HGET article::10086 author
"peter"
与字符串键的INCRBY
命令一样,如果散列的字段里面存储着能够被Redis
解释为整数的数字,那么用户就可以使用HINCRBY
命令为该字段的值加上指定的整数增量:HINCRBY hash field increment
redis> HINCRBY article::10086 view_count 1
(integer) 101
HINCRBY
命令,从而达到对值执行减法计算的目的。HINCRBYFLOAT
命令的作用和HINCRBY
命令的作用类似,它们之间的主要区别在于HINCRBYFLOAT
命令不仅可以使用整数作为增量,还可以使用浮点数作为增量:HINCRBYFLOAT hash field increment
redis> HGET geo::peter longitude
"100.0099647"
redis> HINCRBYFLOAT geo::peter longitude 13.2 -- 将字段的值加上13.2
"113.2099647"
HINCRBYFLOAT
命令传入负值浮点数来实现减法操作。用户可以使用HSTRLEN
命令获取给定字段值的字节长度:HSTRLEN hash field
redis> HSTRLEN article::10086 title
(integer) 8 -- title字段的值"greeting"长8个字节
HEXISTS
命令可用于检查用户给定的字段是否存在于散列当中:HEXISTS hash field
redis> HEXISTS article::10086 content
(integer) 1
redis> HEXISTS article::10086 last_updated_at
(integer) 0 -- 不包含该字段
HDEL
命令用于删除散列中的指定字段及其相关联的值:HDEL hash field
redis> HDEL article::10086 author
(integer) 1
用户可以通过使用HLEN
命令获取给定散列包含的字段数量:HLEN hash
redis> HLEN article::10086
(integer) 4 -- 这个散列包含4个字段
用户可以使用HMSET
命令一次为散列中的多个字段设置值:HMSET hash field value [field value ...]
redis> HSET article::10086 title "greeting"
(integer) 1
通过使用HMGET
命令,用户可以一次从散列中获取多个字段的值:HMGET hash field [field ...]
redis> HMGET article::10086 author created_at
1) "peter" -- author字段的值
2) "1442744762.631885" -- created_at字段的值
Redis为散列提供了HKEYS、HVALS和HGETALL这3个命令,可以分别用于获取散列包含的所有字段、所有值以及所有字段和值:HKEYS hash
, HVALS hash
, HGETALL hash
redis> HKEYS article::10086
1) "title"
2) "content"
3) "author"
4) "created_at"
redis> HVALS article::10086
1) "greeting"
2) "hello world"
3) "peter"
4) "1442744762.631885"
redis> HGETALL article::10086
1) "title" -- 字段
2) "greeting" -- 字段的值
3) "content"
4) "hello world"
5) "author"
6) "peter"
7) "created_at"
8) "1442744762.631885"
Redis的列表(list)是一种线性的有序结构,可以按照元素被推入列表中的顺序来存储元素,这些元素既可以是文字数据,又可以是二进制数据,并且列表中的元素可以重复出现。
用户可以通过LPUSH
命令,将一个或多个元素推入给定列表的左端:LPUSH list item [item item ...]
redis> LPUSH todo "buy some milk"
(integer) 1 -- 列表现在包含1个元素
redis> LPUSH another-todo "buy some milk" "watch tv" "finish homework"
(integer) 3
LPUSH命令将按照元素给定的顺序,从左到右依次将所有给定元素推入列表左端。
RPUSH
命令和LPUSH
命令类似,这两个命令执行的都是元素推入操作,唯一区别就在于LPUSH
命令会将元素推入列表左端,而RPUSH
命令会将元素推入列表右端:RPUSH list item [item item ...]
redis> RPUSH todo "buy some milk"
(integer) 1 -- 列表现在包含1个元素
redis> RPUSH another-todo "buy some milk" "watch tv" "finish homework"
(integer) 3
RPUSH命令将按照元素给定的顺序,从左到右依次将所有给定元素推入列表右端。
当用户调用LPUSH命令或RPUSH命令尝试将元素推入列表的时候,如果给定的列表并不存在,那么命令将自动创建一个空列表,并将元素推入刚刚创建的列表中。
redis> LPUSH list1 "item1"
(integer) 1
redis> RPUSH list2 "item1"
(integer) 1
Redis还提供了LPUSHX命令和RPUSHX命令,只会在列表已经存在的情况下,将元素推入刚刚创建的列表中。
redis> LPUSHX list3 "item-x"
(integer) 0 -- 没有推入任何元素
redis> RPUSHX list3 "item-y"
(integer) 0 -- 没有推入任何元素
redis> LPUSH list3 "item1"
(integer) 1 -- 推入一个元素,使得列表变为非空
redis> LPUSHX list3 "item-x"
(integer) 2 -- 执行推入操作之后,列表包含2个元素
用户可以通过LPOP
命令移除位于列表最左端的元素,并将被移除的元素返回给用户:LPOP list
redis> LPOP todo
"finish homework"
用户可以通过RPOP
命令移除位于列表最右端的元素,并将被移除的元素返回给用户:RPOP list
redis> RPOP todo
"finish homework"
RPOPLPUSH
命令的行为和它的名字一样,首先使用RPOP
命令将源列表最右端的元素弹出,然后使用LPUSH
命令将被弹出的元素推入目标列表左端,使之成为目标列表的最左端元素:RPOPLPUSH source target
redis> RPUSH list1 "a" "b" "c" -- 创建两个示例列表list1和list2
(integer) 3
用户可以通过执行LLEN
命令来获取列表的长度,即列表包含的元素数量:LLEN list
redis> LLEN todo
(integer) 3
Redis
列表包含的每个元素都有与之对应的正数索引和负数索引:LINDEX list index
redis> LINDEX alphabets 0
"a"
用户除了可以使用LINDEX
命令获取给定索引上的单个元素之外,还可以使用LRANGE
命令获取给定索引范围上的多个元素:LRANGE list start end
redis> LRANGE alphabets 0 3 -- 获取列表索引0至索引3上的所有元素
1) "a" -- 位于索引0上的元素
2) "b" -- 位于索引1上的元素
3) "c" -- 位于索引2上的元素
4) "d" -- 位于索引3上的元素
用户可以通过LSET
命令,为列表的指定索引设置新元素:LSET list index new_element
redis> LRANGE todo 0 -1
1) "buy some milk"
2) "watch tv"
3) "finish homework"
通过使用LINSERT
命令,用户可以将一个新元素插入列表某个指定元素的前面或者后面:LINSERT list BEFORE|AFTER target_element new_element
redis> LRANGE lst 0 -1
1) "a"
2) "b"
3) "c"
LTRIM
命令接受一个列表和一个索引范围作为参数,并移除列表中位于给定索引范围之外的所有元素,只保留给定范围之内的元素:LTRIM list start end
redis> RPUSH alphabets "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k"
(integer) 11
redis> LTRIM alphabets 0 6
OK
redis> LRANGE alphabets 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
BLPOP
命令是带有阻塞功能的左端弹出操作,它接受任意多个列表以及一个秒级精度的超时时限作为参数:BLPOP list [list ...] timeout
redis> BLPOP alphabets 5 -- 尝试弹出alphabets列表的最左端元素,最多阻塞5s
1) "alphabets" -- 被弹出元素的来源列表
2) "a" -- 被弹出元素
例子:
A、B、C这3个客户端先后执行了BLPOP lst 10命令,并且都因为lst列表为空而被阻塞,如果在这些客户端被阻塞期间,客户端D执行了RPUSH lst"hello""world""again"命令,那么服务器首先会处理客户端A的BLPOP命令,并将被弹出的"hello"元素返回给它;接着处理客户端B的BLPOP命令,并将被弹出的"world"元素返回给它;最后处理客户端C的BLPOP命令,并将被弹出的"again"元素返回给它。
阻塞效果的范围:
BLPOP命令的阻塞效果只对执行这个命令的客户端有效,其他客户端以及Redis服务器本身并不会因为这个命令而被阻塞。
BRPOP
命令是带有阻塞功能的右端弹出操作,除了弹出的方向不同之外,其他方面都和BLPOP
命令一样:BRPOP list [list ...] timeout
redis> BRPOP queue1 queue2 queue3 10
1) "queue2" -- 被弹出元素的来源列表
2) "bye bye" -- 被弹出元素
BRPOPLPUSH
命令是RPOPLPUSH
命令的阻塞版本,BRPOPLPUSH
命令接受一个源列表、一个目标列表以及一个秒级精度的超时时限作为参数:BRPOPLPUSH source target timeout
client-1> LRANGE list3 0 -1
1) "hello"
client-1> LRANGE list4 0 -1
1) "a"
2) "b"
3) "c"
client-1> BRPOPLPUSH list3 list4 10
"hello"
client-1> LRANGE list3 0 -1
(empty list or set)
client-1> LRANGE list4 0 -1
1) "hello"
2) "a"
3) "b"
4) "c"
client-1> BRPOPLPUSH list3 list4 10
"world"
(1.42s) -- 被阻塞了1.42s
-- 由于list3为空,如果我们再次执行相同的BRPOPLPUSH命令,那么客户端client-1将被阻塞,直到我们从另一个客户端client-2向list3推入新元素为止
client-1> LRANGE list3 0 -1
(empty list or set)
client-1> LRANGE list4 0 -1
1) "world"
2) "hello"
3) "a"
4) "b"
5) "c"
Redis的集合(set)键允许用户将任意多个各不相同的元素存储到集合中,这些元素既可以是文本数据,也可以是二进制数据。虽然列表键也允许我们存储多个元素,但集合与列表有以下两个明显的区别:
通过使用SADD
命令,用户可以将一个或多个元素添加到集合中:SADD set element [element ...]
redis> SADD databases "MongoDB" "CouchDB"
(integer) 2 -- 集合新添加了2个元素
因为集合不存储相同的元素,所以用户在使用SADD命令向集合中添加元素的时候,SADD命令会自动忽略已存在的元素,只将不存在于集合的新元素添加到集合中。
通过使用SREM
命令,用户可以从集合中移除一个或多个已存在的元素:SREM set element [element ...]
redis> SREM databases "MS SQL" "Oracle" "CouchDB"
(integer) 3 -- 有3个元素被移除
SMOVE
命令允许用户将指定的元素从源集合移动到目标集合:SMOVE source target element
redis> SMOVE databases nosql "Redis"
(integer) 1 -- 移动成功
通过使用SMEMBERS
命令,用户可以取得集合包含的所有元素:SMEMBERS set
redis> SMEMBERS fruits
1) "banana"
2) "cherry"
3) "apple"
通过使用SCARD
命令,用户可以获取给定集合的大小,即集合包含的元素数量:SCARD set
redis> SCARD databases
(integer) 4 -- 这个集合包含4个元素
通过使用SISMEMBER
命令,用户可以检查给定的元素是否存在于集合当中:SISMEMBER set element
redis> SMEMBERS databases
1) "Redis"
2) "MySQL"
3) "MongoDB"
4) "PostgreSQL"
redis> SISMEMBER databases "MongoDB"
(integer) 1 -- 存在
redis> SISMEMBER databases "Oracle"
(integer) 0 -- 不存在
通过使用SRANDMEMBER
命令,用户可以从集合中随机地获取指定数量的元素。SRANDMEMBER
命令接受一个可选的count
参数,用于指定用户想要获取的元素数量,如果用户没有给定这个参数,那么SRANDMEMBER
命令默认只获取一个元素:SRANDMEMBER set [count]
redis> SMEMBERS databases
1) "Neo4j"
2) "Redis"
3) "PostgreSQL"
4) "CouchDB"
redis> SRANDMEMBER databases
"MySQL
redis> SMEMBERS databases -- 集合包含的元素和执行SRANDMEMBER之前完全一样
1) "Neo4j"
2) "Redis"
3) "PostgreSQL"
4) "CouchDB"
redis> SRANDMEMBER databases 2 -- 随机地返回2个不重复的元素
1) "MySQL"
2) "Oracle"
redis> SRANDMEMBER databases -3 -- 随机地返回3个可能会重复的元素
1) "Neo4j"
2) "CouchDB"
3) "MongoDB"
count参数的值既可以是正数也可以是负数。
如果count参数的值为正数,那么SRANDMEMBER命令将返回count个不重复的元素。
如果count参数的值为负数,那么SRANDMEMBER命令将随机返回abs(count)个元素(abs(count)也即是count的绝对值),并且在这些元素当中允许出现重复的元素。
通过使用SPOP
命令,用户可以从集合中随机地移除指定数量的元素。SPOP
命令接受一个可选的count
参数,用于指定需要被移除的元素数量。如果用户没有给定这个参数,那么SPOP
命令默认只移除一个元素:SPOP key [count]
redis> SPOP databases -- 随机地移除1个元素
"CouchDB" -- 被移除的是"CouchDB"元素
redis> SPOP databases 3 -- 随机地移除3个元素
1) "Neo4j" -- 被移除的元素是"Neo4j"、"PostgreSQL"和"MySQL"
2) "PostgreSQL"
3) "MySQL"
SINTER
命令可以计算出用户给定的所有集合的交集,然后返回这个交集包含的所有元素:SINTER set [set ...]
SINTERSTORE
命令,可以把给定集合的交集计算结果存储到指定的键里面:SINTERSTORE destination_key set [set ...]
redis> SMEMBERS s1
1) "a"
2) "b"
3) "c"
4) "d"
redis> SMEMBERS s2
1) "c"
2) "d"
3) "e"
4) "f"
redis> SINTER s1 s2
1) "c"
2) "d"
redis> SINTERSTORE s1-inter-s2 s1 s2
(integer) 2 -- 交集包含两个元素
redis> SMEMBERS s1-inter-s2
1) "c"
2) "d"
SUNION
命令可以计算出用户给定的所有集合的并集,然后返回这个并集包含的所有元素:SUNION set [set ...]
SUNIONSTORE
命令,可以把给定集合的并集计算结果存储到指定的键中,并在键已经存在的情况下自动覆盖已有的键:SUNIONSTORE destination_key set [set ...]
redis> SMEMBERS s1
1) "a"
2) "b"
3) "c"
4) "d"
redis> SMEMBERS s2
1) "c"
2) "d"
3) "e"
4) "f"
redis> SUNION s1 s2
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
redis> SUNIONSTORE s1-union-s2 s1 s2
(integer) 6 -- 并集共包含6个元素
redis> SMEMBERS s1-union-s2
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
SDIFF
命令可以计算出给定集合之间的差集,并返回差集包含的所有元素:SDIFF set [set ...]
SDIFFSTORE
命令,可以把给定集合之间的差集计算结果存储到指定的键中,并在键已经存在的情况下自动覆盖已有的键:SDIFFSTORE destination_key set [set ...]
redis> SMEMBERS s1
1) "a"
2) "b"
3) "c"
4) "d"
redis> SMEMBERS s2
1) "c"
2) "d"
3) "e"
4) "f"
redis> SMEMBERS s3
1) "b"
2) "f"
3) "g"
redis> SDIFF s1 s2 s3
1) "a"
redis> SDIFFSTORE diff-result s1 s2 s3
(integer) 1 -- 计算出的差集只包含一个元素
redis> SMEMBERS diff-result
1) "a"
Redis
的有序集合(sorted set
)同时具有“有序”和“集合”两种性质,这种数据结构中的每个元素都由一个成员和一个与成员相关联的分值组成,其中成员以字符串方式存储,而分值则以64位双精度浮点数格式存储。
通过使用ZADD
命令,用户可以向有序集合添加一个或多个新成员:ZADD sorted_set score member [score member ...]
redis> ZADD salary 3500 "peter" 4000 "jack" 2000 "tom" 5500 "mary"
(integer) 4 -- 这个命令向有序集合新添加了4个成员,既更新又添加
redis> ZADD salary XX 4500 "jack" 3800 "bob"
(integer) 0 -- 只更新已有成员分值
redis> ZADD salary NX 1800 "jack" 3800 "bob"
(integer) 1 -- 只添加新成员
redis> ZADD salary CH 3500 "peter" 4000 "bob" 9000 "david"
(integer) 2 -- 既更新又添加
通过使用ZREM
命令,用户可以从有序集合中移除指定的一个或多个成员以及与这些成员相关联的分值:ZREM sorted_set member [member ...]
redis> ZREM salary "peter"
(integer) 1 -- 移除了一个成员
通过使用ZSCORE
命令,用户可以获取与给定成员相关联的分值:ZSCORE sorted_set member
redis> ZSCORE salary "peter"
"3500"
通过使用ZINCRBY
命令,用户可以对有序集合中指定成员的分值执行自增操作,为其加上指定的增量:ZINCRBY sorted_set increment member
redis> ZINCRBY salary 1000 "tom" -- 将成员"tom"的分值加上1000
"3000" -- 成员"tom"现在的分值为3000
通过执行ZCARD
命令可以取得有序集合的基数,即有序集合包含的成员数量:ZCARD sorted_set
redis> ZCARD salary
(integer) 4 -- 这个有序集合包含4个成员
通过ZRANK
命令和ZREVRANK
命令,用户可以取得给定成员在有序集合中的排名:ZRANK sorted_set member
,ZREVRANK sorted_set member
redis> ZRANK salary "tom"
(integer) 3
redis> ZREVRANK salary "peter"
(integer) 4
通过ZRANGE
命令和ZREVRANGE
命令,用户可以以升序排列或者降序排列方式,从有序集合中获取指定索引范围内的成员:ZRANGE sorted_set start end
,ZREVRANGE sorted_set start end
redis> ZRANGE salary 0 -1 -- 以升序排列方式获取所有成员
1) "peter"
2) "bob"
3) "jack"
4) "tom"
5) "mary"
redis> ZREVRANGE salary 0 -1 -- 以降序排列方式获取所有成员
1) "mary"
2) "tom"
3) "jack"
4) "bob"
5) "peter"
redis> ZRANGE salary 0 3 WITHSCORES -- 获取指定索引范围内的成员以及与这些成员相关联的分值
1) "peter"
2) "3500" -- 成员"peter"的分值
3) "bob"
4) "3800" -- 成员"bob"的分值
5) "jack"
6) "4500" -- 成员"jack"的分值
7) "tom"
8) "5000" -- 成员"tom"的分值
通过使用ZRANGEBYSCORE
命令或者ZREVRANGEBYSCORE
命令,用户可以以升序排列或者降序排列的方式获取有序集合中分值介于指定范围内的成员:ZRANGEBYSCORE sorted_set min max
,ZREVRANGEBYSCORE sorted_set max min
redis> ZRANGEBYSCORE salary 3800 5000 -- 闭区间
1) "bob"
2) "jack"
3) "tom"
redis> ZREVRANGEBYSCORE salary 5000 3000 -- 闭区间
1) "tom"
2) "jack"
3) "bob"
4) "peter"
redis> ZRANGEBYSCORE salary 3800 5000 WITHSCORES -- 通过在执行时给定可选的WITHSCORES选项来同时获取成员及其分值
1) "bob"
2) "3800" -- 成员"bob"的分值
3) "jack"
4) "4500" -- 成员"jack"的分值
5) "tom"
6) "5000" -- 成员"tom"的分值
redis> ZREVRANGEBYSCORE salary 5000 3000 WITHSCORES -- 通过在执行时给定可选的WITHSCORES选项来同时获取成员及其分值
1) "tom"
2) "5000" -- 成员"tom"的分值
3) "jack"
4) "4500" -- 成员"jack"的分值
5) "bob"
6) "3800" -- 成员"bob"的分值
redis> ZRANGEBYSCORE salary 3000 5000 LIMIT 1 2 -- 取出排序集合里面的第二和第三个成员
1) "bob"
2) "jack"
redis> ZRANGEBYSCORE salary (3500 (5000 WITHSCORES -- 开区间
1) "bob"
2) "3800"
3) "jack"
4) "4500"
redis> ZRANGEBYSCORE salary -inf (5000 WITHSCORES -- 无穷小到5000
1) "peter"
2) "3500"
3) "bob"
4) "3800"
5) "jack"
6) "4500"
redis> ZRANGEBYSCORE salary (4000 +inf WITHSCORES -- 4000到无穷大
1) "jack"
2) "4500"
3) "tom"
4) "5000"
5) "mary"
6) "5500"
通过使用COUNT
命令,用户可以统计出有序集合中分值介于指定范围之内的成员数量:ZCOUNT sorted_set min max
redis> ZCOUNT salary 3000 5000 -- 也可以使用开区间或-inf或+inf
(integer) 4 -- 有序集合里面有4个成员的分值介于3000~5000之间
ZREMRANGEBYRANK
命令可以从升序排列的有序集合中移除位于指定排名范围内的成员,然后返回被移除成员的数量:ZREMRANGEBYRANK sorted_set start end
redis> ZREMRANGEBYRANK salary 0 3
(integer) 4 -- 这个命令移除了4个成员
ZREMRANGEBYSCORE
命令可以从有序集合中移除位于指定分值范围内的成员,并在移除操作执行完毕返回被移除成员的数量:ZREMRANGEBYSCORE sorted_set min max
redis> ZREMRANGEBYSCORE salary 3000 4000
(integer) 2 -- 有2个成员被移除了
与集合一样,Redis
也为有序集合提供了相应的并集运算命令ZUNIONSTORE
和交集运算命令ZINTERSTORE
,这两个命令的基本格式如下:ZUNIONSTORE destination numbers sorted_set [sorted_set ...]
,ZINTERSTORE destination numbers sorted_set [sorted_set ...]
redis> ZUNIONSTORE union-result-1 2 sorted_set1 sorted_set2
(integer) 5 -- 这个并集包含了5个成员,也可以把集合当做参数传入,集合为参数时看作所有成员的分值都为1的有序集合
redis> ZINTERSTORE inter-result-1 2 sorted_set1 sorted_set2
(integer) 1 -- 这个交集只包含了一个成员
redis> ZINTERSTORE agg-sum 3 ss1 ss2 ss3 AGGREGATE SUM
(integer) 1
redis> ZRANGE agg-sum 0 -1 WITHSCORES
1) "a"
2) "8" -- 三个集合交集成员为a,然后求和
redis> ZINTERSTORE agg-min 3 ss1 ss2 ss3 AGGREGATE MIN
(integer) 1
redis> ZRANGE agg-min 0 -1 WITHSCORES
1) "a"
2) "1" -- 选出交集成员中最小分值
redis> ZINTERSTORE agg-max 3 ss1 ss2 ss3 AGGREGATE MAX
(integer) 1
redis> ZRANGE agg-max 0 -1 WITHSCORES
1) "a"
2) "5" -- 选出交集成员中最大分值
并集也可以使用sum、min、max
在默认情况下,ZUNIONSTORE和ZINTERSTORE将直接使用给定有序集合的成员分值去计算结果有序集合的成员分值,但是在有需要的情况下,用户也可以通过可选的WEIGHTS参数为各个给定有序集合的成员分值设置权重:
ZUNIONSTORE destination numbers sorted_set [sorted_set ...] [WEIGHTS weight [weight ...]]
ZINTERSTORE destination numbers sorted_set [sorted_set ...] [WEIGHTS weight [weight ...]]
在使用WEIGHTS选项时,用户需要为每个给定的有序集合分别设置一个权重,命令会将这个权重与成员的分值相乘,得出成员的新分值,然后执行聚合计算;与此相反,如果用户在使用WEIGHTS选项时,不想改变某个给定有序集合的分值,那么只需要将那个有序集合的权重设置为1即可。
redis> zadd ss1 2 "a"
(integer) 1
redis> zadd ss2 3 "b"
(integer) 1
redis> zadd ss3 4 "c"
(integer) 1
redis> ZUNIONSTORE weighted-result 3 ss1 ss2 ss3 weights 3 5 1
(integer) 3
redis> ZRANGE weighted-result 0 -1 withscores -- 集合成员乘以对应的集合权重,然后几个集合中相同成员的分值相加,ss1中的成员都乘以3,ss2中都乘以5
1) "c"
2) "4"
3) "a"
4) "6"
5) "b"
6) "15"
对于拥有不同分值的有序集合成员来说,成员的大小将由分值决定,至于分值相同的成员,它们的大小则由该成员在字典序中的大小决定。ZRANGEBYLEX sorted_set min max
,ZREVRANGEBYLEX sorted_set max min
命令的min参数和max参数用于指定用户想要获取的字典序范围,它们的值可以是以下4种值之一:
redis> ZRANGEBYLEX words - +
1) "address"
2) "after"
3) "apple"
4) "bamboo"
redis> ZREVRANGEBYLEX words (c [a
1) "book"
2) "bear"
3) "banana"
4) "bamboo"
5) "apple"
6) "after"
7) "address"
redis> ZRANGEBYLEX words [b + LIMIT 0 1
1) "bamboo"
ZRANGEBYLEX sorted_set min max [LIMIT offset count]
,ZREVRANGEBYLEX sorted_set max min [LIMIT offset count]
限制命令返回的成员数量
对于按照字典序排列的有序集合,用户可以使用ZLEXCOUNT
命令统计有序集合中位于字典序指定范围内的成员数量:ZLEXCOUNT sorted_set min max
redis> ZLEXCOUNT words [a (b
(integer) 3 -- 这个有序集合中有3个以字母a开头的成
对于按照字典序排列的有序集合,用户可以使用ZREMRANGEBYLEX
命令去移除有序集合中位于字典序指定范围内的成员:ZREMRANGEBYLEX sorted_set min max
redis> ZREMRANGEBYLEX words [b (c
(integer) 4 -- 有4个成员被移除了
ZPOPMAX
和ZPOPMIN
是Redis 5.0版本新添加的两个命令,分别用于移除并返回有序集合中分值最大和最小的N个元素:ZPOPMAX sorted_set [count]
,ZPOPMIN sorted_set [count]
redis> ZPOPMAX salary
1) "mary" -- 被移除元素的成员
2) "5500" -- 被移除元素的分值
redis> ZPOPMIN salary
1) "peter"
2) "3500"
BZPOPMAX
命令和BZPOPMIN
命令分别是ZPOPMAX
命令以及ZPOPMIN
命令的阻塞版本,这两个阻塞命令都接受任意多个有序集合和一个秒级精度的超时时限作为参数:BZPOPMAX sorted_set [sorted_set ...] timeout
,BZPOPMIN sorted_set [sorted_set ...] timeout
redis> ZRANGE ss1 0 -1 WITHSCORES
(empty list or set)
redis> ZRANGE ss2 0 -1 WITHSCORES
1) "a"
2) "1"
3) "b"
4) "2"
redis> ZRANGE ss3 0 -1 WITHSCORES
1) "c"
2) "3"
redis> BZPOPMAX ss1 ss2 ss3 10 -- 跳过空集ss1
1) "ss2" -- 被弹出元素所在的有序集合
2) "b" -- 被弹出元素的成员
3) "2" -- 被弹出元素的分值
redis> BZPOPMAX ss1 ss2 ss3 10 -- 再次执行
1) "ss2"
2) "a"
3) "1"
redis> BZPOPMAX ss1 ss2 ss3 10 -- 再次执行
1) "ss3" -- 跳过空集ss1,ss2
2) "c"
3) "3"
HyperLogLog是一个专门为了计算集合的基数而创建的概率算法,对于一个给定的集合,HyperLogLog可以计算出这个集合的近似基数:近似基数并非集合的实际基数,它可能会比实际的基数小一点或者大一点,但是估算基数和实际基数之间的误差会处于一个合理的范围之内,因此那些不需要知道实际基数或者因为条件限制而无法计算出实际基数的程序就可以把这个近似基数当作集合的基数来使用。
HyperLogLog的优点在于它计算近似基数所需的内存并不会因为集合的大小而改变,无论集合包含的元素有多少个,HyperLogLog进行计算所需的内存总是固定的,并且是非常少的。具体到实现上,Redis的每个HyperLogLog只需要使用12KB内存空间,就可以对接近:2 64 个元素进行计数,而算法的标准误差仅为0.81%,因此它计算出的近似基数是相当可信的。
用户可以通过执行PFADD
命令,使用HyperLogLog
对给定的一个或多个集合元素进行计数:PFADD hyperloglog element [element ...]
redis> PFADD alphabets "a" "b" "c"
(integer) 1
redis> PFADD alphabets "a" -- 已经计过数的元素
(integer) 0
在使用PFADD
命令对元素进行计数之后,用户可以通过执行PFCOUNT
命令来获取HyperLogLog
为集合计算出的近似基数:PFCOUNT hyperloglog [hyperloglog ...]
redis> PFCOUNT alphabets
(integer) 3
redis> PFADD alphabets1 "a" "b" "c"
(integer) 1
redis> PFADD alphabets2 "c" "d" "e"
(integer) 1
redis> PFCOUNT alphabets1 alphabets2
(integer) 5
PFMERGE
命令可以对多个给定的HyperLogLog
执行并集计算,然后把计算得出的并集HyperLogLog
保存到指定的键中:PFMERGE destination hyperloglog [hyperloglog ...]
redis> PFADD numbers1 128 256 512
(integer) 1
redis> PFADD numbers2 128 256 512
(integer) 1
redis> PFADD numbers3 128 512 1024
(integer) 1
redis> PFMERGE union-numbers numbers1 numbers2 numbers3
OK
redis> PFCOUNT union-numbers
(integer) 4
PFCOUNT命令在计算多个HyperLogLog的近似基数时会执行以下操作:
1) 在内部调用PFMERGE命令,计算所有给定HyperLogLog的并集,并将这个并集存储到一个临时的HyperLogLog中。
2) 对临时HyperLogLog执行PFCOUNT命令,得到它的近似基数(因为这是针对单个HyperLogLog的PFCOUNT,所以这个操作不会引起循环调用)。
3) 删除临时HyperLogLog。
4) 向用户返回之前得到的近似基数。
PFCOUNT将执行以下以下操作:
1) 执行PFMERGE<temp-hyperloglog>numbers1numbers2numbers3,把3个给定HyperLogLog的并集结果存储到临时HyperLogLog中。
2) 执行PFCOUNT<temp-hyperloglog>,取得并集HyperLogLog的近似基数4。
3) 执行DEL<temp-hyperloglog>,删除临时HyperLogLog。
4) 向用户返回之前得到的近似基数4。
Redis
的位图(bitmap)是由多个二进制位组成的数组,数组中的每个二进制位都有与之对应的偏移量(也称索引),用户通过这些偏移量可以对位图中指定的一个或多个二进制位进行操作。
通过使用SETBIT
命令,用户可以为位图指定偏移量上的二进制位设置值:SETBIT bitmap offset value
redis> SETBIT bitmap001 0 1
(integer) 0 -- 二进制位原来的值为0
redis> SETBIT bitmap001 3 1
(integer) 0
redis> SETBIT bitmap001 -1 1 -- SETBIT命令只能使用正数偏移量
(error) ERR bit offset is not an integer or out of range
使用GETBIT
命令,用户可以获取位图指定偏移量上的二进制位的值:GETBIT bitmap offset
redis> GETBIT bitmap001 0 -- GETBIT命令只能使用正数偏移量
(integer) 1
用户可以通过执行BITCOUNT
命令统计位图中值为1的二进制位数量:BITCOUNT key
redis> BITCOUNT bitmap001
(integer) 3 -- 这个位图有3个二进制位被设置成了1
redis> BITCOUNT bitmap003 0 1 -- 统计第一个字节和第二个字节中有多少个二进制位被设置成了1
(integer) 9
用户可以通过执行BITPOS
命令,在位图中查找第一个被设置为指定值的二进制位,并返回这个二进制位的偏移量:BITPOS bitmap value
redis> BITPOS bitmap003 1
(integer) 0 -- 位图第一个被设置为1的二进制位的偏移量为0
redis> BITPOS bitmap003 1 1 1 -- 在第二个字节中找到第一个值为1的二进制位所处的偏移量,BITPOS bitmap value [start end],索引可以使用负数
(integer) 12
用户可以通过BITOP
命令,对一个或多个位图执行指定的二进制位运算,并将运算结果存储到指定的键中:BITOP operation result_key bitmap [bitmap ...]
redis> BITOP AND and_result bitmap001 bitmap002 bitmap003
(integer) 3 -- 运算结果and_result位图的长度为3字节
BITFIELD
命令允许用户在位图中的任意区域(field)存储指定长度的整数值,并对这些整数值执行加法或减法操作。BITFIELD
命令支持SET
、GET
、INCRBY
、OVERFLOW
这4个子命令
通过使用BITFIELD
命令的SET
子命令,用户可以在位图的指定偏移量offset上设置一个type类型的整数值value:BITFIELD bitmap SET type offset value
redis> BITFIELD bitmap SET u8 0 198
1) (integer) 0 -- 该区域被设置之前存储的整数值为0
redis> BITFIELD bitmap SET u8 0 123 SET i32 20 10086 SET i64 188 123456789 -- 允许在一次调用中执行多个子命令
1) (integer) 198
2) (integer) 0
3) (integer) 0
假设现在有一个位图,它存储着多个8位长的无符号整数,而我们想要把它的第133个8位无符号整数的值设置为22。如果使用SET子命令的偏移量设置格式,就需要先使用算式(133-1)*8计算出第133个8位无符号整数在位图中的起始偏移量1056,然后再执行以下命令:
BITFIELD bitmap SET u8 1056 22
很明显,这种手动计算偏移量然后进行设置的做法非常麻烦也很容易出错。与此相反,如果我们使用的是SET子命令的索引设置格式,那么只需要执行以下命令就可以对位图的第133个8位无符号整数进行设置了:
BITFIELD bitmap SET u8 #132 22 -- 根据索引对区域进行设置,BITFIELD bitmap SET type #index value
通过使用BITFIELD
命令的GET
子命令,用户可以从给定的偏移量或者索引中取出指定类型的整数值:BITFIELD bitmap GET type offset
redis> BITFIELD bitmap SET u8 0 123 SET i32 20 10086 SET i64 188 123456789
1) (integer) 0
2) (integer) 0
3) (integer) 0
redis> BITFIELD bitmap GET u8 0 GET i32 20 GET i64 188
1) (integer) 123
2) (integer) 10086
3) (integer) 123456789
redis> BITFIELD unsigned-8bits SET u8 #0 13 SET u8 #1 100 SET u8 #7 73
1) (integer) 0
2) (integer) 0
3) (integer) 0
redis> BITFIELD unsigned-8bits GET u8 #0 GET u8 #1 GET u8 #7 -- 根据索引去除指定类型的整数值,BITFIELD bitmap GET type #index
1) (integer) 13
2) (integer) 100
3) (integer) 73
除了设置和获取整数值之外,BITFIELD命令还可以对位图存储的整数值执行加法操作或者减法操作,这两个操作都可以通过INCRBY子命令实现:
BITFIELD bitmap INCRBY type offset increment
BITFIELD bitmap INCRBY type #index increment
redis> BITFIELD numbers SET u8 #0 10 -- 将区域的值设置为整数10
1) (integer) 0
redis> BITFIELD numbers GET u8 #0
1) (integer) 10
redis> BITFIELD numbers INCRBY u8 #0 15 -- 将整数的值加上15
1) (integer) 25
redis> BITFIELD numbers INCRBY u8 #0 -20 -- 将整数的值减去20
1) (integer) 5
OVERFLOW子命令的参数可以是WRAP、SAT或者FAIL中的一个:
redis> BITFIELD unsigned-4bits GET u4 #0 GET u4 #1 GET u4 #2
1) (integer) 15
2) (integer) 15
3) (integer) 15
redis> BITFIELD unsigned-4bits OVERFLOW WRAP INCRBY u4 #0 1 OVERFLOW SAT INCRBY u4 #1 1 OVERFLOW FAIL INCRBY u4 #2 1
1) (integer) 0
2) (integer) 15
3) (nil)
在一般情况下,当用户使用字符串或者散列去存储整数的时候,Redis
都会为被存储的整数分配一个long类型的值(通常为32位长或者64位长),并使用对象去包裹这个值,然后再把对象关联到数据库或者散列中。
与此相反,BITFIELD
命令允许用户自行指定被存储整数的类型,并且不会使用对象去包裹这些整数,因此当我们想要存储长度比long类型短的整数,并且希望尽可能地减少对象包裹带来的内存消耗时,就可以考虑使用位图来存储整数。
redis> GET 8bit-int
"\x04"
redis> GET 32bit-ints
"\x00\x00\x00{\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'f"
redis> STRLEN 8bit-int -- 这个位图长1字节
(integer) 1
redis> STRLEN 32bit-ints -- 这个位图长16字节
(integer) 16
redis> GETRANGE 32bit-ints 0 3 -- 获取位图的前4个字节
"\x00\x00\x00{"
通过使用GEOADD
命令,用户可以将给定的一个或多个经纬度坐标存储到位置集合中,并为这些坐标设置相应的名字:GEOADD location_set longitude latitude name [longitude latitude name ...]
redis> GEOADD Guangdong-cities 113.2278442 23.1255978 Guangzhou 113.106308 23.0088312 Foshan 113.7943267 22.9761989 Dongguan 114.0538788 22.5551603 Shenzhen
4 -- 添加广州、佛山、东莞和深圳市的坐标
在使用GEOADD
命令将位置及其坐标存储到位置集合之后,用户可以使用GEOPOS
命令去获取给定位置的坐标:GEOPOS location_set name [name ...]
redis> GEOPOS Guangdong-cities Qingyuan Guangzhou Zhongshan
1) 1) "113.20996731519699" -- 清远市的经度
2) "23.593675019671288" -- 清远市的纬度
2) 1) "113.22784155607224" -- 广州市的经度
2) "23.125598202060807" -- 广州市的纬度
3) 1) "113.40603142976761" -- 中山市的经度
2) "22.511156445825442" -- 中山市的纬度
在使用GEOADD
命令将位置及其坐标存储到位置集合中之后,可以使用GEODIST
命令计算两个给定位置之间的直线距离:GEODIST location_set name1 name2
redis> GEODIST Guangdong-cities Qingyuan Guangzhou
"52094.433840356309" -- 清远和广州之间的直线距离约为52094m
redis> GEODIST Guangdong-cities Qingyuan Guangzhou km -- 指定距离的单位
"52.094433840356309" -- 清远和广州之间直线距离约为52km
通过使用GEORADIUS
命令,用户可以指定一个经纬度作为中心点,并从位置集合中找出位于中心点指定半径范围内的其他位置:GEORADIUS location_set longitude latitude radius unit
-- 向位置集合中添加清远、广州、佛山、东莞、深圳、中山这6座城市的坐标
redis> GEOADD Guangdong-cities 113.2099647 23.593675 Qingyuan 113.2278442 23.1255978
Guangzhou 113.106308 23.0088312 Foshan 113.7943267 22.9761989 Dongguan 114.0538788 22.5551603
Shenzhen 113.4060288 22.5111574 Zhongshan
redis> GEORADIUS Guangdong-cities 112.3351942 23.0586893 50 km
(empty list or set) -- 距离肇庆市50km范围内,没有其他城市
redis> GEORADIUS Guangdong-cities 112.3351942 23.0586893 100 km
1) "Foshan" -- 佛山和广州都位于距肇庆100km范围之内
2) "Guangzhou"
redis> GEORADIUS Guangdong-cities 112.3351942 23.0586893 200 km WITHDIST -- GEORADIUS命令具有可选的WITHDIST选项,会返回这些位置与中心点之间的距离
1) 1) "Zhongshan" -- 被匹配的位置
2) "125.5669" -- 位置与中心点之间的距离
2) 1) "Shenzhen"
2) "184.9015"
3) 1) "Foshan"
2) "79.1250"
4) 1) "Guangzhou"
2) "91.6332"
5) 1) "Dongguan"
2) "149.6536"
6) 1) "Qingyuan"
2) "107.3463"
redis> GEORADIUS Guangdong-cities 112.3351942 23.0586893 150 km ASC -- GEORADIUS location_set longitude latitude radius unit [ASC|DESC],排序查找结果,默认无序
1) "Foshan"
2) "Guangzhou"
3) "Qingyuan"
4) "Zhongshan"
5) "Dongguan"
redis> GEORADIUS Guangdong-cities 112.3351942 23.0586893 150 km ASC WITHDIST
1) 1) "Foshan"
2) "79.1250"
2) 1) "Guangzhou"
2) "91.6332"
3) 1) "Qingyuan"
2) "107.3463"
4) 1) "Zhongshan"
2) "125.5669"
5) 1) "Dongguan"
2) "149.6536"
redis> GEORADIUS Guangdong-cities 112.3351942 23.0586893 200 km COUNT 3 -- GEORADIUS location_set longitude latitude radius unit [COUNT n],限制命令获取的位置数量
1) "Foshan"
2) "Guangzhou"
3) "Qingyuan"
可同时使用多个选项
GEORADIUSBYMEMBER
命令的作用和GEORADIUS
命令的作用一样,都是找出中心点指定半径范围内的其他位置,这两个命令的主要区别在于GEORADIUS
命令通过给定经纬度来指定中心点,而GEORADIUSBYMEMBER
命令则通过选择位置集合中的一个位置作为中心点:GEORADIUSBYMEMBER location_set name radius unit [WITHDIST] [WITHCOORD] [ASC|DESC] [COUNT n]
redis> GEORADIUSBYMEMBER Guangdong-cities Guangzhou 150 km WITHDIST ASC
1) 1) "Guangzhou"
2) "0.0000"
2) 1) "Foshan"
2) "17.9819"
3) 1) "Qingyuan"
2) "52.0944"
4) 1) "Dongguan"
2) "60.3114"
5) 1) "Zhongshan"
2) "70.7415"
6) 1) "Shenzhen"
2) "105.8068"
用户可以通过向GEOHASH命令传入一个或多个位置来获得这些位置对应的经纬度坐标的Geohash表示:
redis> GEOHASH Guangdong-cities Qingyuan Guangzhou Shenzhen
1) "ws0w0phgp70" -- 清远市经纬度坐标的Geohash值
2) "ws0e89curg0" -- 广州市经纬度坐标的Geohash值
3) "ws107659240" -- 深圳市经纬度坐标的Geohash值
redis> GEORADIUSBYMEMBER Guangdong-cities "Guangzhou" 200 km WITHHASH -- 在进行范围查找时获取Geohash值
1) 1) "Zhongshan" -- 被匹配的位置
2) (integer) 4046330600091985 -- 该位置经纬度坐标的Geohash值
2) 1) "Shenzhen"
2) (integer) 4046432447218769
3) 1) "Foshan"
2) (integer) 4046506835759376
4) 1) "Guangzhou"
2) (integer) 4046533621643967
5) 1) "Dongguan"
2) (integer) 4046540375616238
6) 1) "Qingyuan"
2) (integer) 4046597933543051
Redis
使用有序集合存储GEO
数据,一个位置集合实际上就是一个有序集合:当用户调用GEO
命令对位置集合进行操作时,这些命令实际上是在操作一个有序集合。
redis> ZRANGE Guangdong-cities 0 -1 WITHSCORES
1) "Zhongshan" -- 位置
2) "4046330600091985" -- Geohash值
3) "Shenzhen"
4) "4046432447218769"
5) "Foshan"
6) "4046506835759376"
流(stream)是Redis 5.0版本中新增加的数据结构,也是该版本最重要的更新。在以往的版本中,为了实现消息队列这一常见应用,用户往往会使用列表、有序集合和发布与订阅这3种功能,但这些不同的实现都有各自的缺陷:
除了以上3种数据结构各自具有的问题之外,还有一个问题是3种数据结构共有的:无论是列表、有序集合还是发布与订阅,它们的元素都只能是单个值。换句话说,如果用户想要用这些数据结构实现的消息队列传递多项信息,那么必须使用JSON之类的序列化格式来将多项信息打包存储到单个元素中,然后再在取出元素之后进行相应的反序列化操作。
Redis流的出现解决了上述提到的所有问题,它是上述3种数据结构的综合体,具备它们各自的所有优点以及特点,是使用Redis实现消息队列应用的最佳选择。流是一个包含零个或任意多个流元素的有序队列,队列中的每个元素都包含一个ID和任意多个键值对,这些元素会根据ID的大小在流中有序地进行排列。
用户可以通过执行XADD
命令,将一个带有指定ID
以及包含指定键值对的元素追加到流的末尾:XADD stream id field value [field value ...]
流元素的ID由毫秒时间(millisecond)和顺序编号(sequcen number)两部分组成,其中使用UNIX时间戳表示的毫秒时间用于标识与元素相关联的时间,而以0为起始值的顺序编号则用于区分同一时间内产生的多个不同元素。因为毫秒时间和顺序编号都使用64位的非负整数表示,所以整个流ID的总长为128位,而Redis在接受流ID输入以及展示流ID的时候都会使用连字符-分割这两个部分。
redis> XADD s1 1100000000000-12345 k1 v1
1100000000000-12345
redis> XADD temp-stream 1000000000000 k1 v1 -- Redis会自动将ID的顺序编号部分设置为0
1000000000000-0
redis> XADD s1 1100000000000-12345 k2 v2 -- 同一个流中的不同元素是不允许使用相同ID
(error) ERR The ID specified in XADD is equal or smaller than the target stream top item
redis> XADD s1 1000000000000-12345 k2 v2 -- 毫秒时间部分太小
(error) ERR The ID specified in XADD is equal or smaller than the target stream
top item
redis> XADD s1 1100000000000-100 k2 v2 -- 毫秒时间相同,但顺序编号部分太小
(error) ERR The ID specified in XADD is equal or smaller than the target stream
top item
Redis还要求新元素的ID必须比流中所有已有元素的ID都要大。具体来说,Redis会记住每个流已有元素的最大ID,并在用户尝试向流里面添加新元素的时候,使用新元素的ID与流目前最大的ID进行对比:
Redis
流对元素ID的要求非常严格,并且还会拒绝不符合规则的ID。为了方便用户执行添加操作,Redis
为XADD
命令的id参数设定了一个特殊值:当用户将符号用作id参数的值时,Redis
将自动为新添加的元素生成一个可用的新ID。
具体来说,自动生成的新ID会将Redis
所在宿主机器当前毫秒格式的UNIX
时间戳用作ID的毫秒时间,并根据当前已有ID的最大顺序编号来设置新ID的顺序编号部分:
·如果在当前毫秒之内还没有出现过任何ID,那么新ID的顺序编号将被设置为0。比如,如果当前时间为1530200000000ms,并且在这一毫秒之内没有任何现存的ID,那么新元素的ID将被设置为1530200000000-0。
·如果在当前毫秒内已经存在其他ID,那么这些ID中顺序编号最大的那个加上1就是新ID的顺序编号。比如,如果在1530200000000ms内,现存最大的顺序编号为10086,那么新ID的顺序编号将是10086加1的结果10087,而完整的新ID则是1530200000000-10087。
redis> XADD s1 * k2 v2
"1663308584308-0"
如果用户使用了*作为ID参数的值,但是宿主机器的当前时间比流中已有最大ID的毫秒时间要小,那么Redis将使用该ID的毫秒时间来作为新ID的毫秒时间,以此来避免机器时间倒流产生错误。
XADD
命令还提供了MAXLEN
选项,让用户可以在添加新元素的同时删除旧元素,以此来限制流的长度:XADD stream [MAXLEN len] id field value [field value ...]
redis> XADD mini-stream MAXLEN 3 * k4 v4
1400000000000-0
用户除了可以在执行XADD
命令的同时使用MAXLEN
命令对流进行修剪之外,还可以通过执行XTRIM
命令直接将流修剪至指定长度:XTRIM stream MAXLEN len
redis> XTRIM mini-stream MAXLEN 3
(integer) 2
XDEL
命令接受一个流以及任意多个元素ID作为输入,并从流中移除ID对应的元素:XDEL stream [id id ... id]
redis> XDEL trash-stream 2000000000000
(integer) 1
用户可以通过对流执行XLEN
命令,获取流目前包含的元素数量:XLEN stream
redis> XLEN stream-1
(integer) 3 -- 流包含3个元素
正如前面所说,流本质上是一个有序序列,对于这种序列,使用有序方式获取序列中的各项元素是一种非常常见的操作。正如Redis
为另一种有序序列数据结构列表提供了LRANGE
命令一样,Redis也为流提供了XRANGE
命令,这个命令可以以遍历或者迭代的方式,访问流中的单个或者任意多个元素。
XRANGE
命令接受一个流、一个起始ID、一个结束ID以及一个可选的COUNT
选项作为参数:XRANGE stream start-id end-id [COUNT n]
redis> XRANGE temp-stream 3000000000000 3000000000000 -- 可以使用特殊值减号-和加号+
1) 1) 3000000000000-0 -- 流元素的ID
2) 1) "k3" -- 流元素包含的键
2) "v3" -- 流元素包含的值
redis> XRANGE temp-stream - + COUNT 3 -- 获取指定数量的元素,XRANGE stream start-id end-id [COUNT n]
1) 1) 1000000000000-0
2) 1) "k1"
2) "v1"
2) 1) 2000000000000-0
2) 1) "k2"
2) "v2"
3) 1) 3000000000000-0
2) 1) "k3"
2) "v3"
redis> XREVRANGE temp-stream + - COUNT 3 -- 获取temp-stream中ID最大的3个元素
1) 1) 9000000000000-0
2) 1) "k9"
2) "v9"
2) 1) 8000000000000-0
2) 1) "k8"
2) "v8"
3) 1) 7000000000000-0
2) 1) "k7"
2) "v7"
除了XRANGE
命令和XREVRANGE
命令之外,Redis
还提供了XREAD
命令用于获取流中元素:XREAD [BLOCK ms] [COUNT n] STREAMS stream1 stream2 stream3 ... id1 id2 id3 ...
COUNT
选项限制命令对于每个流最多可以返回多少个元素:XREAD [COUNT n] STREAMS stream1 stream2 stream3 ... id1 id2 id3 ...
redis> XREAD COUNT 3 STREAMS s1 1000000000000
1) 1) "s1" -- 元素的来源流
2) 1) 1) 1100000000000-0 -- 第一个元素及其ID
2) 1) "k1" -- 第一个元素包含的键值对
2) "v1"
2) 1) 1200000000000-0 -- 第二个元素
2) 1) "k2"
2) "v2"
3) 1) 1300000000000-0 -- 第三个元素
2) 1) "k3"
2) "v3"
redis> XREAD COUNT 1 STREAMS s1 s2 s3 1000000000000 1000000000000 1000000000000
1) 1) "s1" -- 这个元素来源于流s1
2) 1) 1) 1100000000000-0
2) 1) "k1"
2) "v1"
2) 1) "s2" -- 这个元素来源于流s2
2) 1) 1) 1531743117644-0
2) 1) "k1"
2) "v1"
3) 1) "s3" -- 这个元素来源于流s3
2) 1) 1) 1531748220373-0
2) 1) "k1"
2) "v1"
通过使用BLOCK选项并给定一个毫秒精度的超时时间作为参数,用户能够以可阻塞的方式执行XREAD命令:XREAD BLOCK ms STREAMS stream1 stream2 stream3 ... id1 id2 id3 ...
BLOCK选项的值可以是任何大于等于0的数值,给定0则表示阻塞直到出现可返回的元素为止。根据用户给定的流是否拥有符合条件的元素,带有BLOCK选项的XREAD命令的行为也会有所不同。
redis> XREAD BLOCK 10000000 COUNT 2 STREAMS s1 s2 bs1 0 0 0
1) 1) "s1"
2) 1) 1) 1100000000000-0
2) 1) "k1"
2) "v1"
2) 1) 1200000000000-0
2) 1) "k2"
2) "v2"
2) 1) "s2"
2) 1) 1) 1531751993870-0
2) 1) "k1"
2) "v1"
2) 1) 1531751997935-0
2) 1) "k2"
2) "v2"
虽然流bs1没有可供读取的元素,但是由于流s1和s2都拥有可供读取的元素,所以命令没有进入阻塞状态,而是直接返回了可供读取的元素,并且元素的数量没有超过COUNT
选项的限制。
如果用户在执行带有BLOCK
选项的XREAD
命令时,给定的所有流都不存在可供读取的元素,那么命令将进入阻塞状态。如果在给定的阻塞时长之内有一个可供读取的元素出现,那么Redis
将把这个元素分发给所有因为该元素而被阻塞的客户端,这种情况下的XREAD
命令会无视用户给定的COUNT
选项,只返回一个元素。
Redis
为XREAD
命令提供了特殊ID参数$符号,用户在执行阻塞式的XREAD
命令时,只要将$符号用作ID参数的值,XREAD
命令就会只获取给定流在命令执行之后新出现的元素:
XREAD BLOCK ms STREAMS stream1 stream2 stream3 ... $ $ $ ...
举个例子,假设我们现在想要获取流bs1接下来将要出现的第一个新元素,那么可以执行以下命令:
redis> XREAD BLOCK 10000000 STREAMS bs1 $
执行这个调用的客户端将进入阻塞状态。在此之后,如果在给定的时限内,有另一个客户端向流s1推入新元素,那么原客户端的阻塞状态就会被解除,并返回被推入的元素,就像这样:
1) 1) "bs1" -- 元素的来源流
2) 1) 1) 1300000000000-0 -- 元素的ID
2) 1) "k3" -- 元素的键值对
2) "v3"
(2.64s) -- 客户端被阻塞的时长
在某些问题中,我们想要做的是从同一流中向许多客户端提供不同的消息子集。一个明显有用的例子是处理缓慢的消息:让N个不同的客户端接收流的不同部分来加快消息的处理。例如:如果有三个消费者A1、A2、A3和一个包含消息1、2、3、4、5、6、7的流,那么我们想要达到的是像下面这样分配消息。
1 --> A1
2 --> A2
3 --> A3
4 --> A1
5 --> A2
6 --> A3
7 --> A1
为了实现这一点,Redis使用了一个叫做消费者组的概念。从实现的角度来看,Redis消费者组与Kafka (TM)消费者组没有任何关系,只是在功能上是相似的:允许一组客户端合作消费同一消息流的不同部分。
我们可以将一个消费者组简单理解为一个从流中获取数据的特殊的消费者。它从流中获取数据,然后再服务于多个消费者,同时提供了如下的保证:
1) 每条消息都提供给不同的消费者,不会将相同的消息传递给多个消费者。
2) 在消费者组中,消费者通过客户端的名称(区分大小写的字符串)进行区分,当断开连接重新连通后,消费者客户端还是提供相同的名字,会被当做同一个消费者。这意味着在消费者组中由客户端提供唯一标识符。
3) 每个消费者组都有未被消费的第一个ID的概念,这样当消费者请求新消息时,它可以只提供以前未传递的消息。
4) 消费消息需要使用特定命令进行显式确认。Redis将该确认解释为:此消息已正确处理,可以从消费者组中移除。
5) 消费者组跟踪所有当前挂起的消息,即已传递给消费者组的某个消费者但尚未确认为已处理的消息。由于此功能,当访问流的消息历史记录时,每个使用者将只看到传递给它的消息。
Redis
流的消费者组(consumer group)允许用户将一个流从逻辑上划分为多个不同的流,并让消费者组属下的消费者去处理组中的消息。
XGROUP CREATE stream group start_id
命令中的stream
参数用于指定流的名字,group
参数用于指定将要创建的消费者组的名字。start_id
参数用于指定消费者组在流中的起始ID,这个ID决定了消费者组要从流的哪个ID之后开始进行读取。
客户端可以通过执行XREADGROUP命令来读取消费者组中的消息:
XREADGROUP GROUP group consumer [COUNT n] [BLOCK ms] STREAMS stream [stream ...] id [id ...]
从逻辑上来说,消费者就是负责处理消息的客户端。与创建消费者组不一样,消费者不用显式地创建,用户只要在执行XREADGROUP命令时给定消费者的名字,Redis就会自动为新出现的消费者创建相应的数据结构。
与消费者组一样,消费者也会维护一个属于自己的待处理消息队列:每当用户使用XREADGROUP命令读取出一条消息,并将这条消息指派给一个消费者处理时,该消费者就会把所指派的消息添加到自己的待处理消息队列中。
需要注意的是,与多个消费者组能够共享同一个流中的元素不一样,同一消费者组中的每条消息只能有一个消费者,换句话说,不同的消费者将独占组中的不同消息:当一个消费者读取了组中的一条消息之后,同组的其他消费者将无法读取这条消息。
当消费者处理完一条消息之后,它需要向Redis发送一条针对该消息的XACK
命令:XACK stream group id [id id ...]
当Redis接收到消费者发来的XACK命令之后,就会从消费者组的待处理消息队列以及消费者的待处理消息队列中移除指定的消息。这样一来,这些消息的状态就会从“待处理”转换为“已确认”,以此来表示消费者已经处理完这些消息了。
综合起来,一条消费者组消息从出现到处理完毕,需要经历以下阶段:
XADD
命令向流中添加一条消息时,该消息就从原来的“不存在”状态转换成了“未递送”状态。XREADGROUP
命令从流中读取一条消息时,该消息就从原来的“未递送”状态转换成了“待处理”状态。XACK
命令向服务器进行确认时,该消息就从原来的“待处理”状态转换成了“已确认”状态。redis> XGROUP CREATE mystream mygroup 0 -- 创建消费者组,cgs是必须存在的流
OK
redis> XREADGROUP GROUP mygroup worker1 count 1 STREAMS mystream > -- 以消费者worker1的身份,从消费者组mygroup中读取出相应的消息, > 表示只接收从来没有被投递给其他消费者的消息,即新的消息
1) 1) "cgs" -- 来源流
2) 1) 1) 1535875626221-0 -- 消息
2) 1) "k1"
2) "v1"
2) 1) 1535875628970-0 -- 消息
2) 1) "k2"
2) "v2"
redis> XPENDING cgs mygroup
1) (integer) 5 -- 消费者组目前处于待处理状态的消息数量,已读取,未处理的消息5条
2) "1663911944636-0" -- 最小的待处理消息ID
3) "1663916290849-0" -- 最大的待处理消息ID
4) 1) 1) "consumerA" -- 消费者的名字
2) "1" -- 该消费者正在处理的消息数量
2) 1) "worker1"
2) "4"
redis> XINFO GROUPS cgs
1) 1) "name" -- 消费者组的名字
2) "mygroup"
3) "consumers" -- 属下消费者的数量
4) (integer) 2
5) "pending" -- 该组目前的待处理消息数量
6) (integer) 5
7) "last-delivered-id" -- 该组目前的最后递送消息ID
8) "1663916290849-0"
redis> XACK cgs mygroup 1663916290849-0 -- 消费者处理完ID为1663916290849的消息之后,对其进行确认
(integer) 1
redis> xpending cgs mygroup -- 以通过再次执行XPENDING命令来确认队列中的待处理消息
1) (integer) 4
2) "1663911944636-0"
3) "1663913588314-0"
4) 1) 1) "worker1"
2) "4" -- 有四条待处理消息,consumerA 的消息已经被处理
可以看到,使用 start end count 选项可以获取详细信息,从读取到现在经历的毫秒数、消息被读取的次数。再加上消费者参数,可以获取具体某个消费者的Pending列表。
通过执行XGROUP CREATE命令,用户可以为流创建一个具有指定名字的消费者组:XGROUP CREATE stream group id
redis> xgroup create s1 mygroup 0 -- 创建消费者组,cgs是必须存在的流
OK
redis> XGROUP CREATE mystream1 mygroup 0 MKSTREAM -- 如果流不存在,使用可选的MKSTREAM子命令作为最后一个参数可以自动创建对应的流
OK
redis> XGROUP CREATE userinfo GRP-AFEI -- streams不存在时报错
$(error) ERR The XGROUP subcommand requires the key to exist. Note that for CREATE you may want to use the MKSTREAM option to create an empty stream automatically.
对于一个已经存在的消费者组来说,用户可以通过执行XGROUP SETID
命令来为消费者组设置新的最后递送消息ID:XGROUP SETID stream group id
redis> XINFO GROUPS cgs
1) 1) "name"
2) "mygroup"
3) "consumers"
4) (integer) 2
5) "pending"
6) (integer) 4
7) "last-delivered-id"
8) "1663916290849-0"
redis> xgroup setid cgs mygroup 1663911944636-0
OK
redis> XINFO GROUPS cgs
1) 1) "name"
2) "mygroup"
3) "consumers"
4) (integer) 2
5) "pending"
6) (integer) 4
7) "last-delivered-id"
8) "1663911944636-0" -- ID已改变
除了合法的消息ID之外,特殊符号$也可以用作id参数的值,这个符号可以把消费者组的最后递送消息ID设置为流最新消息的ID:
redis> xadd cgs * message h -- 向流插入一条新消息
"1663919899444-0"
redis> XGROUP SETID cgs mygroup $ -- 执行修改命令
OK
redis> XINFO GROUPS cgs -- 最后递送ID已被修改
1) 1) "name"
2) "mygroup"
3) "consumers"
4) (integer) 2
5) "pending"
6) (integer) 4
7) "last-delivered-id"
8) "1663919899444-0" -- ID 已改变
需要注意的是,使用XGROUP SETID命令显式地修改最后递送消息ID将对后续执行的XREADGROUP命令的结果产生影响,简单来说:
当用户不再需要某个消费者的时候,可以通过执行以下命令将其删除:XGROUP DELCONSUMER stream group consumer
redis> XINFO CONSUMERS cgs mygroup
1) 1) name -- 消费者的名字
2) "worker1"
3) pending -- 消费者正在处理的消息数量
4) (integer) 2
5) idle -- 消费者闲置的时间
6) (integer) 44481
2) 1) name
2) "worker2"
3) pending
4) (integer) 3
5) idle
6) (integer) 24816
redis> XGROUP DELCONSUMER cgs mygroup worker1 -- 将消费者worker1删除
(integer) 2 -- 这个消费者还有两条消息未确认
redis> XINFO CONSUMERS cgs mygroup
1) 1) name
2) "worker2"
3) pending
4) (integer) 3
5) idle
6) (integer) 72596
当消费者被删除之后,它在被删除时处理的消息也会从消费者组的待处理消息队列中移除。换句话说,属于被删除消费者的待处理消息将不再处于“待处理”状态,这些消息可能已经被消费者处理掉了,但也可能尚未得到妥善的处理。
与上一个命令类似,当一个消费者组完成了它的任务之后,用户可以通过执行以下命令来删除它:XGROUP DESTROY stream group
redis> XGROUP DESTROY cgs mygroup -- 删除cgs流的mygroup消费者组
(integer) 1
redis> XINFO GROUPS cgs -- cgs 流现在已经不再拥有任何消费者组了
(empty array)
XREADGROUP
命令是消费者组版本的XREAD
命令,用户可以使用这个命令读取消费者组中的消息:
XREADGROUP GROUP group consumer [COUNT n] [BLOCK ms] STREAMS stream [stream ...] id [id ...]
redis> XREADGROUP GROUP mygroup worker1 count 1 STREAMS mystream > -- 以消费者worker1的身份,从消费者组mygroup中读取出相应的消息, > 表示只接收从来没有被投递给其他消费者的消息,即新的消息
1) 1) "cgs" -- 来源流
2) 1) 1) 1535875626221-0 -- 消息
2) 1) "k1"
2) "v1"
2) 1) 1535875628970-0 -- 消息
2) 1) "k2"
2) "v2"
redis> xreadgroup group mygroup consumerA count 1 streams s1 0 -- count 1 表示只读一条,指定0表示访问所有投递给该消费者的历史消息, 消费者在消费时指定即可,不用预先创建
1) 1) "s1"
2) 1) 1) "1663408584308-1"
2) 1) "k3"
2) "v3"
redis> xreadgroup group mygroup consumerC streams s1 1663408584308-1 -- 指定1663408584308-1表示投递给该消费者且大于这个ID的历史消息
1) 1) "s1"
2) 1) 1) "1663833859359-0"
2) 1) "message"
2) "b"
2) 1) "1663833861537-0"
2) 1) "message"
2) "c"
3) 1) "1663833863881-0"
2) 1) "message"
2) "d"
4) 1) "1663833866316-0"
2) 1) "message"
2) "e"
5) 1) "1663833870573-0"
2) (nil)
可以看出,三个消费者都可以消费到这 10 条消息,有着互斥原则
,三个消费者配合协作来消费同一个消息队列,可以在消费能力不足,也就是消息处理程序效率不高时,使用该模式。
XREADGROUP GROUP myGroup consumerA COUNT 1 STREAMS s1 >
,用于组myGroup 内消费者 consumerA 在队列 s1 中消费,参数 > 表示未被组内消费的起始消息,参数 count 1 表示获取一条。
用户可以通过XPENDING
命令,获取指定流的指定消费者组目前的待处理消息的相关信息:
XPENDING stream group [start stop count] [consumer]
redis> XPENDING cgs mygroup
1) (integer) 2 -- 待处理消息的数量
2) 1534435172217-0 -- 首条消息的ID
3) 1534435256529-0 -- 最后一条消息的ID
4) 1) 1) "worker1" -- 各个消费者目前正在处理的消息数量
2) "1"
2) 1) "worker2"
2) "1"
redis> XPENDING cgs mygroup 1534435172217-0 1534435172217-0 1 -- 限制起始的消息ID和消息count
1) 1) 1534435172217-0 -- 消息ID
2) "worker1" -- 所属消费者
3) (integer) 52490194 -- 消息最后一次递送给消费者之后,过去了多少毫秒
4) (integer) 1 -- 消息被递送的次数
通过执行XACK
命令,用户可以将消费者组中的指定消息标记为“已处理”。被标记的消息将从当前消费者的待处理消息队列中移除,而之后执行的XREADGROUP
命令也不会再读取这些消息:
XACK stream group id [id id ...]
redis> XPENDING cgs mygroup - + 1 worker1
1) 1) 1534498374797-0
2) "worker1"
3) (integer) 19027
4) (integer) 1
redis> XACK cgs mygroup 1534498374797-0 -- 该消息将被标记为“已处理
(integer) 1 -- 有一条消息被标记了
redis> XPENDING cgs mygroup - + 1 worker1 -- 消息将从消费者worker1的待处理消息队列中消失
(empty list or set)
用户可以通过执行XCLAIM
命令,将指定消息的归属权从一个消费者转向另一个消费者,这个命令的基本格式并不复杂:
XCLAIM stream group new_consumer max_pending_time id [id id ...]
命令中的stream参数和group参数指定了消息所在的流和消费者组,new_consumer指定了消息的新消费者,而命令中的任意多个id参数则指明了需要转移归属权的消息。
除此之外,命令中毫秒格式的max_pending_time参数指定了执行归属权转移操作所需的最大消息处理时限,具体来说:
redis> XCLAIM cgs mygroup worker2 60000 1535002039330-0
1) 1) 1535002039330-0 -- 被转移消息的ID
2) 1) "k1" -- 被转移消息的内容
2) "v1"
如果消息1535002039330-0在的消费者处理该消息的时间超过了60000ms,那么将该消息的归属权转移给消费者worker2。
XCLAIM命令在成功执行之后将会返回被转移的消息作为结果;相反,如果转移操作因为处理时限未到等原因而未能顺利执行,那么命令将返回一个空列表:
redis> XCLAIM cgs mygroup worker2 60000 1535002039330-0
(empty list or set)
XINFO CONSUMERS
命令用于打印指定消费者组的所有消费者,以及这些消费者的相关信息:XINFO CONSUMERS stream group-name
命令打印的信息包括消费者的名字、它们正在处理的消息数量以及消费者的闲置时长。
以下是一个XINFO CONSUMERS
命令的使用示例:
redis> XINFO CONSUMERS cgs mygroup
1) 1) name -- 消费者的名字
2) "worker1"
3) pending -- 正在处理的消息数量
4) (integer) 1
5) idle -- 毫秒格式的闲置时长
6) (integer) 50899
2) 1) name
2) "worker2"
3) pending
4) (integer) 0
5) idle
6) (integer) 7371
XINFO GROUPS
命令用于打印与给定流相关联的所有消费者组,以及这些消费者组的相关信息:XINFO GROUPS stream
命令打印的信息包括消费者组的名字、它拥有的消费者数量、组中正在处理消息的数量以及该组最后递送消息的ID。
以下是一个XINFO GROUPS命令的使用示例:
redis> XINFO GROUPS cgs
1) 1) name -- 组名
2) "mygroup"
3) consumers -- 消费者数量
4) (integer) 2
5) pending -- 组中正在处理的消息数量
6) (integer) 1
7) last-delivered-id -- 最后递送消息的ID
8) 1532339991221-0
XINFO STREAM命令用于打印给定流的相关信息:XINFO STREAM stream
命令打印的信息包括流的长度(包含的消息数量)、流在底层的基数树表示的相关信息、流相关的消费者组数量、流最后生成的消息的ID以及流的第一个节点和最后一个节点。
以下是对cgs流执行XINFO STREAM命令的结果:
redis> XINFO STREAM cgs
1) length -- 长度
2) (integer) 1
3) radix-tree-keys -- 基数树的键数量
4) (integer) 1
5) radix-tree-nodes -- 基数树的节点数量
6) (integer) 2
7) groups -- 与之相关联的消费者组数量
8) (integer) 1
9) last-generated-id -- 最后生成的消息的ID
10) 1532339991221-0
11) first-entry -- 流的第一个节点
12) 1) 1532339991221-0
2) 1) "msg"
2) "initial message"
13) last-entry -- 流的第二个节点
14) 1) 1532339991221-0
2) 1) "msg"
2) "initial message"
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。