一、字符串
字符串键是Redis最基本的键值对类型,将一个单独的键和一个单独的值关联起来。通过字符串键,不仅可以存储和读取字符串,如果输入能被解释为整数和浮点数,还能执行自增或自减操作。
1、SET:设置字符串键的值
命令 | SET key value [EX seconds|PX milliseconds] [NX|XX] |
---|---|
效果 | 为字符串键设置值,如果字符串键不存在,创建这个字符串键;如果已经存在,直接更新值。 EX和PX选项设置键的生存时间(以秒或毫秒为单位)。当生存时间消耗殆尽后,这个键就会被移除。 NX和XX选项决定了是只进行创建操作还是更新操作。(NX代表not exists) |
返回值 | 如果不使用NX和XX选项,返回OK。 若给定NX,仅当key不存在的情况下执行操作并返回OK,否则返回nil表示操作失败。若给定XX,仅当key存在的情况下执行操作覆盖旧值并返回OK,否则返回nil表示操作失败。 |
注意:同样可以设置字符串键值的还有SETNX、SETEX、PSETEX,它们顾名思义很好理解作用,例如SETNX相当于给定了NX选项的SET命令。由于SET本身已经足够全能,推荐都使用SET命令,其他的命令在遥远的未来有可能被移除掉。
2、GET:获取字符串键的值
命令 | GET key |
---|---|
效果 | 根据字符串键获取值 |
返回值 | 键对应的值。如果键不存在,返回nil。 |
3、GETSET:获取旧值并设置新值
命令 | GETSET key value |
---|---|
效果 | 先获取字符串键当前值,再设置新值,最后返回旧值 |
返回值 | 返回旧值。如果键不存在,返回nil。 |
4、MSET、MSETNX、MGET:批量设置获取值
命令 | MSET key1 value1 [key2 value2 ...] |
---|---|
效果 | 一次为多个字符串键分别设置值 |
返回值 | 返回OK |
相比SET,一条MSET在代替多条SET的同时只需要客户端和服务器进行一次通信,从而提高了执行效率。
如果你不想覆盖已经存在的值,使用MSETNX:
命令 | MSETNX key1 value1 [key2 value2 ...] |
---|---|
效果 | 仅当所有key都不存在才一次设置多个键值对,否则放弃操作 |
返回值 | 如果所有key都不存在,操作成功,返回1;如果有至少一个key有值,放弃操作,返回0。 |
既然能批量写,自然也能批量读:
命令 | MGET key1 [key2 ...] |
---|---|
效果 | 一次获取多个字符串键的值 |
返回值 | 返回一个列表,按键的顺序排列各个值 |
redis> MSET a food b water
OK
redis> MGET b a c
1) "water"
2) "food"
3) (nil)
5、STRLEN:获取值的字节长度
命令 | STRLEN key |
---|---|
效果 | 获取字符串值的字节长度 |
返回值 | 返回一个整数。如果字符串键不存在,返回0。 |
6、GETRANGE、SETRANGE:根据索引获取或设置内容
每个字符串值都可以通过索引获取对应的字节,既可以使用正数索引,也可以使用负数索引(末位为-1,倒数第二个字节为-2,以此类推)
命令 | GETRANGE key start end |
---|---|
效果 | 获取值在[start, end]索引范围的子串 |
返回值 | 返回子串。如果键不存在,或者两个索引都超过了范围,返回空字符串。 |
命令 | SETRANGE key index substr |
---|---|
效果 | 将值从指定index处开始替换。如果index超过了值本身的范围,空余部分使用x00填充 |
返回值 | 返回替换后值的长度 |
7、APPEND:追加新内容到值的末尾
命令 | APPEND key suffix |
---|---|
效果 | 将suffix的内容追加到值的末尾。如果键不存在,相当于SET |
返回值 | 返回操作后值的长度 |
8、INCRBY、DECRBY、INCR、DECR、INCRBYFLOAT:对整数和浮点数执行加法和减法操作
如果存储的字符串键的值可以被解释为整数或浮点数,则可以执行加法和减法操作,包括:
1、对于整数而言,必须位于64位长度的有符号整数的范围内,为-9223372036854775808~9223372036854775807之间。
2、可以用C语言的long double类型存储的浮点数。
下表列出了一些情形:
值 | 解释值 |
---|---|
12345 | 整数 |
+888 | 整数 |
-999 | 整数 |
3.14 | 浮点数 |
+2.718 | 浮点数 |
-6.66 | 浮点数 |
-.5 | 浮点数 |
5. | 整数 |
12345678901234567890 | 这个整数太大了,超过了64位有符号整数的范围,故为字符串 |
1.23e4 | Reids无法解释科学计数法,所以是字符串 |
123abc | 字符串 |
INCRBY和DECRBY可以对整数值执行加法和减法操作。
命令 | INCRBY key increment |
---|---|
效果 | 为整数加上指定的整数增量,更新之。如果值或增量是浮点数等非整数会报错。如果键不存在,先初始化值为0再执行操作。 |
返回值 | 返回运算后的结果 |
increment可以为负整数,这意味着INCR可以执行减法操作,同样DECR可以执行加法操作。
INCR和DECR是INCRBY和DECRBY的简化版,用于对整数值自增1或自减1。
命令 | INCR key |
---|---|
效果 | 整数值自增1。如果值是浮点数等非整数会报错。如果键不存在,先初始化值为0再执行操作。 |
返回值 | 返回运算后的结果 |
以上的四个命令只能用于整数的加减运算,加数或被加数为浮点数都会导致Redis返回错误。使用INCRBYFLOAT可以执行整数和浮点数的加减法操作。(没有DECRBYFLOAT)
命令 | INCRBYFLOAT key increment |
---|---|
效果 | 执行浮点数加减法,更新之。值和increment都可以是整数或浮点数,甚至形如1.23e4这样用指数表示。如果键不存在,先初始化值为0再执行操作。如果执行结果可以表示为整数,则以整数形式存储。最多保留小数点后17位数字。 |
返回值 | 返回运算后的结果 |
9、总结
- SET、GET、GETSET设置或获取字符串键,其中SET包含了丰富的选项,既可以决定是创建还是更新,也可以设置生存时间。这几个命令都只能操作一个键值对。
- MSET、MSETNX、MGET批量操作多个键值对,只需要客户端和服务器进行一次通信,从而提高了执行效率。
- 字符串值既可以使用正数索引,也可以使用负数索引。
- GETRANGE、SETRANGE、APPEND可以获取和设置字符串值的部分。
- 如果值可以被解释为数值,可以对数值进行增减操作,如INCRBY、DECRBY、INCR、DECR、INCRBYFLOAT。
- 获取字符串值的字节长度:STRLEN
二、散列(hash)
Redis的散列键将一个键和一个散列关联起来,在散列中可以为任意多个字段(field)分别设置值。比起字符串键,散列键能将多个有关联的数据打包起来存储。
1、HSET、HSETNX、HGET:设置值或读取值
命令 | HSET hash field1 value1 [field2 value2...] |
---|---|
效果 | 为一个或多个指定字段设置值。根据字段是否存在于散列进行创建或覆盖操作。 |
返回值 | 新增的字段的数量。这意味着如果全部是覆盖,将返回0 |
在Redis 4.0.0之前,HSET只能一次给一个字段设置值。
相比SET,HSET可以一次设置多个值,但没有诸如NX的选项,此时可以使用HSETNX:
命令 | HSETNX hash field value |
---|---|
效果 | 只在字段不存在的情况下为其设置值 |
返回值 | 如果字段不存在,设置成功并返回1,反之不执行操作,返回0 |
没有HSETEX这样的命令,这是因为散列的生存时间是对整个散列而言的,不能单独为某个字段设置生存时间。
获取某个字段的值,用HGET:
命令 | HGET hash field |
---|---|
效果 | 获取字段的值 |
返回值 | 返回字段的值。如果散列或字段不存在,返回nil。 |
redis> HSET hash1 f1 hello f2 world
2
redis> HSET hash1 f2 World f1 Hello
0
redis> HSETNX hash1 f2 WORLD
0
redis> HGET hash1 f2
"World"
redis> HGET hash1 f
(nil)
2、HMGET:批量操作
命令 | HMGET hash field1 [field2 ...] |
---|---|
效果 | 一次获取多个字段的值 |
返回值 | 返回一个数组 |
类似于MSET,散列也提供了批量设置字段的命令HMSET,不过自从Redis 4.0.0增强了HSET之后,官方建议不要继续使用功能重复的HMSET。(HMSET:明明是我先...)
3、HKEYS、HVALS、HGETALL:获取所有字段和值
前面的命令必须指定字段才能获取值,那么有没有直接获取整个散列所有字段和值的命令呢?您好,有的。
命令 | HKEYS hash |
---|---|
效果 | 获取散列的所有字段 |
返回值 | 以列表返回散列的所有字段。如果散列不存在,返回空列表 |
命令 | HVALS hash |
---|---|
效果 | 获取散列的所有值 |
返回值 | 以列表返回散列的所有值。如果散列不存在,返回空列表 |
命令 | HGETALL hash |
---|---|
效果 | 获取散列所有字段和值 |
返回值 | 以列表返回散列的所有字段和值。如果散列不存在,返回空列表 |
redis> HKEYS hash1
1) "f1"
2) "f2"
redis> HVALS hash1
1) "Hello"
2) "World"
redis> HGETALL hash1
1) "f1"
2) "Hello"
3) "f2"
4) "World"
4、HSTRLEN、HLEN:值的字节长度和散列的字段数量
命令 | HSTRLEN hash field |
---|---|
效果 | 获取字段值的字节长度 |
返回值 | 返回字段值的字节长度。如果散列或字段不存在,返回0 |
命令 | HLEN hash |
---|---|
效果 | 获取散列包含的字段数量 |
返回值 | 返回散列包含的字段数量。如果散列不存在,返回0 |
5、HEXISTS、HDEL:字段是否存在,删除字段
命令 | HEXISTS hash field |
---|---|
效果 | 检查给定字段是否存在于散列中 |
返回值 | 如果散列包含给定字段,返回1,否则返回0。如果散列不存在,返回0 |
命令 | HDEL hash field1 [field2 ...] |
---|---|
效果 | 删除一个或多个给定字段。本来就不存在的字段会被忽略。 |
返回值 | 返回删除成功的字段的数量。如果散列不存在,返回0 |
注意:HDEL删除的是字段,如要删除整个散列,使用DEL命令(后面会讲到)。
redis> HGETALL hash1
1) "f1"
2) "Hello"
3) "f2"
4) "World"
redis> HDEL hash1 f2 f3
(integer) 1
redis> HKEYS hash1
1) "f1"
6、HINCRBY、HINCRBYFLOAT:对字段存储的数字值执行加减操作
命令 | HINCRBY hash field increment |
---|---|
效果 | 对整数值执行加法或减法操作,更新之 |
返回值 | 返回操作结果 |
命令 | HINCRBYFLOAT hash field increment |
---|---|
效果 | 执行浮点数加法或减法操作,更新之 |
返回值 | 返回操作结果 |
Redis没有提供HINCR、HDECRBY之类的命令。
7、散列与字符串
由于一个散列中可以包含多个字段,它天然地适合多项关联数据的存放,而只需要在数据库中创建一个散列键。虽然用字符串键也能达到类似的效果,但更多的键将带来更大的开销。
另一方面,字符串键的命令比散列更加丰富,功能更加强大。比如字符串键支持GETRANGE、APPEND命令,这些更加精细的操作是散列不具备的,后面还会说到对字符串键的值按位进行读写,即Bitmap。此外,散列也无法单独对某个字段设置过期时间,一旦某个散列键过期,它存储的所有字段和值都将消失,而使用字符串键就不会有这样的顾虑。
在命令上,二者都有很多相似的命令,列举如下:
字符串 | 散列 | 对比 |
---|---|---|
SET | HSET | 给字符串键/散列的字段设置值 |
SETNX | HSETNX | 条件是字符串键/字段不存在 |
MSET | HSET、HMSET | 为多个字符串键/散列的多个字段设置值 |
GET | HGET | 获取字符串键/散列的字段的值 |
MGET | HMGET | 获取多个字符串键/散列的多个字段的值 |
STRLEN | HSTRLEN | 获取字符串值/字段值的字节长度 |
INCRBY | HINCRBY | 对字符串值/字段值进行整数加减操作 |
INCRBYFLOAT | HINCRBYFLOAT | 对字符串值/字段值进行浮点数加减操作 |
EXISTS | HEXISTS | 检查键/字段是否存在于数据库/散列,EXISTS后面会讲 |
DEL | HDEL | 从数据库/散列中删除键/字段,DEL后面会讲 |
此外,散列还有:
- HLEN获取散列的字段数。
- HKEYS、HVALS、HGETALL列出散列的所有字段。
三、列表(list)
Redis的列表是一种线性的有序结构,按照元素被推入的顺序存储元素,当然也可以通过索引进行访问。
1、LPUSH、LPUSHX、RPUSH、RPUSHX:将元素推入左端或右端
命令 | LPUSH list element1 [element2 ...] |
---|---|
效果 | 将元素推入列表左端。如果推入多个元素,则从左往右依次将所有元素推入左端 |
返回值 | 返回推入操作后列表的元素数量 |
命令 | LPUSHX list element1 [element2 ...] |
---|---|
效果 | 只对已存在的列表执行推入操作 |
返回值 | 返回推入操作后列表的元素数量。如果列表键不存在,不执行推入操作,返回0 |
redis> LPUSHX list1 3 4 5
(integer) 0
类似地,RPUSH、RPUSHX将元素推入列表右端。
2、LPOP、RPOP:弹出元素
命令 | LPOP list |
---|---|
效果 | 弹出列表最左端的元素 |
返回值 | 返回弹出的元素。如果列表不存在,返回nil |
类似地,RPOP从右端弹出元素。
注意:可以一次推入多个,但是只能一次弹出一个。如果想安全弹出多个,可以使用事务。
3、RPOPLPUSH:将右端弹出的元素推入左端
命令 | RPOPLPUSH source destination |
---|---|
效果 | 先使用RPOP将源列表最右端元素弹出,然后使用LPUSH将被弹出的元素推入目标列表左端。源列表和目标列表可以相同,借此可以构造一个循环的列表 |
返回值 | 返回该元素。如果源列表不存在,放弃执行弹出和推入,返回nil |
注:没有LPOPRPUSH命令。
4、LINDEX、LRANGE:获取指定索引和索引范围上的元素
类似于字符串的索引,列表包含的每个元素也有对应的正数索引和负数索引。
命令 | LINDEX list index |
---|---|
效果 | 获取指定索引上的元素 |
返回值 | 返回指定索引上的元素。如果列表不存在或索引越界,返回nil |
命令 | LRANGE list start stop |
---|---|
效果 | 获取[start, stop]索引范围上的元素 |
返回值 | 返回指定范围内的所有元素。如果列表不存在,或两个索引都越界,或start大于stop,返回空列表。如果只有start越界,start会被修正为0,然后返回对应范围的元素;同理如果stop越界,修正为-1 |
redis> RPUSH list1 1 2 3
(integer) 3
redis> LRANGE list1 0 -1
1) "1"
2) "2"
3) "3"
redis> LINDEX list1 2
"3"
redis> LINDEX list1 3
(nil)
redis> LRANGE list1 2 5
1) "3"
5、LSET:根据索引设置新元素
命令 | LSET list index element |
---|---|
效果 | 为指定索引设置新元素 |
返回值 | 返回OK。如果列表不存在或给定索引越界,返回错误 |
注意:不同于LINDEX,如果LSET的索引越界,将返回错误。
6、LINSERT:将元素插入列表
命令 | LINSERT list BEFORE|AFTER target_element new_element |
---|---|
效果 | 将新元素插入目标元素的前面或后面。如果目标元素在列表中有多个,只对从左往右第一个目标元素执行操作 |
返回值 | 返回插入后列表的长度。如果目标元素不存在,返回-1 |
redis> LPUSH list2 hello world hello
(integer) 3
redis> LINSERT list2 BEFORE hello yes
(integer) 4
redis> LRANGE list2 0 -1
1) "yes"
2) "hello"
3) "world"
4) "hello"
7、LREM:移除元素
命令 | LREM list count element |
---|---|
效果 | 移除列表中指定元素element。count的值决定了移除元素的方式: 如果count为0,表示移除所有指定元素; 如果count为正整数,从列表左端开始移除count数量的指定元素; 如果count为负整数,从列表右端开始移除-count数量的指定元素。 |
返回值 | 返回被移除的元素的数量,也就是说如果不存在element或者列表不存在,返回0 |
redis> LPUSH list3 hello world hello foo hello
(integer) 5
redis> LREM list3 -2 hello
(integer) 2
redis> LRANGE list3 0 -1
1) "hello"
2) "foo"
3) "world"
8、LTRIM:修剪列表
命令 | LTRIM list start stop |
---|---|
效果 | 只保留给定范围[start, stop]内的元素,移除其他元素。 |
返回值 | 返回OK。索引越界不会返回错误(例如,如果start超过了数组的最大索引,将得到一个空列表,也就是说该列表键被移除。再如,如果start大于stop,得到的也是一个空列表。如果end超过了数组的最大索引,将被视为-1) |
9、LLEN:获取列表的长度
命令 | LLEN list |
---|---|
效果 | 获取列表的长度 |
返回值 | 返回列表的长度。如果列表不存在,返回0 |
10、BLPOP、BRPOP、BRPOPLPUSH:阻塞式操作
BLPOP和BRPOP是带有阻塞功能的弹出操作,它接受一个或多个列表以及一个秒级精度的超时时间作为参数。
命令 | BLPOP list1 [list2 ...] timeout |
---|---|
效果 | 从左往右依次检查给定列表,一旦遇到非空列表就对其执行左端弹出,不会阻塞。如果所有列表都是空的,它将阻塞当前redis-cli客户端并开始等待,直到有列表变为非空可以弹出元素,或者达到超时时间。如果timeout为0,将永远阻塞直到可以弹出。 |
返回值 | 如果执行了弹出操作,返回一个二元列表,分别是执行了弹出操作的列表名、弹出的元素本身。如果超时,返回nil。 |
下面的例子中,list4为空而list5非空,客户端不会阻塞,立即从list5左端弹出c。
redis> LPUSH list5 a b c
(integer) 3
redis> LPUSH list6 c d e
(integer) 3
redis> BLPOP list4 list5 list6 5
1) "list5"
2) "c"
redis> LRANGE list5 0 -1
1) "b"
2) "a"
下面的例子中,list7和list8都是空的,引发当前redis-cli客户端阻塞,但是在等待的5秒钟内另一个客户端向list8中推入了元素a,因此当前客户端成功弹出元素,并回到非阻塞状态。注意展示的1.32s的阻塞时长是客户端为了方便用户添加的额外信息,并非BLPOP的返回结果。
redis> BLPOP list7 list8 5 -- 阻塞
1) "list8" -- 弹出元素,解除阻塞
2) "a"
(1.32s)
下面的例子中,list7和list8都是空的,引发当前redis-cli客户端阻塞,并且在等待的5秒钟内一直没有新元素添加至两个列表,直到超时,并回到非阻塞状态。
redis> BLPOP list7 list8 5 -- 阻塞
(nil) -- 超时,解除阻塞
(5.29s)
同样,BRPOPLPUSH是RPOPLPUSH的阻塞版本。
命令 | BRPOPLPUSH source destination timeout |
---|---|
效果 | 如果源列表非空,行为和RPOPLPUSH一样,弹出源列表最右端元素并推入目标列表左端,最后返回这个元素。如果源列表为空,阻塞客户端直到源列表可以弹出元素,或者超时。如果timeout为0,将永远阻塞直到可以弹出 |
返回值 | 如果在超时时间内弹出了元素,返回该元素。如果超时,返回nil |
11、总结
- LPUSH、LPUSHX、RPUSH、RPUSHX推入元素,它们都支持批量操作。
- LINSERT一次插入一个元素。
- LPOP、RPOP一次弹出一个元素,此外还有个较为综合的RPOPLPUSH。
- LINDEX、LRANGE根据索引或索引范围获取元素,LSET根据索引设置元素。
- LREM移除一个或多个元素,LTRIM直接修剪列表。
- LLEN获取列表长度。
- 有三个阻塞操作:BLPOP、BRPOP、BRPOPLPUSH。
四、集合(set)
Redis的集合键可以存储任意多个各不相同的元素。
虽然列表也可以存储多个元素,但集合和列表的区别在于:
- 列表可以存储重复元素,集合不能存储重复元素,即使添加已存在的元素也会被忽略;
- 列表的存储是有序的,可以用索引获取对应元素,集合是无序的,没有索引的概念。
1、SADD、SREM:添加、移除元素
命令 | SADD set member1 [member2 ...] |
---|---|
效果 | 将一个或多个元素添加到集合中。会忽略已经存在的元素 |
返回值 | 返回成功添加的新元素数量 |
命令 | SREM set member1 [member2 ...] |
---|---|
效果 | 从集合中移除一个或多个元素。忽略不存在的元素 |
返回值 | 返回被移除的元素数量 |
redis> SADD set1 a b c
(integer) 3
redis> SADD set1 b d e
(integer) 2
redis> SREM set1 a d f
(integer) 2
2、SMOVE:将元素从一个集合移动到另一个集合
命令 | SMOVE source destination member |
---|---|
效果 | 将元素从源集合移动到目标集合 |
返回值 | 移动操作成功执行返回1。如果源集合不存在,或者source不在源集合中,返回0。(如果要移动的元素已经存在于目标集合,仍然会从源集合中移除,最终返回1) |
3、SCARD:获取集合包含的元素数量
命令 | SCARD set |
---|---|
效果 | 获取给定集合包含的元素数量 |
返回值 | 返回集合包含的元素数量 |
4、SISMEMBER:检查给定元素是否存在于集合
命令 | SISMEMBER set member |
---|---|
效果 | 检查给定元素是否存在于集合 |
返回值 | 存在返回1,不存在或集合本身不存在返回0 |
5、SMEMBERS:获取集合包含的所有元素
命令 | SMEMBERS set |
---|---|
效果 | 获取集合包含的所有元素 |
返回值 | 返回集合包含的所有元素。如果集合不存在,返回空集合 |
6、SRANDMEMBER、SPOP:随机获取元素
命令 | SRANDMEMBER set [count] |
---|---|
效果 | 从集合中随机获取指定数量的元素。如果不指定count,就只获取一个元素。SRANDMEMBER不会将元素从集合中删除。 |
返回值 | 如果没有给定count,返回nil或单独的元素。如果给定count,返回空列表或者列表,列表元素具体由count的值决定: 如果count大于集合长度,返回所有元素。 如果count为0,返回空集合。 如果count为负数,随机返回-count个元素,并且这些元素中允许出现相同的值。(也就是说,如果-count大于集合长度,仍然会返回-count个元素,而不是所有元素) |
命令 | SPOP set [count] |
---|---|
效果 | 从集合中随机移除指定数量的元素。如果不给定count,只移除一个元素。count不能为负数。 |
返回值 | 返回这些被移除的元素。如果count为0,不执行移除操作,返回空集合。 |
SPOP与SRANDMEMBER非常相似,主要区别在于SPOP会移除被随机选中的元素,而SRANDMEMBER不会。
7、SINTER、SINTERSTORE、SUNION、SUNIONSTORE、SDIFF、SDIFFSTORE:多个集合的交集、并集、补集计算
SINTER和SINTERSTORE对一个或多个集合执行交集计算。set1和set2取交集,结果再和set3取交集,以此类推。特别地,如果只给定了一个集合,直接返回这个集合,此时相当于SMEMBERS。
命令 | SINTER set1 [set2 ...] |
---|---|
效果 | 对集合执行交集计算 |
返回值 | 返回交集结果的所有元素。 |
redis> SADD set2 a b c
(integer) 3
redis> SADD set3 c
(integer) 1
redis> SADD set4 c d e
(integer) 3
redis> SINTER set1 set2 set3
1) "c"
SINTERSTORE还能将计算结果存储到指定键里面:
命令 | SINTERSTORE destination set1 [set2 ...] |
---|---|
效果 | 对集合执行交集计算并把结果存储到目的集合中。如果目的键已经存在,将会被覆盖 |
返回值 | 返回结果集合的元素数量 |
SUNION和SUNIONSTORE对一个或多个集合执行并集计算。
命令 | SUNION set1 [set2 ...] |
---|---|
效果 | 对集合执行并集计算 |
返回值 | 返回并集结果的所有元素 |
命令 | SUNIONSTORE destination set1 [set2 ...] |
---|---|
效果 | 对集合执行交集计算并把结果存储到目的集合中。如果目的键已经存在,将会被覆盖 |
返回值 | 返回结果集合的元素数量 |
SDIFF和SDIFFSTORE对一个或多个集合执行并集计算。
命令 | SDIFF set1 [set2 ...] |
---|---|
效果 | 对集合执行差集计算:首先计算set1 - set2,然后对所得集合s执行s - set3,以此类推 |
返回值 | 返回差集结果的所有元素 |
命令 | SDIFFSTORE destination set1 [set2 ...] |
---|---|
效果 | 对集合执行差集计算并把结果存储到目的集合中 |
返回值 | 返回结果集合的元素数量 |
8、总结
- SADD添加元素,SREM移除元素,支持操作多个元素。
- SMOVE在集合间移动一个元素。
- SISMEMBER检查存在,SCARD获取总数量,SMEMBERS获取所有元素。
- SRANDMEMBER、SPOP都能随机获取一个或多个元素,后者还会移除元素。
- 集合之间支持交集、并集、差集运算。
五、有序集合(zset)
有序集合同时具有有序和集合两种性质。在有序集合中,每个元素由一个成员和一个与成员关联的分值组成,其中成员以字符串方式存储,分值以64位双精度浮点数格式存储。每个成员是独一无二的,不会重复,并且每个成员按照自己的分值大小进行排序。如果分值相同,则按成员在字典序中的大小排序。分值不仅可以是数字,也可以是字符串"+inf"或者"-inf",分别表示无穷大和无穷小。
1、ZADD:添加或更新成员
命令 | ZADD zset [NX|XX] [CH] [INCR] score1 member1 [score2 member2 ...] |
---|---|
效果 | 添加或更新成员。如果zset或member不存在,添加成员以及分值。如果member已经存在且与给定的不一致,更新分值。 XX和NX选项限制只进行添加或更新操作:如果给定了XX选项,则只更新已经存在于zset中的成员的分值,忽略不存在的;如果给定NX选项,只添加不存在于zset的成员和分值,忽略已经存在的成员。 CH选项决定了返回值是否统计更新分值的数量。(CH代表changed) INCR选项使得ZADD的行为类似于ZINCRBY,如果给定INCR选项,那么只能传入一对score和member,效果是对给定的成员的分值进行自增或自减操作 |
返回值 | 返回成功添加的新成员的数量(不含更新分值的)。如果给定CH选项,则返回被修改的成员的数量,既包括新添加的,也包括更新分值的。如果给定INCR选项,返回进行运算后的分值或者nil(使用了NX或XX选项且操作被忽略) |
redis> ZADD zset1 1 one 2 two
(integer) 2
redis> ZADD zset1 2 one 3 three 4 four
(integer) 2
redis> ZADD zset1 CH 2.5 one 3.5 four 5 five
(integer) 3
redis> ZADD zset1 NX 1 one
(integer) 0
redis> ZADD zset1 INCR -1.5 one
"1"
2、ZREM:移除指定成员
命令 | ZREM zset member1 [member2 ...] |
---|---|
效果 | 移除一个或多个成员以及相关联的分值。如果给定的某个成员不存在,将被忽略。 |
返回值 | 返回移除的数量 |
3、ZSCORE:获取成员的分值
命令 | ZSCORE zset member |
---|---|
效果 | 获取成员的分值 |
返回值 | 返回成员的分值。如果zset不存在,或者member不存在,返回nil |
4、ZCARD:获取有序集合的大小
命令 | ZCARD zset |
---|---|
效果 | 获取有序集合包含的成员数量 |
返回值 | 返回zset包含的成员数量。如果集合不存在,返回0 |
5、ZINCRBY:对成员的分值执行增减操作
命令 | ZINCRBY zset increment member |
---|---|
效果 | 对成员的分值执行自增或自减操作并更新之。如果zset或者member不存在,相当于执行ZADD zset increment member。 |
返回值 | 返回运算后的分值 |
6、ZRANGE、ZREVRANGE:根据索引范围获取范围内的成员
类似于列表的索引,ZRANGE和ZREVRANGE既可以接受正数索引,也可以接受负数索引。默认成员按照分值的大小升序排序,分值相同的成员再按字典序升序排列。
命令 | ZRANGE zset start end [WITHSCORES] |
---|---|
效果 | 以升序排列获取指定索引范围[start, end]内的成员 |
返回值 | 返回范围内的成员。如果给定WITHSCORES选项,则按照member1,score1的形式同时返回成员和关联分值。索引越界不会产生异常。 |
ZREVRANGE返回的是降序排名,此时成员按照分值的大小降序排序,并且分值相同的成员也会按字典序降序排列。
命令 | ZREVRANGE zset start end [WITHSCORES] |
---|---|
效果 | 以降序排列获取指定索引范围[start, end]内的成员 |
返回值 | 返回范围内成员。如果给定WITHSCORES选项,则返回成员的同时也会返回关联的分值。 |
redis> ZADD zset2 1 a 1 b 1 c 2 e
(integer) 4
redis> ZRANGE zset2 0 -1
1) "a"
2) "b"
3) "c"
4) "e"
redis> ZRANGE zset2 0 -1 WITHSCORES
1) "a"
2) "1"
3) "b"
4) "1"
5) "c"
6) "1"
7) "e"
8) "2"
redis> ZREVRANGE zset2 0 -1
1) "e"
2) "c"
3) "b"
4) "a"
7、ZREMRANGEBYRANK:根据索引范围移除指定范围内的成员
命令 | ZREMRANGEBYRANK zset start end |
---|---|
效果 | 从zset中移除指定索引范围[start, end]内的成员 |
返回值 | 返回被移除成员的数量 |
8、ZRANK、ZREVRANK:根据成员获取索引
ZRANK和ZRANGE正好相反,根据单个成员获取索引。
命令 | ZRANK zset member |
---|---|
效果 | 获取成员在zset中的升序排名(即索引),序号从0开始。 |
返回值 | 返回成员在zset中的索引。如果zset或member不存在,返回nil |
ZREVRANK类似于ZRANK,只不过返回的是降序排名。
命令 | ZREVRANK zset member |
---|---|
效果 | 获取成员在zset中的降序排名,序号从0开始。 |
返回值 | 返回成员在zset中的降序排名。如果zset或member不存在,返回nil |
redis> ZREVRANGE zset2 0 -1
1) "e"
2) "c"
3) "b"
4) "a"
redis> ZREVRANK zset2 b
(integer) 2
9、ZRANGEBYSCORE、ZREVRANGEBYSCORE:根据分值获取指定范围内的成员
zset中每个成员和索引以及分值绑定,既能通过索引获取成员,也能通过分值获取成员。分值的范围比较复杂:
- 和索引范围一样,分值范围默认也是闭区间;
- 如果想使用开区间,在分值参数的前面加一个小括号(。例如ZRANGEBYSCORE zset (1 5表示1 < score <= 5,再如ZRANGEBYSCORE zset (5 (10表示5 < score < 10;
- 还可以使用-inf和+inf表示无穷小和无穷大,用于只需要分值范围的上限和下限时。
命令 | ZRANGEBYSCORE zset min max [WITHSCORES] [LIMIT offset count] |
---|---|
效果 | 以升序排列获取zset中分值介于min和max内的成员。使用LIMIT选项可以从结果中只取一部分,例如LIMIT 1 3表示取第2-4个结果。 |
返回值 | 返回范围内成员。如果给定WITHSCORES选项,则返回成员的同时也会返回关联的分值。 |
ZREVRANGEBYSCORE正好相反。需要注意的是,先接受max参数再接受min参数。
命令 | ZREVRANGEBYSCORE zset max maminx [WITHSCORES] [LIMIT offset count] |
---|---|
效果 | 以升序排列获取zset中分值介于min和max内的成员。使用LIMIT选项可以从结果中只取一部分,例如LIMIT 1 3表示取第2-4个结果。 |
返回值 | 返回范围内成员。如果给定WITHSCORES选项,则返回成员的同时也会返回关联的分值。 |
redis> ZADD zset3 1.3 one 2.4 two 4.1 four 6.2 six 2.4 two2
(integer) 5
redis> ZRANGEBYSCORE zset3 -inf +inf
1) "one"
2) "two"
3) "two2"
4) "four"
5) "six"
redis> ZREVRANGEBYSCORE zset3 +inf -inf
1) "six"
2) "four"
3) "two2"
4) "two"
5) "one"
redis> ZRANGEBYSCORE zset3 1 6 LIMIT 1 2 -- 取升序的第二和第三个
1) "two"
2) "two2"
10、ZREMRANGEBYSCORE:根据分值移除指定范围内的成员
命令 | ZREMRANGEBYSCORE zset min max |
---|---|
效果 | 从升序排列的zset中移除指定分值范围min和max内的成员。分值范围参考ZRANGEBYSCORE |
返回值 | 返回被移除成员的数量 |
11、ZCOUNT:统计指定分值范围内的成员数量
命令 | ZCOUNT zset min max |
---|---|
效果 | 统计指定分值范围内的成员数量。分值范围参考ZRANGEBYSCORE |
返回值 | 返回指定分值范围内的成员数量 |
12、ZRANGEBYLEX、ZREVRANGEBYLEX:根据字典序获取指定范围内的成员
如果有序集合中所有成员的分值都相同,它相当于一个按字典序自动排列的列表,此时ZRANGEBYSCORE这样的命令已经没有意义。Redis提供了诸如ZRANGEBYLEX这样的命令,根据字典序进行排序、删除、计数等操作。特别地,它没有WITHSCORES选项。
字典序的范围必须是以下四种之一:
- 以小括号(开头的值,表示开区间;
- 以中括号[开头的值,表示闭区间;
- 减号-,表示无穷小;
- 加号+,表示无穷大。
注意:如果有序集合中的成员分值不同,返回值将变得不可预测。
命令 | ZRANGEBYLEX zset min max [LIMIT offset count] |
---|---|
效果 | 以字典序获取介于min和max内的成员。使用LIMIT选项可以从结果中只取一部分 |
返回值 | 返回范围内成员 |
和ZREVRANGEBYSCORE一样,ZREVRANGEBYLEX也是先接受max参数再接受min参数。
命令 | ZREVRANGEBYLEX zset max min [LIMIT offset count] |
---|---|
效果 | 以逆字典序获取介于min和max内的成员。使用LIMIT选项可以从结果中只取一部分 |
返回值 | 返回范围内成员 |
redis> ZADD zset4 0 a 0 b 0 c 0 d 0 e 0 f 0 g
(integer) 7
redis> ZREVRANGEBYLEX zset4 [c -
1) "c"
2) "b"
3) "a"
redis> ZRANGEBYLEX zset4 (f +
1) "g"
redis> ZRANGEBYLEX zset4 [aaa (d
1) "b"
2) "c"
13、ZREMRANGEBYLEX:根据字典序移除指定范围内的成员
命令 | ZREMRANGEBYLEX zset min max |
---|---|
效果 | 移除指定字典序范围min和max内的成员。字典序范围参考ZRANGEBYLEX |
返回值 | 返回被移除成员的数量 |
14、ZLEXCOUNT:根据字典序统计位于指定范围内的成员数量
命令 | ZCOUNT zset min max |
---|---|
效果 | 统计位于字典序指定范围内的成员数量。字典序范围参考ZRANGEBYLEX |
返回值 | 返回位于字典序指定范围内的成员数量 |
15、ZUNIONSTORE、ZINTERSTORE:并集和交集运算
命令 | ZUNIONSTORE destination numzsets zset1 [zset2 ...] [WEIGHTS weight1 [weight2 ...]] [AGGREGATE SUM|MIN|MAX] |
---|---|
效果 | 进行并集运算并存储到目标有序集合中,如果destination键已经存在,将会被覆盖。 取并集的成员的分值由原来的多个有序集合的成员的分值相加得到。如果给定了AGGREGATE选项,可以改变分值的计算方式:SUM(默认)、MIN、MAX。 numzsets指定参与计算的有序集合数量,必须和zset的数量一致否则报错。 如果使用WEIGHTS选项,需要为每个给定的有序集合分别设置一个权重,新分值由成员的分值与权重之积再聚合计算得到。可以使用集合作为并集运算的输入,集合的分值视为1 |
返回值 | 返回计算结果包含的成员数量 |
下面的例子,分别用两种聚合方式求出了并集
redis> ZADD zset5 1 a 2 b 3 c
(integer) 3
redis> ZADD zset6 2 a 5 c 8 d
(integer) 3
redis> ZUNIONSTORE store1 2 zset5 zset6 WEIGHTS 3 2
(integer) 4
redis> ZRANGE store1 0 -1
1) "b"
2) "a"
3) "d"
4) "c"
redis> ZRANGE store1 0 -1 WITHSCORES
1) "b"
2) "6" -- 6 = 2 * 3
3) "a"
4) "7" -- 7 = 1 * 3 + 2 * 2
5) "d"
6) "16"
7) "c"
8) "19"
redis> ZUNIONSTORE store2 2 zset5 zset6 WEIGHTS 3 2 AGGREGATE MIN
(integer) 4
redis> ZRANGE store2 0 -1 WITHSCORES
1) "a"
2) "3" -- 3 = min(1 * 3, 2 * 2)
3) "b"
4) "6"
5) "c"
6) "9"
7) "d"
8) "16"
下面的例子,将一个有序集合和一个集合取并集,集合中成员的分值视为1。
redis> SMEMBERS set1
1) "b"
2) "e"
3) "c"
redis> ZRANGE zset5 0 -1 WITHSCORES
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
redis> ZUNIONSTORE store3 2 zset5 set1
(integer) 4
redis> ZRANGE store3 0 -1 WITHSCORES
1) "a"
2) "1"
3) "e"
4) "1"
5) "b"
6) "3"
7) "c"
8) "4"
ZINTERSTORE对有序集合取交集计算。
命令 | ZINTERSTORE destination numzsets zset1 [zset2 ...] [WEIGHTS weight1 [weight2 ...]] [AGGREGATE SUM|MIN|MAX] |
---|---|
效果 | 进行交集运算并存储到目标有序集合中,其他类似ZUNIONSTORE |
返回值 | 返回计算结果包含的成员数量 |
16、ZPOPMAX、ZPOPMIN:弹出分值最高和最低的成员
命令 | ZPOPMAX zset [count] |
---|---|
效果 | 弹出count个分值最高的成员,不给定count则弹出1个 |
返回值 | 返回被弹出的这些成员和对应分值 |
命令 | ZPOPMIN zset [count] |
---|---|
效果 | 弹出count个分值最低的成员,不给定count则弹出1个 |
返回值 | 返回被弹出的这些成员和对应分值 |
17、BZPOPMAX、BZPOPMIN:阻塞式弹出分值最高和最低的成员
命令 | BZPOPMAX zset1 [zset2 ...] timeout |
---|---|
效果 | 依次检查给定的所有zset并对第一个非空zset弹出其内最大分值的元素。如果所有zset都没有可弹出的元素,就阻塞客户端直到可以弹出或超时。如果timeout设置为0,表示一直阻塞,直到可弹出元素出现为止。 |
返回值 | 成功弹出元素后返回一个三元列表:被弹出元素所在zset名、被弹出元素的成员、被弹出成员的分值。如果超时,返回nil。 |
命令 | BZPOPMIN zset1 [zset2 ...] timeout |
---|---|
效果 | 类似于BZPOPMAX,只是弹出的是分值最低的成员 |
返回值 | 同BZPOPMAX |
redis> ZRANGE zset5 0 -1 WITHSCORES
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
redis> BZPOPMIN zset5 4
1) "zset5"
2) "a"
3) "1"
18、总结
- ZADD添加一个或多个成员-分值对,ZREM移除一个或多个成员。ZADD还能当ZINCRBY用。
- 用单个成员能做什么?获取分值、更新分值、获取索引,ZSCORE、ZINCRBY、ZRANK、ZREVRANK。
- 用索引能做什么?获取和移除,ZRANGE、ZREVRANGE、ZREMRANGEBYRANK。
- 分值可以使用开区间、-inf和+inf。用分值能做什么?获取、移除、计数,ZRANGEBYSCORE、ZREVRANGEBYSCORE、ZREMRANGEBYSCORE、ZCOUNT。
- 获取集合大小:ZCARD。
- 字典序相关:ZRANGEBYLEX、ZREVRANGEBYLEX、ZREMRANGEBYLEX、ZLEXCOUNT,仅用于分值都相同。
- 并集和交集运算:ZUNIONSTORE、ZINTERSTORE。
- 阻塞或非阻塞弹出分值最高最低成员:ZPOPMAX、ZPOPMIN、BZPOPMAX、BZPOPMIN。
六、HyperLogLog
以下引用自Redis中文网:
Redis HyperLogLog是用来做基数统计的算法,HyperLogLog的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
在 Redis 里面,每个HyperLogLog键只需要花费12 KB内存,就可以计算接近2^64个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为HyperLogLog只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8},那么这个数据集的基数集为 {1, 3, 5 ,7, 8},基数(不重复元素)为5。基数估计就是在误差可接受的范围内,快速计算基数。
1、PFADD:添加指定元素到HyperLogLog中
命令 | PFADD hyperloglog element1 [element2 ...] |
---|---|
效果 | 将元素添加到HyperLogLog集合中 |
返回值 | 如果给定元素中出现了至少一个新元素,返回1,表示因为新元素的添加使得计算出的近似基数发生变化。如果没有新元素,返回0,表示计算出的近似基数不变 |
redis> PFADD hll1 a b c d e f g
(integer) 1
redis> PFCOUNT hll1
(integer) 7
2、PFCOUNT:返回集合的近似基数
命令 | PFCOUNT hyperloglog1 [hyperloglog2 ...] |
---|---|
效果 | 获取集合的近似基数。如果传入多个HyperLogLog,先进行并集计算再求并集集合的近似基数 |
返回值 | 返回集合的近似基数 |
redis> PFADD hll2 hello world yes
(integer) 1
redis> PFADD hll2 yes yes yes
(integer) 0
redis> PFADD hll3 a b c d
(integer) 1
redis> PFCOUNT hll2 hll3
(integer) 7
3、PFMERGE:计算并存储多个HyperLogLog的并集
命令 | PFMERGE destination hyperloglog1 [hyperloglog2 ...] |
---|---|
效果 | 对多个给定的hyperloglog执行并集计算,把并集结果保存到destination中 |
返回值 | 返回OK |
PFCOUNT在计算多个HyperLogLog的并集后会将其存储到一个临时的HyperLogLog中再得到近似基数,最后将临时的HyperLogLog删除掉。而PFMERGE会存储起来。
七、位图(bitmap)
Redis的位图是由多个二进制位组成的数组,每个二进制位有自己的偏移量(索引),通过偏移量即可对指定的二进制位进行操作。
位图是在字符串的基础上实现的,故位图键会被视为一个字符串键,GET、SET、STRLEN、GETRANGE等同样适用。
1、SETBIT:设置二进制位的值
命令 | SETBIT bitmap offset value |
---|---|
效果 | 为位图指定偏移量上的二进制位设置值,值只能为1或0。如果位图当前的长度小于offset,将对该位图进行扩展,并且将所有未被设置的二进制位初始化为0。offset不能为负数。 |
返回值 | 返回被设置之前的旧值。如果bitmap不存在或offset大于当前位图大小,返回0 |
2、GETBIT:获取二进制位的值
命令 | GETBIT bitmap offset |
---|---|
效果 | 获取位图指定偏移量上的二进制位的值。offset不能为负数 |
返回值 | 返回指定偏移量上的二进制位的值。如果bitmap不存在或offset大于当前位图大小,返回0 |
3、BITCOUNT:统计被设置的二进制位数量
命令 | BITCOUNT bitmap [start end] |
---|---|
效果 | 统计位图中值为1的二进制位数量。如果给定了start和end,表示对指定字节范围[start, end]内的二进制位进行统计。start和end可以是负数。 |
返回值 | 返回值为1的二进制位数量 |
注意:BITCOUNT中的start和end与GETRANGE中的start和end一样,都是字节偏移量,而不是二进制偏移量。例如BITCOUNT bitmap 0 2统计的是前三个字节中包含的所有二进制位,而不是前三个二进制位。此外,这里的字节偏移量是可以使用负数索引的。
下面的例子,由于l和d对应的二进制是0110 1100和0110 0100,所以BITCOUT统计的结果是7。之后将l最高位的1改为0(偏移量为9*8+1=73),0010 1100对应符号为英文逗号(,)。
redis> SET bitmap1 'hello world'
OK
redis> STRLEN bitmap1
(integer) 11
redis> BITCOUNT bitmap1 -2 -1
(integer) 7
redis> SETBIT bitmap1 73 0
(integer) 1
redis> BITCOUNT bitmap1 -2 -1
(integer) 6
redis> GETRANGE bitmap1 0 -1
"Hello wor,d"
4、BITPOS:查找第一个指定的二进制位值
命令 | BITPOS bitmap value [start] [end] |
---|---|
效果 | 查找第一个被设置为指定值的二进制位。如果给定start和end,则只在字节范围[start, end]内的二进制位中查找。 |
返回值 | 返回该二进制位的偏移量。如果只给定了start没有给定end,则end视为-1。 如果查找的是1,但位图不存在或所有范围内的位都是0,返回-1。 如果查找的是0且没有给定start和end,且位图中所有位都为1,返回位图最大偏移量+1。 特别地,在同时给定了start和end的情况下,如果范围在bitmap的长度之外,返回-1;如果在范围内均没有查找的值,也返回-1 |
下面这个例子展示了多种情况下查找失败的返回值
redis> SET bitmap1 "x00"
OK
redis> STRLEN bitmap1
(integer) 1
redis> BITPOS bitmap1 1
(integer) -1
redis> BITPOS bitmap1 1 0
(integer) -1
redis> SET bitmap1 "xff"
OK
redis> BITPOS bitmap1 0
(integer) 8
redis> BITPOS bitmap1 0 0
(integer) 8
redis> BITPOS bitmap1 0 0 0
(integer) -1
5、BITOP:执行二进制运算
命令 | BITOP operation destination bitmap1 [bitmap2 ...] |
---|---|
效果 | 执行二进制位运算并存储到结果中。operation必须是是AND、OR、XOR、NOT之一,分别对应与、或、异或、非,其中AND、OR、XOR允许输入任意多个bitmap,而NOT只允许输入一个。如果位图长度不同,较短的位图补0 |
返回值 | 返回被存储位图的字节长度 |
redis> SET bitmap1 "x6c"
OK
redis> SET bitmap2 "xa7z"
OK
redis> BITOP AND bitmap3 bitmap1 bitmap2
(integer) 2
redis> GETRANGE bitmap3 0 -1
"$x00"
redis> BITOP XOR bitmap3 bitmap1 bitmap2
(integer) 2
redis> GETRANGE bitmap3 0 -1
"xcbz"
redis> BITOP NOT bitmap3 bitmap1
(integer) 1
redis> GETRANGE bitmap3 0 -1
"x93"
6、BITFIELD:在位图中存储整数值
BITFIELD命令可以在位图的任意区域中存储整数值,并对这些整数值执行加法或减法操作。它支持SET、GET、INCRBY、OVERFLOW这四个子命令,以下分别介绍。
SET子命令给区域设置值:
BITFIELD bitmap SET type offset|#index value
- type指定被设置值的类型,必须以i或u开头,后跟要设置的值的位长度,i表示有符号整数,u为无符号整数。如i8为有符号8位整数,u16为无符号16位整数。
- offset表示设置位置的位偏移量,也可以用#index,表示根据给定类型的位长度的索引。如u8 #132表示该位图的第133个8位无符号整数,等同于u8 1056。
- value指定被设置的整数值。如果超过了type指定的类型,会被截断。例如,有符号4位整数的最大值为15,这意味着u4 0 123相当于u4 0 11。
返回指定区域被设置前的旧值。
BITFIELD命令允许一次执行多个子命令,比如一次使用多个SET设置多个区域。返回数组,对应每一个子命令的返回结果。
redis> BITFIELD bitmap4 SET u4 0 123
1) (integer) 0
redis> GET bitmap4
"xb0"
redis> BITFIELD bitmap4 SET u4 0 13 SET u4 4 14 -- 一次设置两个
1) (integer) 11
2) (integer) 0
redis> GET bitmap4
"xde"
redis> BITFIELD bitmap4 SET u3 9 7 SET u4 #4 10 -- #4即给位偏移量的16-19进行设置
1) (integer) 0
2) (integer) 0
redis> GET bitmap4
"xdepxa0" -- p的二进制为0111 0000
GET子命令获取区域存储的值:
BITFIELD bitmap GET type offset|#index
这些参数的意义与SET子命令中同名参数的意义完全一样。
如果位图不存在,或者超过了边界,返回0。
redis> GET bitmap4
"xdepxa0"
redis> BITFIELD bitmap4 GET u8 #0 GET u8 #1 GET u8 #2 GET u8 #3
1) (integer) 222 -- xde对应十进制是222
2) (integer) 112 -- p的ascii编号是112
3) (integer) 160 -- xa0对应十进制是160
4) (integer) 0 -- 越界
redis> BITFIELD bitmap4 GET u16 #0 GET u16 #1
1) (integer) 56944 -- xdex70
2) (integer) 40960 -- xa0x00,后补0
INCRBY子命令执行加法或减法操作:
BITFIELD bitmap INCRBY type offset|#index increment
返回整数的当前值。
redis> GET bitmap4
"xdepxa0"
redis> BITFIELD bitmap4 INCRBY u8 0 1 INCRBY u8 #1 -2
1) (integer) 223
2) (integer) 110
redis> GET bitmap4
"xdfnxa0"
OVERFLOW用于控制INCRBY子命令发生计算溢出时的行为:
BITFIELD bitmap OVERFLOW WRAP|SAT|FAIL
- WRAP(默认),向上溢出的整数值从类型的最小值开始计算,向下溢出的整数值从类型的最大值开始计算;
- SAT,饱和运算(saturation arithmetic),如果发生向上溢出,得到的结果是类型的最大值,如果发生向下溢出,得到的结果是类型的最小值;
- FAIL,检测到计算会引发溢出则拒绝计算,返回nil表示计算失败。
OVERFLOW命令只会对排在它后面的那些INCRBY产生效果。
redis> GET bitmap4
"xdfnxa0"
redis> BITFIELD bitmap4 INCRBY u4 0 3 OVERFLOW SAT INCRBY u4 #1 3
1) (integer) 2
2) (integer) 15
redis> GET bitmap4
"/nxa0" -- /对应0x2F
如果用字符串或散列存储整数,Redis会先给整数分配一个long类型的值,并使用对象去包裹这个值,再把对象关联到数据库或散列中。而BITFIELD允许用户自行指定要存储的整数的类型,且不会用对象包裹这些整数,一定程度上减少了带来的内存消耗。
7、总结
- 用位偏移量设置和获取位的值:SETBIT、GETBIT。
- BITCOUNT、BITPOS接受的是字节索引范围,而不是位索引范围。
- BITFIELD执行位运算。
- BITFIELD在位图中存储整数值。
八、地理坐标GEO
Redis GEO是Redis在3.2版本新添加的特性,用户可以将经纬度格式的地理坐标储存到Reids中,并对这些坐标执行距离计算、范围查找等操作。但是极地附近的地理坐标将被限制,不能被存储。总地来说,经纬度的范围如下:
- 精度的范围为-180到180;
- 纬度的范围为 -85.05112878到85.05112878。
Redis使用zset存储GEO数据,每存储一个位置名称以及经纬度时,Redis会通过Geohash算法把经纬度转换为一个独一无二的52位整数,作为分值存储到zset中。(根据Geohash也可以还原出经度和纬度。)因此zset的命令,如ZRANGE、ZCARD、ZREM等也可以使用。
1、GEOADD:添加坐标
命令 | GEOADD geo longitude1 latitude1 member1 [longitude2 latitude2 member2 ...] |
---|---|
效果 | 将给定的一个或多个经纬度坐标存储到位置集合中并设置相应的名字。如果位置已经存在,则更新坐标。 |
返回值 | 返回添加到位置集合的坐标数量(不包含更新的) |
redis> GEOADD geo1 114.05 22.55 shenzhen 113.27 23.13 guangzhou 113.12 23.02 foshan
(integer) 3
2、GEOPOS:根据位置名获取坐标
命令 | GEOPOS geo member1 [member2 ...] |
---|---|
效果 | 获取给定位置的坐标 |
返回值 | 返回一个数组,分别记录每个位置的经度和纬度 |
redis> GEOPOS geo1 shenzhen guangzhou foshan dongguan
1) 1) "114.04999762773513794"
2) "22.5500010475923105"
2) 1) "113.27000051736831665"
2) "23.13000101271457254"
3) 1) "113.12000066041946411"
2) "23.01999918384158406"
4) (nil)
3、GEODIST:计算两个位置之间的直线距离
命令 | GEODIST geo member1 member2 [unit] |
---|---|
效果 | 计算两个位置之间的直线距离。unit指定要使用的单位,默认为米,可选:m、km、mi(英里)、ft(英尺) |
返回值 | 返回直线距离。如果至少有一个成员不存在,返回nil |
redis> GEODIST geo1 shenzhen guangzhou km
"102.7337"
redis> GEODIST geo1 shenzhen foshan
"108755.5425"
redis> GEODIST geo1 shenzhen dongguan ft
(nil)
4、GEORADIUS:查找指定半径范围内的其他位置
命令 | GEORADIUS geo longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE geo] [STOREDIST zset] |
---|---|
效果 | 指定一个经纬度为中心点,从位置集合中找出位于中心点指定半径范围内的其他位置。 如果给定WITHCOORD选项,每返回一个位置还会返回该位置的坐标。 如果给定WITHDIST选项,每返回一个位置还会返回该位置与中心点之间的距离,单位与给定的unit一致。 如果给定WITHHASH选项,每返回一个位置还会返回该位置的geohash,注意返回的是一个整数,与GEOHASH返回的字符串不一样,但这两个值底层的二进制位相同。 如果给定COUNT选项,限制返回的位置数量,必须为正整数。 如果给定ASC选项,将根据中心点到位置的距离由近到远返回这些位置;DESC反之,由远到近。 如果给定STORE选项,会将结果(位置名+经纬度)存储起来。 如果给定STOREDIST选项,会将结果(位置名+距离)存储到有序集合键中。 |
返回值 | 返回这些位置。如果给定了STORE或STOREDIST选项,则返回存储的成员数量。 |
redis> GEORADIUS geo1 113 22 125 km
1) "shenzhen"
2) "foshan"
redis> GEORADIUS geo1 113 22 125 km WITHCOORD
1) 1) "shenzhen"
2) 1) "114.04999762773513794"
2) "22.5500010475923105"
2) 1) "foshan"
2) 1) "113.12000066041946411"
2) "23.01999918384158406"
redis> GEORADIUS geo1 113 22 125 km WITHDIST WITHCOORD ASC
1) 1) "foshan"
2) "114.1188"
3) 1) "113.12000066041946411"
2) "23.01999918384158406"
2) 1) "shenzhen"
2) "124.1843"
3) 1) "114.04999762773513794"
2) "22.5500010475923105"
5、GEORADIUSBYMEMBER:查找指定位置半径范围内的其他位置
命令 | GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE geo] [STOREDIST zset] |
---|---|
效果 | 和GEORADIUS作用一样,不过GEORADIUSBYMEMBER是通过选择位置集合中的一个位置来作为中心点。注意会把作为中心点的自己也返回。 |
返回值 | 返回这些位置(包括自己)。如果给定了STORE或STOREDIST选项,则返回存储的成员数量。 |
6、GEOHASH:获取指定位置的Geohash值
命令 | GEOHASH geo member1 [member2 ...] |
---|---|
效果 | 获取指定位置的Geohash字符串。 |
返回值 | 返回一个长度为11的字符串,相比内部存储的52位整数在信息上略有丢失,但无伤大雅。注意通过GEOHASH获得的字符串和通过GEORADIUS、GEORADIUSBYMEMBER的WITHHASH选项返回的不一样,后者即为有序集合内存储的整数 |
redis> GEOHASH geo1 shenzhen guangzhou
1) "ws10730em80"
2) "ws0e9d8wn20"
九、数据库操作
所有的键,无论是字符串键、散列键、列表键等,都会被存储到数据库这个容器中。Redis默认在启动时创建16个数据库,这些数据库使用从0开始的整数号码作为标识,一直到15号。当我们启动redis-cli客户端时,默认进入0号数据库。
虽然在同一个数据库中不允许键同名,但在不同的数据库中可以存在相同的键。
1、SELECT:切换数据库
命令 | SELECT index |
---|---|
效果 | 切换至指定的数据库 |
返回值 | 返回OK |
2、KEYS:获取所有与给定匹配符相匹配的键
命令 | KEYS pattern |
---|---|
效果 | 获取所有与给定匹配符相匹配的键。 |
返回值 | 返回所有与给定匹配符相匹配的键 |
可使用的匹配符:
匹配符 | 作用 | 示例 |
---|---|---|
* | 匹配0个或任意多个任意字符 | h*llo匹配hllo、heeeello |
? | 匹配任意的单个字符 | h?llo匹配hello、hallo、hxllo |
[] | 匹配括号中的单个字符 | h[ae]llo匹配hello、hallo但是不匹配hillo |
[?-?] | 匹配范围中的单个字符 | h[a-b]llo匹配hallo、hbllo |
redis> KEYS * -- 获取数据库中所有键
3、SCAN、HSCAN、SSCAN、ZSCAN:以渐进方式迭代
SCAN、HSCAN、SSCAN、ZSCAN都能对数据的集合进行迭代:
- SCAN迭代当前数据库中所有键;
- HSCAN迭代指定散列中所有字段和值;
- SSCAN迭代指定集合中所有元素;
- ZSCAN迭代指定有序集合中所有成员和关联分值。
这些迭代都是渐进式的,一次只返回少量数据,而不会像KEYS或者SMEMBERS一次返回所有内容,避免了数据较多时阻塞服务器的情况。
迭代需要用到游标。在一次完整的迭代中,第一次迭代时游标为0。之后每次获得一部分数据的同时,Redis会返回一个新游标,通过重复使用游标来迭代剩余的数据。一旦返回的游标为0,就表示本次迭代已经完成。
对于一次完整的迭代,SCAN等命令有如下特点:
- 在迭代开始前就存在,并且迭代结束后仍然存在的数据一定会被返回,反之在迭代开始前就被移除,并且迭代结束后也没有添加回来的数据一定不会被返回;
- 在迭代开始后才添加的数据,是否被返回是不确定的;
- 同一个数据有可能被返回多次,因此用户需要自己检测和过滤;
- 每次返回的数量是不确定的,甚至可能一个都不返回,但是只要返回的游标不为0,迭代就没有结束;
- 迭代的过程中可以随时停止迭代,而不需要向服务器报告,也可以随时开始一个新的完整迭代。
命令 | SCAN cursor [MATCH pattern] [COUNT count] |
---|---|
效果 | 迭代当前数据库中所有键。cursor是每次迭代时使用的游标,第一次迭代为0。 MATCH选项指定匹配符,不使用MATCH选项相当于KEYS * 的迭代版本。 COUNT选项提供下一次迭代期望返回的数量,但仅仅是期望值,下次迭代返回的键数量仍然是不确定的,当然更大的COUNT有助于获得更多键。不给定COUNT,默认为10。在完整迭代中每次可以使用不一样的COUNT。 |
返回值 | 返回下一次迭代的游标以及本次迭代得到的所有键 |
redis> SCAN 0 MATCH *set*
1) "36"
2) 1) "zset4"
2) "set4"
redis> SCAN 36 MATCH *set*
1) "42"
2) 1) "zset2"
HSCAN迭代指定散列中所有字段和值:
HSCAN hash cursor [MATCH pattern] [COUNT count]
redis> HSET hash2 a 1 b 2 c 3 d 4 e 5
(integer) 5
redis> HSCAN hash2 0
1) "0"
2) 1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
7) "d"
8) "4"
9) "e"
10) "5"
SSCAN迭代指定集合中所有元素:
SSCAN set cursor [MATCH pattern] [COUNT count]
redis> SSCAN set1 0
1) "0"
2) 1) "b"
2) "e"
3) "c"
ZSCAN迭代指定有序集合中所有成员和关联分值:
ZSCAN zset cursor [MATCH pattern] [COUNT count]
redis> ZSCAN zset5 0
1) "0"
2) 1) "b"
2) "2"
3) "c"
4) "3"
4、RANDOMKEY:随机返回一个键
命令 | RANDOMKEY |
---|---|
效果 | 随机返回一个键,不会移除返回的键 |
返回值 | 返回一个键。如果数据库为空,返回nil |
5、EXISTS:检查键是否存在
命令 | EXISTS key1 [key2 ...] |
---|---|
效果 | 检查给定键是否存在 |
返回值 | 返回存在的键的数量 |
6、DEL:移除键
命令 | DEL key1 [key2 ...] |
---|---|
效果 | 移除指定的键 |
返回值 | 返回成功移除的数量 |
7、UNLINK:异步方式移除指定的键
命令 | UNLINK key1 [key2 ...] |
---|---|
效果 | DEL是同步移除的,如果待移除的键非常大或多,服务器在执行过程中可能会被阻塞。UNLINK是异步的,在调用时只会在数据库中移除对该键的引用,实际移除操作交给后台线程执行,不会造成服务器阻塞。 |
返回值 | 返回被移除键的数量 |
8、TYPE:查看键的类型
命令 | TYPE key |
---|---|
效果 | 查看键的类型 |
返回值 | 返回值可能是:string(包括Hyperloglog和位图)、hash、list、set、zset(包括地理位置)、stream。如果键不存在,返回none |
9、RENAME、RENAMENX:修改键名
命令 | RENAME key newkey |
---|---|
效果 | 修改键名。如果key不存在,报错。如果newkey已存在,会先移除newkey,再执行改名操作 |
返回值 | 返回OK。如果key不存在,返回异常。即使key和newkey相同也不会返回异常 |
命令 | RENAMENX key newkey |
---|---|
效果 | 仅当新键名尚未被占用的情况下改名。 |
返回值 | 修改成功返回1,否则返回0。如果key不存在,返回异常 |
10、SORT:对键的值进行排序
SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern] ...] [ASC|DESC] [ALPHA] [STORE destination]
对列表元素、集合元素和有序集合的成员进行排序。
- 使用ASC或DESC选项指定排序方式为升序或降序,不指定默认为升序ASC
- 使用LIMIT选项返回部分结果,默认情况下它等同于LIMIT 0 10
- 默认只进行数字排序,如果值包含非数字,将会返回错误,如果想执行字符串排序操作,使用ALPHA选项
- 使用destination选项将排序结果存储为列表,最后返回的是被存储的元素数量
默认情况下,排序权重就是成员本身,即使是有序集合也是如此(而不是关联分值)。
redis> SMEMBERS set1
1) "b"
2) "e"
3) "c"
redis> SORT set1 ALPHA DESC
1) "e"
2) "c"
3) "b"
redis> LRANGE list2 0 -1
1) "yes"
2) "hello"
3) "world"
4) "hello"
redis> SORT list2 ALPHA
1) "hello"
2) "hello"
3) "world"
4) "yes"
redis> ZRANGE zset3 0 -1
1) "one"
2) "two"
3) "two2"
4) "four"
5) "six"
redis> SORT zset3 ALPHA
1) "four"
2) "one"
3) "six"
4) "two"
5) "two2"
默认情况下返回被排序的元素作为结果,如果使用GET选项可以获取其他值作为结果:
SORT key [GET pattern [GET pattern] ...]
pattern可以是:
1、包含*的字符串,表示被排序的元素与*进行替换,构建出一个键名,再获取该键的值作为返回值。
下面这个例子,SORT的执行过程如下:
①首先对fruit集合的各个元素进行排序;
②将排序后的元素逐个与*-price替换,得到apple-price等键名;
③最后取得这些键名对应值,作为返回结果。
redis> MSET apple-price 8 banana-price 5 mango-price 6
OK
redis> SADD fruits apple banana mango pear
(integer) 4
redis> SORT fruits ALPHA GET *-price
1) "8"
2) "5"
3) "6"
4) (nil)
2、包含*和->的字符串,->前的字符串被视为散列名,->后面的字符串被视为字段名,被排序的元素与*进行替换,构建散列键名,再获取字段的值作为返回值。
redis> HSET apple price 8
(integer) 1
redis> HSET banana price 5
(integer) 1
redis> HSET mango price 6
(integer) 1
redis> SORT fruits ALPHA GET *->price
1) "8"
2) "5"
3) "6"
4) (nil)
3、单独的#,返回这个元素本身
redis> SORT fruits ALPHA GET # GET *->price
1) "apple"
2) "8"
3) "banana"
4) "5"
5) "mango"
6) "6"
7) "pear"
8) (nil)
默认情况下SORT使用被排序元素本身作为排序权重,如果使用BY选项,可以指定其他键的值作为排序权重。
SORT key [BY pattern]
pattern可以是包含*的字符串,也可以是包含*和->的字符串,与GET选项一样。
下面的例子,根据*-price中存储的价格对fruits集合中的水果进行排序。
redis> SORT fruits BY *-price
1) "pear"
2) "banana"
3) "mango"
4) "apple"
仅仅排序还不够直观,应该把*-price的价格也显示出来,也就是使用两个GET分别获取水果的名称和价格
redis> SORT fruits BY *-price GET # GET *-price
1) "pear"
2) (nil)
3) "banana"
4) "5"
5) "mango"
6) "6"
7) "apple"
8) "8"
11、DBSIZE:获取数据库包含的键数量
命令 | DBSIZE |
---|---|
效果 | 获取当前数据库包含的键数量 |
返回值 | 返回键的数量 |
12、MOVE:将键移动到另一个数据库
命令 | MOVE key db |
---|---|
效果 | 将一个键从当前数据库移到另一个数据库。如果key不存在,或者目标数据库存在与key同名的键,不会移动 |
返回值 | 移动成功返回1。未执行移动返回0 |
13、FLUSHDB、FLUSHALL:清空数据库
命令 | FLUSHDB [ASYNC] |
---|---|
效果 | 清空当前数据库。默认为同步,使用ASYNC选项将实际的数据库清空操作放在后台线程中异步执行,以免阻塞服务器 |
返回值 | 返回OK |
命令 | FLUSHALL [ASYNC] |
---|---|
效果 | 清空所有数据库。默认为同步,使用ASYNC选项设为异步执行。 |
返回值 | 返回OK |
14、SWAPDB:互换数据库
命令 | SWAPDB index1 index2 |
---|---|
效果 | 对指定的两个数据库内所有数据互换 |
返回值 | 返回OK |
十、自动过期
Redis的自动过期特性能使特定键在指定时间后自动被移除。注意:自动过期只能对整个键进行设置,而不能对键中的某个元素进行设置。
1、EXPIRE、PEXPIRE:设置生存时间
EXPIRE、PEXPIRE可以为键设置一个生存时间(time to live,TTL),生存时间一旦被设置就会随着时间流逝不断减少,直到变成0后,键就会被移除。
一些命令,如SET等,在设置或覆盖键时就能指定生存时间。
命令 | EXPIRE key seconds |
---|---|
效果 | 为键设置或更新一个秒级精度的生存时间。如果设置为非正数,将导致这个键被立即移除 |
返回值 | 返回1表示设置成功。如果键不存在,返回0 |
命令 | PEXPIRE key milliseconds |
---|---|
效果 | 为键设置或更新一个毫秒级精度的生存时间。如果设置为非正数,将导致这个键被立即移除 |
返回值 | 返回1表示设置成功。如果键不存在,返回0 |
使用覆盖的方式可以清除键的生存时间,例如不带EX选项的SET,GETSET,以及以STORE结尾的一些命令。而那些从字面意思上看只是更改值的命令,例如INCR,LPUSH,HSET等不会影响到生存时间。
2、EXPIREAT、PEXPIREAT:设置过期时间
过期时间是一个UNIX时间戳,键在这个时间戳到来后被移除。
命令 | EXPIREAT key timestamp |
---|---|
效果 | 设置或更新过期时间,timestamp是一个秒级的UNIX时间戳。如果设置为过去的时间,将导致这个键被立即移除 |
返回值 | 返回1表示设置成功。如果键不存在,返回0 |
命令 | PEXPIREAT key milliseconds-timestamp |
---|---|
效果 | 设置或更新过期时间,milliseconds-timestamp是一个毫秒级的UNIX时间戳。如果设置为过去的时间,将导致这个键被立即移除 |
返回值 | 返回1表示设置成功。如果键不存在,返回0 |
3、PERSIST:移除键的过期时间
命令 | PERSIST key |
---|---|
效果 | 移除键的过期时间,使其变为永久存在 |
返回值 | 返回1表示设置成功。如果键不存在或本来就没有过期时间,返回0 |
4、TTL、PTTL:获取键的剩余生存时间
命令 | TTL key |
---|---|
效果 | 获取键的剩余生存时间(单位秒) |
返回值 | 返回以秒为单位的TTL(向下取整)。如果键不存在,返回-2。如果没有设置过期时间,返回-1 |
命令 | PTTL key |
---|---|
效果 | 获取键的剩余生存时间(单位毫秒) |
返回值 | 返回以毫秒为单位的TTL。其他同TTL |
十一、总结及参考资料
本文介绍了Redis常见的五种数据结构(字符串、散列、列表、集合、有序集合),以及位图、Hyperloglog、地理坐标,操作这些数据结构以及数据库键的命令,并介绍了自动过期的特性。还有很多特性没有涉及,如流(stream)这种Redis 5.0新增的数据结构、流水线和事务、发布与订阅等。进一步的学习,可以参考Redis官网的介绍以及其他资料,列举如下:
1.Redis官网
2.Redis中文网
3.《Redis使用手册》黄建宏著 机械工业出版社出版