欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【Redis】基本数据类型及命令操作(超详细)

程序员文章站 2022-10-25 21:49:31
一、String1.1 概述1.2 相关命令列表1.3 命令示例二、List2.1 概述:2.2 相关命令列表:2.3 命令示例:2.4 链表结构的小技巧:三、Hashes3.1 概述3.2 相关命令列表3.3 命令示例四、Set4.1 概述:4.2 相关命令列表:4.3 命令示例:4.4 应用范围... ......

一、string

可以直接看每一大节的命令示例部分,敲一遍就大体会了

1.1 概述

  • 字符串类型是redis中最为基础的数据存储类型,它在redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如jpeg图像数据或json对象描述信息等。在redis中字符串类型的value最多可以容纳的数据长度是512m。

1.2 相关命令列表

此后按照如下的排版进行讲解:

[命令原型]
* [时间复杂度]   [命令描述]
* 返回值
  1. append key value
    • o(1) 如果该key已经存在,append命令将参数value的数据追加到已存在value的末尾。如果该key不存在,append命令将会创建一个新的key/value。
    • 追加后value的长度。
  2. decr key
    • o(1) 将指定key的value原子性的递减1。如果该key不存在,其初始值为0,在decr之后其值为-1。如果value的值不能转换为整型值,如hello,该操作将执行失败并返回相应的错误信息。注意:该操作的取值范围是64位有符号整型
    • 递减后的value值。
  3. incr key
    • o(1) 将指定key的value原子性的递增1。如果该key不存在,其初始值为0,在incr之后其值为1。如果value的值不能转换为整型值,如hello,该操作将执行失败并返回相应的错误信息。注意:该操作的取值范围是64位有符号整型。
    • 递增后的value值。
  4. decrby key decrement
    • o(1) 将指定key的value原子性的减少decrement。如果该key不存在,其初始值为0,在decrby之后其值为-decrement。如果value的值不能转换为整型值,如hello,该操作将执行失败并返回相应的错误信息。注意:该操作的取值范围是64位有符号整型。
    • 减少后的value值。
  5. incrby key increment
    • o(1) 将指定key的value原子性的增加increment。如果该key不存在,其初始值为0,在incrby之后其值为increment。如果value的值不能转换为整型值,如hello,该操作将执行失败并返回相应的错误信息。注意:该操作的取值范围是64位有符号整型。
    • 增加后的value值。
  6. get key
    • o(1) 获取指定key的value。如果与该key关联的value不是string类型,redis将返回错误信息,因为get命令只能用于获取string value。
    • 与该key相关的value,如果该key不存在,返回nil。
  7. set key value
    • o(1) 设定该key持有指定的字符串value,如果该key已经存在,则覆盖其原有值。
    • 总是返回"ok"。
  8. getset key value
    • o(1) 原子性的设置该key为指定的value,同时返回该key的原有值。和get命令一样,该命令也只能处理string value,否则redis将给出相关的错误信息。
    • 返回该key的原有值,如果该key之前并不存在,则返回nil。
  9. strlen key
    • o(1) 返回指定key的字符值长度,如果value不是string类型,redis将执行失败并给出相关的错误信息。
    • 返回指定key的value字符长度,如果该key不存在,返回0。
  10. setex key seconds value
    • o(1) 原子性完成两个操作,一是设置该key的值为指定字符串,同时设置该key在redis服务器中的存活时间(秒数)。该命令主要应用于redis被当做cache服务器使用时。
  11. setnx key value
    • o(1) 如果指定的key不存在,则设定该key持有指定字符串value,此时其效果等价于set命令。相反,如果该key已经存在,该命令将不做任何操作并返回。
    • 1表示设置成功,否则0。
  12. setrange key offset value
    • o(1) 替换指定key的部分字符串值。从offset开始,替换的长度为该命令第三个参数value的字符串长度,其中如果offset的值大于该key的原有值value的字符串长度,redis将会在value的后面补齐(offset - strlen(value))数量的0x00,之后再追加新值。如果该键不存在,该命令会将其原值的长度假设为0,并在其后添补offset个0x00后再追加新值。鉴于字符串value的最大长度为512m,因此offset的最大值为536870911。最后需要注意的是,如果该命令在执行时致使指定key的原有值长度增加,这将会导致redis重新分配足够的内存以容纳替换后的全部字符串,因此就会带来一定的性能折损。
    • 修改后的字符串value长度。
  13. getrange key start end
    • o(1) 如果截取的字符串长度很短,我们可以该命令的时间复杂度视为o(1),否则就是o(n),这里n表示截取的子字符串长度。该命令在截取子字符串时,将以闭区间的方式同时包含start(0表示第一个字符)和end所在的字符,如果end值超过value的字符长度,该命令将只是截取从start开始之后所有的字符数据。
    • 子字符串
  14. setbit key offset value
    • o(1) 设置在指定offset上bit的值,该值只能为1或0,在设定后该命令返回该offset上原有的bit值。如果指定key不存在,该命令将创建一个新值,并在指定的offset上设定参数中的bit值。如果offset大于value的字符长度,redis将拉长value值并在指定offset上设置参数中的bit值,中间添加的bit值为0。最后需要说明的是offset值必须大于0。
    • 在指定offset上的bit原有值。
  15. getbit key offset
    • o(1) 返回在指定offset上bit的值,0或1。如果offset超过string value的长度,该命令将返回0,所以对于空字符串始终返回0。
    • 在指定offset上的bit值。
  16. mget key [key ...]
    • o(n) n表示获取key的数量。返回所有指定keys的values,如果其中某个key不存在,或者其值不为string类型,该key的value将返回nil。
    • 返回一组指定keys的values的列表。
  17. mset key value [key value ...]
    • o(n) n表示指定key的数量。该命令原子性的完成参数中所有key/value的设置操作,其具体行为可以看成是多次迭代执行set命令。
    • 该命令不会失败,始终返回ok。
  18. msetnx key value [key value ...]
    • o(n) n表示指定key的数量。该命令原子性的完成参数中所有key/value的设置操作,其具体行为可以看成是多次迭代执行setnx命令。然而这里需要明确说明的是,如果在这一批keys中有任意一个key已经存在了,那么该操作将全部回滚,即所有的修改都不会生效。
    • 1表示所有keys都设置成功,0则表示没有任何key被修改。

1.3 命令示例

  • 1.set/get/append/strlen
/> redis-cli   #执行redis客户端工具。
redis 127.0.0.1:6379> exists mykey #判断该键是否存在,存在返回1,否则返回0。
(integer) 0
redis 127.0.0.1:6379> append mykey "hello"   #该键并不存在,因此append命令返回当前value的长度。
(integer) 5
redis 127.0.0.1:6379> append mykey " world" #该键已经存在,因此返回追加后value的长度。
(integer) 11
redis 127.0.0.1:6379> get mykey  #通过get命令获取该键,以判断append的结果。
"hello world"
redis 127.0.0.1:6379> set mykey "this is a test" #通过set命令为键设置新值,并覆盖原有值。
ok
redis 127.0.0.1:6379> get mykey
"this is a test"
redis 127.0.0.1:6379> strlen mykey   #获取指定key的字符长度,等效于c库中strlen函数。
(integer) 14
  • 2.incr/decr/incrby/decrby
redis 127.0.0.1:6379> set mykey 20  #设置key的值为20
ok
redis 127.0.0.1:6379> incr mykey    #该key的值递增1
(integer) 21
redis 127.0.0.1:6379> decr mykey  #该key的值递减1
(integer) 20
redis 127.0.0.1:6379> del mykey   #删除已有键。
(integer) 1
redis 127.0.0.1:6379> decr mykey #对空值执行递减操作,其原值被设定为0,递减后的值为-1
(integer) -1
redis 127.0.0.1:6379> del mykey   
(integer) 1
redis 127.0.0.1:6379> incr mykey #对空值执行递增操作,其原值被设定为0,递增后的值为1
(integer) 1
redis 127.0.0.1:6379> set mykey hello #将该键的value设置为不能转换为整型的普通字符串。
ok
redis 127.0.0.1:6379> incr mykey #在该键上再次执行递增操作时,redis将报告错误信息。
(error) err value is not an integer or out of range
redis 127.0.0.1:6379> set mykey 10
ok
redis 127.0.0.1:6379> decrby mykey 5 
(integer) 5
redis 127.0.0.1:6379> incrby mykey 10
(integer) 15

  • 3.getset:
redis 127.0.0.1:6379> incr mycounter    #将计数器的值原子性的递增1
(integer) 1
#在获取计数器原有值的同时,并将其设置为新值,这两个操作原子性的同时完成。
redis 127.0.0.1:6379> getset mycounter 0  
"1"
redis 127.0.0.1:6379> get mycounter  #查看设置后的结果。
"0"
  • 4.setex:
redis 127.0.0.1:6379> setex mykey 10 "hello"   #设置指定key的过期时间为10秒。
ok    
#通过ttl命令查看一下指定key的剩余存活时间(秒数),0表示已经过期,-1表示永不过期。
redis 127.0.0.1:6379> ttl mykey                       
(integer) 4
redis 127.0.0.1:6379> get mykey    #在该键的存活期内我们仍然可以获取到它的value。
"hello"
redis 127.0.0.1:6379> ttl mykey       #该ttl命令的返回值显示,该key已经过期。
(integer) 0
redis 127.0.0.1:6379> get mykey        #获取已过期的key将返回nil。
(nil)

  • 5.setnx:
redis 127.0.0.1:6379> del mykey    #删除该键,以便于下面的测试验证。
(integer) 1
redis 127.0.0.1:6379> setnx mykey "hello"   #该键并不存在,因此该命令执行成功。
(integer) 1
redis 127.0.0.1:6379> setnx mykey "world" #该键已经存在,因此本次设置没有产生任何效果。
(integer) 0
redis 127.0.0.1:6379> get mykey    #从结果可以看出,返回的值仍为第一次设置的值。
"hello"
  • 6.setrange/getrange:
redis 127.0.0.1:6379> set mykey "hello world"    #设定初始值。
ok
redis 127.0.0.1:6379> setrange mykey 6 dd    #从第六个字节开始替换2个字节(dd只有2个字节)
(integer) 11
redis 127.0.0.1:6379> get mykey    #查看替换后的值。
"hello ddrld"
redis 127.0.0.1:6379> setrange mykey 20 dd   #offset已经超过该key原有值的长度了,该命令将会在末尾补0。
(integer) 22
redis 127.0.0.1:6379> get mykey     #查看补0后替换的结果。
"hello ddrld\x00\x00\x00\x00\x00\x00\x00\x00\x00dd"
redis 127.0.0.1:6379> del mykey    #删除该key。
(integer) 1
redis 127.0.0.1:6379> setrange mykey 2 dd    #替换空值。
(integer) 4
redis 127.0.0.1:6379> get mykey      #查看替换空值后的结果。
"\x00\x00dd"   
redis 127.0.0.1:6379> set mykey "0123456789"   #设置新值。
ok
redis 127.0.0.1:6379> getrange mykey 1 2      #截取该键的value,从第一个字节开始,到第二个字节结束。
"12"
redis 127.0.0.1:6379> getrange mykey 1 20   #20已经超过value的总长度,因此将截取第一个字节后面的所有字节。
"123456789"
  • 7.setbit/getbit:
redis 127.0.0.1:6379> del mykey
(integer) 1
redis 127.0.0.1:6379> setbit mykey 7 1       #设置从0开始计算的第七位bit值为1,返回原有bit值0
(integer) 0
redis 127.0.0.1:6379> get mykey     #获取设置的结果,二进制的0000 0001的十六进制值为0x01
"\x01"
redis 127.0.0.1:6379> setbit mykey 6 1       #设置从0开始计算的第六位bit值为1,返回原有bit值0
(integer) 0
redis 127.0.0.1:6379> get mykey  #获取设置的结果,二进制的0000 0011的十六进制值为0x03
"\x03"
redis 127.0.0.1:6379> getbit mykey 6     #返回了指定offset的bit值。
(integer) 1
redis 127.0.0.1:6379> getbit mykey 10     #offset已经超出了value的长度,因此返回0。
(integer) 0
  • 8.mset/mget/msetnx:
redis 127.0.0.1:6379> mset key1 "hello" key2 "world"   #批量设置了key1和key2两个键。
ok
redis 127.0.0.1:6379> mget key1 key2      #批量获取了key1和key2两个键的值。
1) "hello"
2) "world"
#批量设置了key3和key4两个键,因为之前他们并不存在,所以该命令执行成功并返回1。
redis 127.0.0.1:6379> msetnx key3 "study" key4 "zhang" 
(integer) 1
redis 127.0.0.1:6379> mget key3 key4                   
1) "study"
2) "zhang"
#批量设置了key3和key5两个键,但是key3已经存在,所以该命令执行失败并返回0。
redis 127.0.0.1:6379> msetnx key3 "hello" key5 "world" 
(integer) 0
#批量获取key3和key5,由于key5没有设置成功,所以返回nil。
redis 127.0.0.1:6379> mget key3 key5                   
1) "study"
2) (nil)

二、list

2.1 概述:

  • 在redis中,list类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。list中可以包含的最大元素数量是4294967295。
  • 从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是非常低效的。相信对于有良好数据结构基础的开发者而言,这一点并不难理解。

2.2 相关命令列表:

此后按照如下的排版进行讲解:

[命令原型]
* [时间复杂度]   [命令描述]
* 返回值
  1. lpush key value [value ...]
    • o(1) 在指定key所关联的list value的头部插入参数中给出的所有values。如果该key不存在,该命令将在插入之前创建一个与该key关联的空链表,之后再将数据从链表的头部插入。如果该键的value不是链表类型,该命令将返回相关的错误信息。
    • 插入后链表中元素的数量。
  2. lpushx key value
    • o(1) 仅有当参数中指定的key存在时,该命令才会在其所关联的list value的头部插入参数中给出的value,否则将不会有任何操作发生。
    • 插入后链表中元素的数量。
  3. lrange key start stop
    • o(s+n) 时间复杂度中的s为start参数表示的偏移量,n表示元素的数量。该命令的参数start和end都是0-based。即0表示链表头部(leftmost)的第一个元素。其中start的值也可以为负值,-1将表示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。该命令在获取元素时,start和end位置上的元素也会被取出。如果start的值大于链表中元素的数量,空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括start)开始,链表中剩余的所有元素。
    • 返回指定范围内元素的列表。
  4. lpop key
    • o(1) 返回并弹出指定key关联的链表中的第一个元素,即头部元素
    • 如果key不存,返回nil。
  5. llen key
    • o(1) 返回指定key关联的链表中元素的数量,如果该key不存在,则返回0。如果与该key关联的value的类型不是链表,则返回相关的错误信息。
    • 链表中元素的数量。
  6. lrem key count value
    • o(n) 时间复杂度中n表示链表中元素的数量。在指定key关联的链表中,删除前count个值等于value的元素。如果count大于0,从头向尾遍历并删除,如果count小于0,则从尾向头遍历并删除。如果count等于0,则删除链表中所有等于value的元素。如果指定的key不存在,则直接返回0
    • 返回被删除的元素数量。
  7. lset key index value
    • o(n) 时间复杂度中n表示链表中元素的数量。但是设定头部或尾部的元素时,其时间复杂度为o(1)。设定链表中指定位置的值为新值,其中0表示第一个元素,即头部元素,-1表示尾部元素。
    • 如果索引值index超出了链表中元素的数量范围,该命令将返回相关的错误信息。
  8. lindex key index
    • o(n) 时间复杂度中n表示在找到该元素时需要遍历的元素数量。对于头部或尾部元素,其时间复杂度为o(1)。该命令将返回链表中指定位置(index)的元素,index是0-based,表示头部元素,如果index为-1,表示尾部元素。如果与该key关联的不是链表,该命令将返回相关的错误信息。
    • 返回请求的元素,如果index超出范围,则返回nil。
  9. ltrim key start stop
    • o(n) n表示被删除的元素数量。该命令将仅保留指定范围内的元素,从而保证链接中的元素数量相对恒定。start和stop参数都是0-based,0表示头部元素。和其他命令一样,start和stop也可以为负值,-1表示尾部元素。
    • 如果start大于链表的尾部,或start大于stop,该命令不错报错,而是返回一个空的链表,与此同时该key也将被删除。如果stop大于元素的数量,则保留从start开始剩余的所有元素。
  10. linsert key before|after pivot value
    • o(n) 时间复杂度中n表示在找到该元素pivot之前需要遍历的元素数量。这样意味着如果pivot位于链表的头部或尾部时,该命令的时间复杂度为o(1)。该命令的功能是在pivot元素的前面或后面插入参数中的元素value。如果key不存在,该命令将不执行任何操作。如果与key关联的value类型不是链表,相关的错误信息将被返回。
    • 成功插入后链表中元素的数量,如果没有找到pivot,返回-1,如果key不存在,返回0。
  11. rpush key value [value ...] o
    • (1) 在指定key所关联的list value的尾部插入参数中给出的所有values。如果该key不存在,该命令将在插入之前创建一个与该key关联的空链表,之后再将数据从链表的尾部插入。如果该键的value不是链表类型,该命令将返回相关的错误信息。
    • 插入后链表中元素的数量。
  12. rpushx key value
    • o(1) 仅有当参数中指定的key存在时,该命令才会在其所关联的list value的尾部插入参数中给出的value,否则将不会有任何操作发生。
    • 插入后链表中元素的数量。
  13. rpop key
    • o(1) 返回并弹出指定key关联的链表中的最后一个元素,即尾部元素,。如果该key不存,返回nil。
    • 链表尾部的元素。
  14. rpoplpush source destination
    • o(1) 原子性的从与source键关联的链表尾部弹出一个元素,同时再将弹出的元素插入到与destination键关联的链表的头部。如果source键不存在,该命令将返回nil,同时不再做任何其它的操作了。如果source和destination是同一个键,则相当于原子性的将其关联链表中的尾部元素移到该链表的头部。
    • 返回弹出和插入的元素。

2.3 命令示例:

  • 1. lpush/lpushx/lrange
/> redis-cli    ## 在shell提示符下启动redis客户端工具。
redis 127.0.0.1:6379> del mykey
(integer) 1
## mykey键并不存在,该命令会创建该键及与其关联的list,
## 之后在将参数中的values从左到右依次插入。
redis 127.0.0.1:6379> lpush mykey a b c d
(integer) 4
## 取从位置0开始到位置2结束的3个元素。
redis 127.0.0.1:6379> lrange mykey 0 2
1) "d"
2) "c"
3) "b"
## 取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
## mykey2键此时并不存在,因此该命令将不会进行任何操作,其返回值为0。
redis 127.0.0.1:6379> lpushx mykey2 e
(integer) 0
## 可以看到mykey2没有关联任何list value。
redis 127.0.0.1:6379> lrange mykey2 0 -1
(empty list or set)
## mykey键此时已经存在,所以该命令插入成功,并返回链表中当前元素的数量。
redis 127.0.0.1:6379> lpushx mykey e
(integer) 5
## 获取该键的list value的头部元素。
redis 127.0.0.1:6379> lrange mykey 0 0
1) "e"
  • 2. lpop/llen
redis 127.0.0.1:6379> lpush mykey a b c d
(integer) 4
redis 127.0.0.1:6379> lpop mykey
"d"
redis 127.0.0.1:6379> lpop mykey
"c"
## 在执行lpop命令两次后,链表头部的两个元素已经被弹出,
## 此时链表中元素的数量是2
redis 127.0.0.1:6379> llen mykey
(integer) 2
  • 3. lrem/lset/lindex/ltrim
## 为后面的示例准备测试数据。
redis 127.0.0.1:6379> lpush mykey a b c d a c
(integer) 6
## 从头部(left)向尾部(right)变量链表,
## 删除2个值等于a的元素,返回值为实际删除的数量。
redis 127.0.0.1:6379> lrem mykey 2 a
(integer) 2
## 看出删除后链表中的全部元素。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "c"
2) "d"
3) "c"
4) "b"
## 获取索引值为1(头部的第二个元素)的元素值。
redis 127.0.0.1:6379> lindex mykey 1
"d"
## 将索引值为1(头部的第二个元素)的元素值设置为新值e。
redis 127.0.0.1:6379> lset mykey 1 e
ok
## 查看是否设置成功。
redis 127.0.0.1:6379> lindex mykey 1
"e"
## 索引值6超过了链表中元素的数量,该命令返回nil。
redis 127.0.0.1:6379> lindex mykey 6
(nil)
## 设置的索引值6超过了链表中元素的数量,设置失败,该命令返回错误信息。
redis 127.0.0.1:6379> lset mykey 6 hh
(error) err index out of range
## 仅保留索引值0到2之间的3个元素,注意第0个和第2个元素均被保留。
redis 127.0.0.1:6379> ltrim mykey 0 2
ok
## 查看trim后的结果。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "c"
2) "e"
3) "c"

  • 4. linsert
##删除该键便于后面的测试。
redis 127.0.0.1:6379> del mykey
(integer) 1
##为后面的示例准备测试数据。
redis 127.0.0.1:6379> lpush mykey a b c d e
(integer) 5
##在a的前面插入新元素a1。
redis 127.0.0.1:6379> linsert mykey before a a1
(integer) 6
##查看是否插入成功,从结果看已经插入。注意lindex的index值是0-based。
redis 127.0.0.1:6379> lindex mykey 0
"e"
##在e的后面插入新元素e2,从返回结果看已经插入成功。
redis 127.0.0.1:6379> linsert mykey after e e2
(integer) 7
##再次查看是否插入成功。
redis 127.0.0.1:6379> lindex mykey 1
"e2"
##在不存在的元素之前或之后插入新元素,该命令操作失败,并返回-1。
redis 127.0.0.1:6379> linsert mykey after k a
(integer) -1
##为不存在的key插入新元素,该命令操作失败,返回0。
redis 127.0.0.1:6379> linsert mykey1 after a a2
(integer) 0
  • 5. rpush/rpushx/rpop/rpoplpush
##删除该键,以便于后面的测试。
redis 127.0.0.1:6379> del mykey
(integer) 1
##从链表的尾部插入参数中给出的values,插入顺序是从左到右依次插入。
redis 127.0.0.1:6379> rpush mykey a b c d
(integer) 4
##通过lrange的可以获悉rpush在插入多值时的插入顺序。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
##该键已经存在并且包含4个元素,rpushx命令将执行成功,
##并将元素e插入到链表的尾部。
redis 127.0.0.1:6379> rpushx mykey e
(integer) 5
## 通过lindex命令可以看出之前的rpushx命令确实执行成功
## 因为索引值为4的元素已经是新元素了。
redis 127.0.0.1:6379> lindex mykey 4
"e"
##由于mykey2键并不存在,因此该命令不会插入数据,其返回值为0。
redis 127.0.0.1:6379> rpushx mykey2 e
(integer) 0
##在执行rpoplpush命令前,先看一下mykey中链表的元素有哪些,
##注意他们的位置关系。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
##将mykey的尾部元素e弹出,同时再插入到mykey2的头部(原子性的完成这两步操作)。
redis 127.0.0.1:6379> rpoplpush mykey mykey2
"e"
##通过lrange命令查看mykey在弹出尾部元素后的结果。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
##通过lrange命令查看mykey2在插入元素后的结果。
redis 127.0.0.1:6379> lrange mykey2 0 -1
1) "e"
##将source和destination设为同一键,将mykey中的尾部元素移到其头部。
redis 127.0.0.1:6379> rpoplpush mykey mykey
"d"
##查看移动结果。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "d"
2) "a"
3) "b"
4) "c"

2.4 链表结构的小技巧:

  • 针对链表结构的value,redis在其官方文档中给出了一些实用技巧,如rpoplpush命令,下面给出具体的解释。
  • redis链表经常会被用于消息队列的服务,以完成多程序之间的消息交换。假设一个应用程序正在执行lpush操作向链表中添加新的元素,我们通常将这样的程序称之为"生产者(producer)",而另外一个应用程序正在执行rpop操作从链表中取出元素,我们称这样的程序为"消费者(consumer)"。如果此时,消费者程序在取出消息元素后立刻崩溃,由于该消息已经被取出且没有被正常处理,那么我们就可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态的不一致等现象的发生。然而通过使用rpoplpush命令,消费者程序在从主消息队列中取出消息之后再将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时我们还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。

三、hashes

3.1 概述

  • 我们可以将redis中的hashes类型看成具有string key和string value的map容器。所以该类型非常适合于存储值对象的信息。如username、password和age等。如果hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个hash可以存储4294967295个键值对。

3.2 相关命令列表

此后按照如下的排版进行讲解:

[命令原型]
* [时间复杂度]   [命令描述]
* 返回值
  1. hset key field value

    • o(1) 为指定的key设定field/value对,如果key不存在,该命令将创建新key以参数中的field/value对,如果参数中的field在该key中已经存在,则用新值覆盖其原有值。
    • 1表示新的field被设置了新值,0表示field已经存在,用新值覆盖原有值。
  2. hget key field

    • o(1) 返回指定key中指定field的关联值。
    • 返回参数中field的关联值,如果参数中的key或field不存,返回nil。
  3. hexists key field

    • o(1) 判断指定key中的指定field是否存在。
    • 1表示存在,0表示参数中的field或key不存在。
  4. hlen key

    • o(1) 获取该key所包含的field的数量。
    • 返回key包含的field数量,如果key不存在,返回0。
  5. hdel key field [field ...] o(n) 时间复杂度中的n表示参数中待删除的字段数量。从指定key的hashes value中删除参数中指定的多个字段,如果不存在的字段将被忽略。如果key不存在,则将其视为空hashes,并返回0.

    • 实际删除的field数量。
  6. hsetnx key field value

    • o(1) 只有当参数中的key或field不存在的情况下,为指定的key设定field/value对,否则该命令不会进行任何操作。
    • 1表示新的field被设置了新值,0表示key或field已经存在,该命令没有进行任何操作。
  7. hincrby key field increment

    • o(1) 增加指定key中指定field关联的value的值。如果key或field不存在,该命令将会创建一个新key或新field,并将其关联的value初始化为0,之后再指定数字增加的操作。该命令支持的数字是64位有符号整型,即increment可以负数。
    • 返回运算后的值。
  8. hgetall key

    • o(n) 时间复杂度中的n表示key包含的field数量。获取该键包含的所有field/value。其返回格式为一个field、一个value,并以此类推。
    • field/value的列表。
  9. hkeys key

    • o(n) 时间复杂度中的n表示key包含的field数量。返回指定key的所有fields名。
    • field的列表。
  10. hvals key

    • o(n) 时间复杂度中的n表示key包含的field数量。返回指定key的所有values名。
    • value的列表。
  11. hmget key field [field ...]

    • o(n) 时间复杂度中的n表示请求的field数量。获取和参数中指定fields关联的一组values。
    • 如果请求的field不存在,其值返回nil。如果key不存在,该命令将其视为空hash,因此返回一组nil。 返回和请求fields关联的一组values,其返回顺序等同于fields的请求顺序。
  12. hmset key field value [field value ...] * o(n) 时间复杂度中的n表示被设置的field数量。逐对依次设置参数中给出的field/value对。如果其中某个field已经存在,则用新值覆盖原有值。如果key不存在,则创建新key,同时设定参数中的field/value。

3.3 命令示例

  • 1. hset/hget/hdel/hexists/hlen/hsetnx
## 在shell命令行启动redis客户端程序
/> redis-cli
## 给键值为myhash的键设置字段为field1,值为study。
redis 127.0.0.1:6379> hset myhash field1 "study"
(integer) 1
## 获取键值为myhash,字段为field1的值。
redis 127.0.0.1:6379> hget myhash field1
"study"
## myhash键中不存在field2字段,因此返回nil。
redis 127.0.0.1:6379> hget myhash field2
(nil)
## 给myhash关联的hashes值添加一个新的字段field2,其值为zhang。
redis 127.0.0.1:6379> hset myhash field2 "zhang"
(integer) 1
## 获取myhash键的字段数量。
redis 127.0.0.1:6379> hlen myhash
(integer) 2
## 判断myhash键中是否存在字段名为field1的字段,由于存在,返回值为1。
redis 127.0.0.1:6379> hexists myhash field1
(integer) 1
## 删除myhash键中字段名为field1的字段,删除成功返回1。
redis 127.0.0.1:6379> hdel myhash field1
(integer) 1
## 再次删除myhash键中字段名为field1的字段,
## 由于上一条命令已经将其删除,因为没有删除,返回0。
redis 127.0.0.1:6379> hdel myhash field1
(integer) 0
## 判断myhash键中是否存在field1字段,由于上一条命令已经将其删除,因为返回0。
redis 127.0.0.1:6379> hexists myhash field1
(integer) 0
## 通过hsetnx命令给myhash添加新字段field1,
## 其值为zhang,因为该字段已经被删除,所以该命令添加成功并返回1。
redis 127.0.0.1:6379> hsetnx myhash field1 study
(integer) 1
## 由于myhash的field1字段已经通过上一条命令添加成功,
## 因为本条命令不做任何操作后返回0。
redis 127.0.0.1:6379> hsetnx myhash field1 study
(integer) 0
  • 2. hincrby
## 删除该键,便于后面示例的测试。
redis 127.0.0.1:6379> del myhash
(integer) 1
## 准备测试数据,该myhash的field字段设定值1。
redis 127.0.0.1:6379> hset myhash field 5
(integer) 1
## 给myhash的field字段的值加1,返回加后的结果。
redis 127.0.0.1:6379> hincrby myhash field 1
(integer) 6
## 给myhash的field字段的值加-1,返回加后的结果。
redis 127.0.0.1:6379> hincrby myhash field -1
(integer) 5
## 给myhash的field字段的值加-10,返回加后的结果。
redis 127.0.0.1:6379> hincrby myhash field -10
(integer) -5   
  • 3. hgetall/hkeys/hvals/hmget/hmset
## 删除该键,便于后面示例测试。
redis 127.0.0.1:6379> del myhash
(integer) 1
## 为该键myhash,一次性设置多个字段,
## 分别是field1 = "hello", field2 = "world"。
redis 127.0.0.1:6379> hmset myhash field1 "hello" field2 "world"
ok
## 获取myhash键的多个字段,其中field3并不存在,
## 因为在返回结果中与该字段对应的值为nil。
redis 127.0.0.1:6379> hmget myhash field1 field2 field3
1) "hello"
2) "world"
3) (nil)
## 返回myhash键的所有字段及其值,从结果中可以看出,他们是逐对列出的。
redis 127.0.0.1:6379> hgetall myhash
1) "field1"
2) "hello"
3) "field2"
4) "world"
## 仅获取myhash键中所有字段的名字。
redis 127.0.0.1:6379> hkeys myhash
1) "field1"
2) "field2"
## 仅获取myhash键中所有字段的值。
redis 127.0.0.1:6379> hvals myhash
1) "hello"
2) "world"

四、set

4.1 概述:

  • 在redis中,我们可以将set类型看作为没有排序的字符集合,和list类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是,这些操作的时间复杂度为o(1),即常量时间内完成次操作。set可包含的最大元素数量是4294967295。
  • 和list类型不同的是,set集合中不允许出现重复的元素,这一点和c++标准库中的set容器是完全相同的。换句话说,如果多次添加相同元素,set中将仅保留该元素的一份拷贝。和list类型相比,set类型在功能上还存在着一个非常重要的特性,即在服务器端完成多个sets之间的聚合计算操作,如unions、intersections和differences。由于这些操作均在服务端完成,因此效率极高,而且也节省了大量的网络io开销。

4.2 相关命令列表:

此后按照如下的排版进行讲解:

[命令原型]
* [时间复杂度]   [命令描述]
* 返回值
  1. sadd key member [member ...]

    • o(n) 时间复杂度中的n表示操作的成员数量。如果在插入的过程用,参数中有的成员在set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。如果执行该命令之前,该key并不存在,该命令将会创建一个新的set,此后再将参数中的成员陆续插入。如果该key的value不是set类型,该命令将返回相关的错误信息。
    • 本次操作实际插入的成员数量。
  2. scard key

    • o(1) 获取set中成员的数量。
    • 返回set中成员的数量,如果该key并不存在,返回0。
  3. sismember key member

    • o(1) 判断参数中指定成员是否已经存在于与key相关联的set集合中。
    • 1表示已经存在,0表示不存在,或该key本身并不存在。
  4. smembers key

    • o(n) 时间复杂度中的n表示set中已经存在的成员数量。获取与该key关联的set中所有的成员。
    • 返回set中所有的成员。
  5. spop key

    • o(1) 随机的移除并返回set中的某一成员。 由于set中元素的布局不受外部控制,因此无法像list那样确定哪个元素位于set的头部或者尾部。
    • 返回移除的成员,如果该key并不存在,则返回nil。
  6. srem key member [member ...]

    • o(n) 时间复杂度中的n表示被删除的成员数量。从与key关联的set中删除参数中指定的成员,不存在的参数成员将被忽略,如果该key并不存在,将视为空set处理。
    • 从set中实际移除的成员数量,如果没有则返回0。
  7. srandmember key

    • o(1) 和spop一样,随机的返回set中的一个成员,不同的是该命令并不会删除返回的成员。
    • 返回随机位置的成员,如果key不存在则返回nil。
  8. smove source destination member

    • o(1) 原子性的将参数中的成员从source键移入到destination键所关联的set中。因此在某一时刻,该成员或者出现在source中,或者出现在destination中。如果该成员在source中并不存在,该命令将不会再执行任何操作并返回0,否则,该成员将从source移入到destination。如果此时该成员已经在destination中存在,那么该命令仅是将该成员从source中移出。如果和key关联的value不是set,将返回相关的错误信息。
    • 1表示正常移动,0表示source中并不包含参数成员。
  9. sdiff key [key ...]

    • o(n) 时间复杂度中的n表示所有sets中成员的总数量。返回参数中第一个key所关联的set和其后所有keys所关联的sets中成员的差异。如果key不存在,则视为空set。
    • 差异结果成员的集合。
  10. sdiffstore destination key [key ...]

    • o(n) 该命令和sdiff命令在功能上完全相同,两者之间唯一的差别是sdiff返回差异的结果成员,而该命令将差异成员存储在destination关联的set中。如果destination键已经存在,该操作将覆盖它的成员。
    • 返回差异成员的数量。
  11. sinter key [key ...]

    • o(n*m) 时间复杂度中的n表示最小set中元素的数量,m则表示参数中sets的数量。该命令将返回参数中所有keys关联的sets中成员的交集。因此如果参数中任何一个key关联的set为空,或某一key不存在,那么该命令的结果将为空集。
    • 交集结果成员的集合。
  12. sinterstore destination key [key ...]

    • o(n*m) 该命令和sinter命令在功能上完全相同,两者之间唯一的差别是sinter返回交集的结果成员,而该命令将交集成员存储在destination关联的set中。如果destination键已经存在,该操作将覆盖它的成员。
    • 返回交集成员的数量。
  13. sunion key [key ...]

    • o(n) 时间复杂度中的n表示所有sets中成员的总数量。该命令将返回参数中所有keys关联的sets中成员的并集。 并集结果成员的集合。
  14. sunionstore destination key [key ...]

    • o(n) 该命令和sunion命令在功能上完全相同,两者之间唯一的差别是sunion返回并集的结果成员,而该命令将并集成员存储在destination关联的set中。如果destination键已经存在,该操作将覆盖它的成员。
    • 返回并集成员的数量。

4.3 命令示例:

  • 1. sadd/smembers/scard/sismember
## 在shell命令行下启动redis的客户端程序。
/> redis-cli
## 插入测试数据,由于该键myset之前并不存在,因此参数中的三个成员都被正常插入。
redis 127.0.0.1:6379> sadd myset a b c
(integer) 3
## 由于参数中的a在myset中已经存在,因此本次操作仅仅插入了d和e两个新成员。
redis 127.0.0.1:6379> sadd myset a d e
(integer) 2
## 判断a是否已经存在,返回值为1表示存在。
redis 127.0.0.1:6379> sismember myset a
(integer) 1
## 判断f是否已经存在,返回值为0表示不存在。
redis 127.0.0.1:6379> sismember myset f
(integer) 0
## 通过smembers命令查看插入的结果,从结果可以,输出的顺序和插入顺序无关。
redis 127.0.0.1:6379> smembers myset
1) "c"
2) "d"
3) "a"
4) "b"
5) "e"
## 获取set集合中元素的数量。
redis 127.0.0.1:6379> scard myset
(integer) 5

  • 2. spop/srem/srandmember/smove
## 删除该键,便于后面的测试。
redis 127.0.0.1:6379> del myset
(integer) 1
## 为后面的示例准备测试数据。
redis 127.0.0.1:6379> sadd myset a b c d
(integer) 4
## 查看set中成员的位置。
redis 127.0.0.1:6379> smembers myset
1) "c"
2) "d"
3) "a"
4) "b"
## 从结果可以看出,该命令确实是随机的返回了某一成员。
redis 127.0.0.1:6379> srandmember myset
"c"
## set中尾部的成员b被移出并返回,事实上b并不是之前插入的第一个或最后一个成员。
redis 127.0.0.1:6379> spop myset
"b"
## 查看移出后set的成员信息。
redis 127.0.0.1:6379> smembers myset
1) "c"
2) "d"
3) "a"
## 从set中移出a、d和f三个成员,其中f并不存在,因此只有a和d两个成员被移出,返回为2。
redis 127.0.0.1:6379> srem myset a d f
(integer) 2
## 查看移出后的输出结果。
redis 127.0.0.1:6379> smembers myset
1) "c"
## 为后面的smove命令准备数据。
redis 127.0.0.1:6379> sadd myset a b
(integer) 2
redis 127.0.0.1:6379> sadd myset2 c d
(integer) 2
## 将a从myset移到myset2,从结果可以看出移动成功。
redis 127.0.0.1:6379> smove myset myset2 a
(integer) 1
## 再次将a从myset移到myset2,由于此时a已经不是myset的成员了,
## 因此移动失败并返回0。
redis 127.0.0.1:6379> smove myset myset2 a
(integer) 0
## 分别查看myset和myset2的成员,确认移动是否真的成功。
redis 127.0.0.1:6379> smembers myset
1) "b"
redis 127.0.0.1:6379> smembers myset2
1) "c"
2) "d"
3) "a"

  • 3. sdiff/sdiffstore/sinter/sinterstore
##为后面的命令准备测试数据。
redis 127.0.0.1:6379> sadd myset a b c d
(integer) 4
redis 127.0.0.1:6379> sadd myset2 c
(integer) 1
redis 127.0.0.1:6379> sadd myset3 a c e
(integer) 3
##myset和myset2相比,a、b和d三个成员是两者之间的差异成员。
## 再用这个结果继续和 myset3进行差异比较,b和d是myset3不存在的成员。
redis 127.0.0.1:6379> sdiff myset myset2 myset3
1) "d"
2) "b"
##将3个集合的差异成员存在在diffkey关联的set中,并返回插入的成员数量。
redis 127.0.0.1:6379> sdiffstore diffkey myset myset2 myset3
(integer) 2
##查看一下sdiffstore的操作结果。
redis 127.0.0.1:6379> smembers diffkey
1) "d"
2) "b"
##从之前准备的数据就可以看出,这三个set的成员交集只有c。
redis 127.0.0.1:6379> sinter myset myset2 myset3
1) "c"
##将3个集合中的交集成员存储到与interkey关联的set中,并返回交集成员的数量。
redis 127.0.0.1:6379> sinterstore interkey myset myset2 myset3
(integer) 1
##查看一下sinterstore的操作结果。
redis 127.0.0.1:6379> smembers interkey
1) "c"
##获取3个集合中的成员的并集。    
redis 127.0.0.1:6379> sunion myset myset2 myset3
1) "b"
2) "c"
3) "d"
4) "e"
5) "a"
##将3个集合中成员的并集存储到unionkey关联的set中,并返回并集成员的数量。
redis 127.0.0.1:6379> sunionstore unionkey myset myset2 myset3
(integer) 5
##查看一下suiionstore的操作结果。
redis 127.0.0.1:6379> smembers unionkey
1) "b"
2) "c"
3) "d"
4) "e"
5) "a"

4.4 应用范围:

  • 可以使用redis的set数据类型跟踪一些唯一性数据,比如访问某一博客的唯一ip地址信息。对于此场景,我们仅需在每次访问该博客时将访问者的ip存入redis中,set数据类型会自动保证ip地址的唯一性。
  • 充分利用set类型的服务端聚合操作方便、高效的特性,可以用于维护数据对象之间的关联关系。比如所有购买某一电子设备的客户id被存储在一个指定的set中,而购买另外一种电子产品的客户id被存储在另外一个set中,如果此时我们想获取有哪些客户同时购买了这两种商品时,set的intersections命令就可以充分发挥它的方便和效率的优势了。

五、sorted-sets

5.1 概述:

  • sorted-sets和sets类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个set中。它们之间的主要差别是sorted-sets中的每一个成员都会有一个分数(score)与之关联,redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管sorted-sets中的成员必须是唯一的,但是分数(score)却是可以重复的。
  • 在sorted-set中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为集合中成员数量的对数。由于sorted-sets中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的。事实上,redis所具有的这一特征在很多其它类型的数据库中是很难实现的,换句话说,在该点上要想达到和redis同样的高效,在其它数据库中进行建模是非常困难的。

5.2 相关命令列表:

此后按照如下的排版进行讲解:

[命令原型]
* [时间复杂度]   [命令描述]
* 返回值
  1. zadd key score member [score] [member]

    • o(log(n)) 时间复杂度中的n表示sorted-sets中成员的数量。添加参数中指定的所有成员及其分数到指定key的sorted-set中,在该命令中我们可以指定多组score/member作为参数。如果在添加时参数中的某一成员已经存在,该命令将更新此成员的分数为新值,同时再将该成员基于新值重新排序。如果键不存在,该命令将为该键创建一个新的sorted-sets value,并将score/member对插入其中。如果该键已经存在,但是与其关联的value不是sorted-sets类型,相关的错误信息将被返回。
    • 本次操作实际插入的成员数量。
  2. zcard key

    • o(1) 获取与该key相关联的sorted-sets中包含的成员数量。
    • 返回sorted-sets中的成员数量,如果该key不存在,返回0。
  3. zcount key min max

    • o(log(n)+m) 该命令用于获取分数(score)在min和max之间的成员数量。针对min和max参数需要额外说明的是,-inf和+inf分别表示sorted-sets中分数的最高值和最低值。缺省情况下,min和max表示的范围是闭区间范围,即min <= score <= max内的成员将被返回。然而我们可以通过在min和max的前面添加"("字符来表示开区间,如(min max表示min < score <= max,而(min (max表示min < score < max。
    • 分数指定范围内成员的数量。
  4. zincrby key increment member

    • o(log(n)) 该命令将为指定key中的指定成员增加指定的分数。如果成员不存在,该命令将添加该成员并假设其初始分数为0,此后再将其分数加上increment。如果key不存,该命令将创建该key及其关联的sorted-sets,并包含参数指定的成员,其分数为increment参数。如果与该key关联的不是sorted-sets类型,相关的错误信息将被返回。
    • 以字符串形式表示的新分数。
  5. zrange key start stop [withscores]

    • o(log(n)+m) 时间复杂度中的n表示sorted-set中成员的数量,m则表示返回的成员数量。该命令返回顺序在参数start和stop指定范围内的成员,这里start和stop参数都是0-based,即0表示第一个成员,-1表示最后一个成员。如果start大于该sorted-set中的最大索引值,或start > stop,此时一个空集合将被返回。如果stop大于最大索引值,该命令将返回从start到集合的最后一个成员。如果命令中带有可选参数withscores选项,该命令在返回的结果中将包含每个成员的分数值,如value1,score1,value2,score2...。  
      • 返回索引在start和stop之间的成员列表。
  6. zrangebyscore key min max [withscores] [limit offset count]

    • o(log(n)+m) 该命令将返回分数在min和max之间的所有成员,即满足表达式min <= score <= max的成员,其中返回的成员是按照其分数从低到高的顺序返回,如果成员具有相同的分数,则按成员的字典顺序返回。可选参数limit用于限制返回成员的数量范围。可选参数offset表示从符合条件的第offset个成员开始返回,同时返回count个成员。可选参数withscores的含义参照zrange中该选项的说明。最后需要说明的是参数中min和max的规则可参照命令zcount。
    • 返回分数在指定范围内的成员列表。
  7. zrank key member

    • o(log(n)) 时间复杂度中的n表示sorted-set中成员的数量。sorted-set中的成员都是按照分数从低到高的顺序存储,该命令将返回参数中指定成员的位置值,其中0表示第一个成员,它是sorted-set中分数最低的成员。
    • 如果该成员存在,则返回它的位置索引值。否则返回nil。
  8. zrem key member [member ...]

    • o(m log(n)) 时间复杂度中n表示sorted-set中成员的数量,m则表示被删除的成员数量。该命令将移除参数中指定的成员,其中不存在的成员将被忽略。如果与该key关联的value不是sorted-set,相应的错误信息将被返回。
    • 实际被删除的成员数量。
  9. zrevrange key start stop [withscores]

    • o(log(n)+m) 时间复杂度中的n表示sorted-set中成员的数量,m则表示返回的成员数量。该命令的功能和zrange基本相同,唯一的差别在于该命令是通过反向排序获取指定位置的成员,即从高到低的顺序。如果成员具有相同的分数,则按降序字典顺序排序。
    • 返回指定的成员列表。
  10. zrevrank key member

    • o(log(n)) 时间复杂度中的n表示sorted-set中成员的数量。该命令的功能和zrank基本相同,唯一的差别在于该命令获取的索引是从高到低排序后的位置,同样0表示第一个元素,即分数最高的成员。
    • 如果该成员存在,则返回它的位置索引值。否则返回nil。
  11. zscore key member

    • o(1) 获取指定key的指定成员的分数。
    • 如果该成员存在,以字符串的形式返回其分数,否则返回nil。
  12. zrevrangebyscore key max min [withscores] [limit offset count]

    • o(log(n)+m) 时间复杂度中的n表示sorted-set中成员的数量,m则表示返回的成员数量。该命令除了排序方式是基于从高到低的分数排序之外,其它功能和参数含义均与zrangebyscore相同。
    • 返回分数在指定范围内的成员列表。
  13. zremrangebyrank key start stop

    • o(log(n)+m) 时间复杂度中的n表示sorted-set中成员的数量,m则表示被删除的成员数量。删除索引位置位于start和stop之间的成员,start和stop都是0-based,即0表示分数最低的成员,-1表示最后一个成员,即分数最高的成员。
    • 被删除的成员数量。
  14. zremrangebyscore key min max

    • o(log(n)+m) 时间复杂度中的n表示sorted-set中成员的数量,m则表示被删除的成员数量。删除分数在min和max之间的所有成员,即满足表达式min <= score <= max的所有成员。对于min和max参数,可以采用开区间的方式表示,具体规则参照zcount。
    • 被删除的成员数量。

5.3 命令示例:

  • 1. zadd/zcard/zcount/zrem/zincrby/zscore/zrange/zrank
##在shell的命令行下启动redis客户端工具。
/> redis-cli
##添加一个分数为1的成员。
redis 127.0.0.1:6379> zadd myzset 1 "one"
(integer) 1
## 添加两个分数分别是2和3的两个成员。
redis 127.0.0.1:6379> zadd myzset 2 "two" 3 "three"
(integer) 2
## 0表示第一个成员,-1表示最后一个成员。
## withscores选项表示返回的结果中包含每个成员及其分数,否则只返回成员。
redis 127.0.0.1:6379> zrange myzset 0 -1 withscores
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
## 获取成员one在sorted-set中的位置索引值。0表示第一个位置。
redis 127.0.0.1:6379> zrank myzset one
(integer) 0
## 成员four并不存在,因此返回nil。
redis 127.0.0.1:6379> zrank myzset four
(nil)
## 获取myzset键中成员的数量。    
redis 127.0.0.1:6379> zcard myzset
(integer) 3
## 返回与myzset关联的sorted-set中,
## 分数满足表达式1 <= score <= 2的成员的数量。
redis 127.0.0.1:6379> zcount myzset 1 2
(integer) 2
## 删除成员one和two,返回实际删除成员的数量。
redis 127.0.0.1:6379> zrem myzset one two
(integer) 2
## 查看是否删除成功。
redis 127.0.0.1:6379> zcard myzset
(integer) 1
#获取成员three的分数。返回值是字符串形式。
redis 127.0.0.1:6379> zscore myzset three
"3"
## 由于成员two已经被删除,所以该命令返回nil。
redis 127.0.0.1:6379> zscore myzset two
(nil)
## 将成员one的分数增加2,并返回该成员更新后的分数。
redis 127.0.0.1:6379> zincrby myzset 2 one
"3"
## 将成员one的分数增加-1,并返回该成员更新后的分数。
redis 127.0.0.1:6379> zincrby myzset -1 one
"2"
## 查看在更新了成员的分数后是否正确。
redis 127.0.0.1:6379> zrange myzset 0 -1 withscores
1) "one"
2) "2"
3) "two"
4) "2"
5) "three"
6) "3"
 
  • 2. zrangebyscore/zremrangebyrank/zremrangebyscore
redis 127.0.0.1:6379> del myzset
(integer) 1
redis 127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four
(integer) 4
## 获取分数满足表达式1 <= score <= 2的成员。
redis 127.0.0.1:6379> zrangebyscore myzset 1 2
1) "one"
2) "two"
## 获取分数满足表达式1 < score <= 2的成员。
redis 127.0.0.1:6379> zrangebyscore myzset (1 2
1) "two"
## -inf表示第一个成员,+inf表示最后一个成员,
## limit后面的参数用于限制返回成员的自己,
## 2表示从位置索引(0-based)等于2的成员开始,去后面3个成员。
redis 127.0.0.1:6379> zrangebyscore myzset -inf +inf limit 2 3
1) "three"
2) "four"
## 删除分数满足表达式1 <= score <= 2的成员,并返回实际删除的数量。
redis 127.0.0.1:6379> zremrangebyscore myzset 1 2
(integer) 2
## 看出一下上面的删除是否成功。
redis 127.0.0.1:6379> zrange myzset 0 -1
1) "three"
2) "four"
## 删除位置索引满足表达式0 <= rank <= 1的成员。
redis 127.0.0.1:6379> zremrangebyrank myzset 0 1
(integer) 2
## 查看上一条命令是否删除成功。
redis 127.0.0.1:6379> zcard myzset
(integer) 0


  • 3. zrevrange/zrevrangebyscore/zrevrank
## 为后面的示例准备测试数据。
redis 127.0.0.1:6379> del myzset
(integer) 0
redis 127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four
(integer) 4
## 以位置索引从高到低的方式获取并返回此区间内的成员。
redis 127.0.0.1:6379> zrevrange myzset 0 -1 withscores
1) "four"
2) "4"
3) "three"
4) "3"
5) "two"
6) "2"
7) "one"
8) "1"
## 由于是从高到低的排序,所以位置等于0的是four,1是three,并以此类推。
redis 127.0.0.1:6379> zrevrange myzset 1 3
1) "three"
2) "two"
3) "one"
## 由于是从高到低的排序,所以one的位置是3。
redis 127.0.0.1:6379> zrevrank myzset one
(integer) 3
## 由于是从高到低的排序,所以four的位置是0。
redis 127.0.0.1:6379> zrevrank myzset four
(integer) 0
## 获取分数满足表达式3 >= score >= 0的成员,并以相反的顺序输出,
## 即从高到底的顺序。
redis 127.0.0.1:6379> zrevrangebyscore myzset 3 0
1) "three"
2) "two"
3) "one"
## 该命令支持limit选项,其含义等同于zrangebyscore中的该选项,
## 只是在计算位置时按照相反的顺序计算和获取。
redis 127.0.0.1:6379> zrevrangebyscore myzset 4 0 limit 1 2
1) "three"
2) "two"

5.4 应用范围:

  • 可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行zadd命令更新玩家的分数,此后再通过zrange命令获取积分topten的用户信息。当然我们也可以利用zrank命令通过username来获取玩家的排行信息。最后我们将组合使用zrange和zrank命令快速的获取和某个玩家积分相近的其他用户的信息。
  • sorted-sets类型还可用于构建索引数据。

六、key操作

6.1 概述:

  • 此文主要讲述的是与redis数据类型相关的命令,如string、list、set、hashes和sorted-set。这些命令都具有一个共同点,即所有的操作都是针对与key关联的value的。下面主要总结了与key相关的redis命令。学习这些命令对于学习redis是非常重要的基础,也是能够充分挖掘redis潜力的利器。
  • 还是按照前面的格式,给出所有相关命令的明细列表和典型示例,以便于现在的学习和今后的查阅。

6.2 相关命令列表:

此后按照如下的排版进行讲解:

[命令原型]
* [时间复杂度]   [命令描述]
* 返回值
  1. keys pattern

    • o(n) 时间复杂度中的n表示数据库中key的数量。获取所有匹配pattern参数的keys。需要说明的是,在我们的正常操作中应该尽量避免对该命令的调用,因为对于大型数据库而言,该命令是非常耗时的,对redis服务器的性能打击也是比较大的。pattern支持glob-style的通配符格式,如*表示任意一个或多个字符,?表示任意字符,[abc]表示方括号中任意一个字母。
    • 匹配模式的键列表。
  2. del key [key ...]

    • o(n) 时间复杂度中的n表示删除的key数量。从数据库删除中参数中指定的keys,如果指定键不存在,则直接忽略。还需要另行指出的是,如果指定的key关联的数据类型不是string类型,而是list、set、hashes和sorted set等容器类型,该命令删除每个键的时间复杂度为o(m),其中m表示容器中元素的数量。而对于string类型的key,其时间复杂度为o(1)。
    • 实际被删除的key数量。
  3. exists key

    • o(1) 判断指定键是否存在
    • 1表示存在,0表示不存在。
  4. move key db

    • o(1) 将当前数据库中指定的键key移动到参数中指定的数据库中。如果该key在目标数据库中已经存在,或者在当前数据库中并不存在,该命令将不做任何操作并返回0。
    • 移动成功返回1,否则0。
  5. rename key newkey

    • o(1) 为指定指定的键重新命名,如果参数中的两个keys的命令相同,或者是源key不存在,该命令都会返回相关的错误信息。如果newkey已经存在,则直接覆盖。
  6. renamenx key newkey

    • o(1) 如果新值不存在,则将参数中的原值修改为新值。其它条件和rename一致。
    • 1表示修改成功,否则0。
  7. persist key

    • o(1) 如果key存在过期时间,该命令会将其过期时间消除,使该key不再有超时,而是可以持久化存储。
    • 1表示key的过期时间被移出,0表示该key不存在或没有过期时间。
  8. expire key seconds

    • o(1) 该命令为参数中指定的key设定超时的秒数,在超过该时间后,key被自动的删除。如果该key在超时之前被修改,与该键关联的超时将被移除。
    • 1表示超时被设置,0则表示key不存在,或不能被设置。
  9. expireat key timestamp

    • o(1) 该命令的逻辑功能和expire完全相同,唯一的差别是该命令指定的超时时间是绝对时间,而不是相对时间。该时间参数是unix timestamp格式的,即从1970年1月1日开始所流经的秒数
    • 1表示超时被设置,0则表示key不存在,或不能被设置。
  10. ttl key

    • o(1) 获取该键所剩的超时描述。
    • 返回所剩描述,如果该键不存在或没有超时设置,则返回-1。
  11. randomkey

    • o(1) 从当前打开的数据库中随机的返回一个key。
    • 返回的随机键,如果该数据库是空的则返回nil。
  12. type key

    • o(1) 获取与参数中指定键关联值的类型,该命令将以字符串的格式返回。
    • 返回的字符串为string、list、set、hash和zset,如果key不存在返回none。
  13. sort key [by pattern] [limit offset count] [get pattern [get pattern ...]] [asc|desc] [alpha] [store destination]

    • o(n+m*log(m)) 这个命令相对来说是比较复杂的,因此我们这里只是给出最基本的用法,有兴趣的网友可以去参考redis的官方文档。
    • 返回排序后的原始列表。

6.3 命令示例:

  • 1. keys/rename/del/exists/move/renamenx
## 在shell命令行下启动redis客户端工具。
/> redis-cli
## 清空当前选择的数据库,以便于对后面示例的理解。
redis 127.0.0.1:6379> flushdb
ok
## 添加string类型的模拟数据。
redis 127.0.0.1:6379> set mykey 2
ok
redis 127.0.0.1:6379> set mykey2 "hello"
ok
## 添加set类型的模拟数据。
redis 127.0.0.1:6379> sadd mysetkey 1 2 3
(integer) 3
## 添加hash类型的模拟数据。
redis 127.0.0.1:6379> hset mmtest username "study"
(integer) 1
## 根据参数中的模式,获取当前数据库中符合该模式的所有key,
## 从输出可以看出,该命令在执行时并不区分与key关联的value类型。
redis 127.0.0.1:6379> keys my*
1) "mysetkey"
2) "mykey"
3) "mykey2"
## 删除了两个keys。
redis 127.0.0.1:6379> del mykey mykey2
(integer) 2
## 查看一下刚刚删除的key是否还存在,从返回结果看,mykey确实已经删除了。
redis 127.0.0.1:6379> exists mykey
(integer) 0
## 查看一下没有删除的key,以和上面的命令结果进行比较。
redis 127.0.0.1:6379> exists mysetkey
(integer) 1
## 将当前数据库中的mysetkey键移入到id为1的数据库中,
## 从结果可以看出已经移动成功。
redis 127.0.0.1:6379> move mysetkey 1
(integer) 1
## 打开id为1的数据库。
redis 127.0.0.1:6379> select 1
ok
## 查看一下刚刚移动过来的key是否存在,从返回结果看已经存在了。
redis 127.0.0.1:6379[1]> exists mysetkey
(integer) 1
## 在重新打开id为0的缺省数据库。
redis 127.0.0.1:6379[1]> select 0
ok
## 查看一下刚刚移走的key是否已经不存在,从返回结果看已经移走。
redis 127.0.0.1:6379> exists mysetkey
(integer) 0
## 准备新的测试数据。    
redis 127.0.0.1:6379> set mykey "hello"
ok
## 将mykey改名为mykey1
redis 127.0.0.1:6379> rename mykey mykey1
ok
## 由于mykey已经被重新命名,再次获取将返回nil。
redis 127.0.0.1:6379> get mykey
(nil)
## 通过新的键名获取。
redis 127.0.0.1:6379> get mykey1
"hello"
## 由于mykey已经不存在了,所以返回错误信息。
redis 127.0.0.1:6379> rename mykey mykey1
(error) err no such key
## 为renamenx准备测试key
redis 127.0.0.1:6379> set oldkey "hello"
ok
redis 127.0.0.1:6379> set newkey "world"
ok
## 由于newkey已经存在,因此该命令未能成功执行。
redis 127.0.0.1:6379> renamenx oldkey newkey
(integer) 0
## 查看newkey的值,发现它也没有被renamenx覆盖。
redis 127.0.0.1:6379> get newkey
"world"
  • 2. persist/expire/expireat/ttl
## 为后面的示例准备的测试数据。
redis 127.0.0.1:6379> set mykey "hello"
ok
## 将该键的超时设置为100秒。
redis 127.0.0.1:6379> expire mykey 100
(integer) 1
## 通过ttl命令查看一下还剩下多少秒。
redis 127.0.0.1:6379> ttl mykey
(integer) 97
## 立刻执行persist命令,该存在超时的键变成持久化的键,即将该key的超时去掉。
redis 127.0.0.1:6379> persist mykey
(integer) 1
## ttl的返回值告诉我们,该键已经没有超时了。
redis 127.0.0.1:6379> ttl mykey
(integer) -1
## 为后面的expire命令准备数据。
redis 127.0.0.1:6379> del mykey
(integer) 1
redis 127.0.0.1:6379> set mykey "hello"
ok
## 设置该键的超时被100秒。
redis 127.0.0.1:6379> expire mykey 100
(integer) 1
## 用ttl命令看一下当前还剩下多少秒,从结果中可以看出还剩下96秒。
redis 127.0.0.1:6379> ttl mykey
(integer) 96
## 重新更新该键的超时时间为20秒,从返回值可以看出该命令执行成功。
redis 127.0.0.1:6379> expire mykey 20
(integer) 1
## 再用ttl确认一下,从结果中可以看出果然被更新了。
redis 127.0.0.1:6379> ttl mykey
(integer) 17
## 立刻更新该键的值,以使其超时无效。
redis 127.0.0.1:6379> set mykey "world"
ok
## 从ttl的结果可以看出,在上一条修改该键的命令执行后,该键的超时也无效了。
redis 127.0.0.1:6379> ttl mykey
(integer) -1

  • 3. type/randomkey/sort
## 由于mm键在数据库中不存在,因此该命令返回none。
redis 127.0.0.1:6379> type mm
none
## mykey的值是字符串类型,因此返回string。
redis 127.0.0.1:6379> type mykey
string
## 准备一个值是set类型的键。
redis 127.0.0.1:6379> sadd mysetkey 1 2
(integer) 2
## mysetkey的键是set,因此返回字符串set。
redis 127.0.0.1:6379> type mysetkey
set
## 返回数据库中的任意键。
redis 127.0.0.1:6379> randomkey
"oldkey"
## 清空当前打开的数据库。
redis 127.0.0.1:6379> flushdb
ok
## 由于没有数据了,因此返回nil。
redis 127.0.0.1:6379> randomkey
(nil)