Redis快速入门视频课程——随堂笔记(一)

视频课程链接:http://edu.51cto.com/course/14463.html

Redis内存数据库,主讲:汤小洋

一、Redis简介

1. 关于NoSQL

​ NoSQL的全称是Not only SQL,在过去的几年中,NoSQL数据库一度成为高并发、海量数据存储解决方案的代名词,与之相应的产品也呈现出雨后春笋般的生机。然而在众多产品中能够脱颖而出的却屈指可数,如Redis、MongoDB、BerkeleyDB和memcached等内存数据库。由于每种产品所拥有的特征不同,因此它们的应用场景也存在着一定的差异,下面仅给出简单的说明:
​ 1). BerkeleyDB是一种极为流行的开源嵌入式数据库,在更多情况下可用于存储引擎,比如BerkeleyDB在被Oracle收购之前曾作为MySQL的存储引擎,由此可以预见,该产品拥有极好的并发伸缩性,支持事务及嵌套事务,海量数据存储等重要特征,在用于存储实时数据方面具有极高的可用价值。然而需要指出的是,该产品的Licence为GPL,这就意味着它并不是在所有情况下都是免费使用的。
​ 2). 对MongoDB的定义为Oriented-Document数据库服务器,和BerkeleyDB不同的是该数据库可以像其他关系型数据库服务器那样独立的运行并提供相关的数据服务。从该产品的官方文档中我们可以获悉,MongoDB主要适用于高并发的论坛或博客网站,这些网站具有的主要特征是并发访问量高、多读少写、数据量大、逻辑关系简单,以及文档数据作为主要数据源等。和BerkeleyDB一样,该产品的License同为GPL。
​ 3). Redis,典型的NoSQL数据库服务器,和BerkeleyDB相比,它可以作为服务程序独立运行于自己的服务器主机。在很多时候,人们只是将Redis视为Key/Value数据库服务器,然而事实并非如此,在目前的版本中,Redis除了Key/Value之外还支持List、Set、Hash和Ordered Set等数据结构,因此它的用途也更为宽泛。和以上两种产品不同的是,Redis的License是Apache License,就目前而言,它是完全免费。
​ 4). memcached,数据缓存服务器。它们之间的最大区别,memcached只是提供了数据缓存服务,一旦服务器宕机,之前在内存中缓存的数据也将全部消失,因此可以看出memcached没有提供任何形式的数据持久化功能,而Redis则提供了这样的功能。再有就是Redis提供了更为丰富的数据存储结构,如Hash和Set。至于它们的相同点,主要有两个,一是完全免费,再有就是它们的提供的命令形式极为接近。

2. Redis的优势

​ 1). 和其他NoSQL产品相比,Redis的易用性极高,因此对于那些有类似产品使用经验的开发者来说,一两天,甚至是几个小时之后就可以利用Redis来搭建自己的平台了。
​ 2). 在解决了很多通用性问题的同时,也为一些个性化问题提供了相关的解决方案,如索引引擎、统计排名、消息队列服务等。

3. 和关系型数据库的比较

​ 在目前版本的Redis中,提供了对五种不同数据类型的支持,其中只有String类型可以被视为Key-Value结构,而其他的数据类型均有适用于各自特征的应用场景。
​ 相比于关系型数据库,由于其存储结构相对简单,因此Redis并不能对复杂的逻辑关系提供很好的支持,然而在适用于Redis的场景中,我们却可以由此而获得效率上的显著提升。即便如此,Redis还是为我们提供了一些数据库应该具有的基础概念,如:在同一连接中可以选择打开不同的数据库,然而不同的是,Redis中的数据库是通过数字来进行命名的,缺省情况下打开的数据库为0。如果程序在运行过程中打算切换数据库,可以使用Redis的select命令来打开其他数据库,如select 1,如果此后还想再切换回缺省数据库,只需执行select 0即可。
在数据存储方面,Redis遵循了现有NoSQL数据库的主流思想,即Key作为数据检索的唯一标识,我们可以将其简单的理解为关系型数据库中索引的键,而Value则作为数据存储的主要对象,其中每一个Value都有一个Key与之关联,这就好比索引中物理数据在数据表中存储的位置。在Redis中,Value将被视为二进制字节流用于存储任何格式的数据,如Json、XML和序列化对象的字节流等,因此我们也可以将其想象为关系型数据库中的BLOB类型字段。由此可见,在进行数据查询时,我们只能基于Key作为我们查询的条件,当然我们也可以应用Redis中提供的一些技巧将Value作为其他数据的Key。

4. 如何持久化内存数据

​ 缺省情况下,Redis会参照当前数据库中数据被修改的数量,在达到一定的阈值后会将数据库的快照存储到磁盘上,这一点我们可以通过配置文件来设定该阈值。通常情况下,我们也可以将Redis设定为定时保存。如当有1000个以上的键数据被修改时,Redis将每隔60秒进行一次数据持久化操作。缺省设置为,如果有9个或9个以下数据修改时,Redis将每15分钟持久化一次。
​ 从上面提到的方案中可以看出,如果采用该方式,Redis的运行效率将会是非常高效的,每当有新的数据修改发生时,仅仅是内存中的缓存数据发生改变,而这样的改变并不会被立即持久化到磁盘上,从而在绝大多数的修改操作中避免了磁盘IO的发生。然而事情往往是存在其两面性的,在该方法中我们确实得到了效率上的提升,但是却失去了数据可靠性。如果在内存快照被持久化到磁盘之前,Redis所在的服务器出现宕机,那么这些未写入到磁盘的已修改数据都将丢失。为了保证数据的高可靠性,Redis还提供了另外一种数据持久化机制--Append模式。如果Redis服务器被配置为该方式,那么每当有数据修改发生时,都会被立即持久化到磁盘。

二、安装Redis

1. 安装

​ 步骤:

  1. 解压redis-3.2.8.tar.gz

    cd ~/software
    tar -zxf redis-3.2.8.tar.gz
  2. 编译

    cd redis-3.2.8
    make
  3. 安装

    mkdir ~/software/redis-bin
    make install PREFIX=~/software/redis-bin/   #PREFIX选项用来指定安装的位置
  4. 启动redis

    cd ~/software/redis-bin/bin/
    ./redis-server #使用默认配置文件启动,默认配置文件所在目录redis-3.2.8/redis.conf
    或
    cp ~/software/redis-3.2.8/redis.conf myredis.conf  #复制默认配置文件到当前目录,并改名
    ./redis-server myredis.conf #使用指定的配置文件启动

    ​ 补充:可以将~/software/redis-bin/bin/添加到PATH变量中,便于执行命令

    vi ~/.bashrc
    export PATH=$PATH:/Users/wangbo/software/redis-bin/bin
    source   ~/.bashrc
  5. 连接redis

    ./redis-cli  #默认连接本机的6379端口(redis默认使用的端口号)
    或
    ./redis-cli -h IP地址 -p 端口号  #连接指定主机、指定端口的redis,如./redis-cli -h localhost -p 6379

    ​ 测试

    127.0.0.1:6379> set name tom
    127.0.0.1:6379> get name

2. 关闭

​ 两种方式

  • 方式1:在服务器窗口中按 Ctrl+C
  • 方式2:在客户端连接后输入 shutdown 或 直接输入 redis-cli shutdown

​ 查看redis进程

ps aux | grep redis #查看redis的进程信息
或
lsof -i:6379 #查看6379端口的进程信息

3. 配置

​ 编辑配置文件:

$ vi myredis.conf
    daemonize yes           #配置为守护进程,后台启动

    port 6379               #修改监听端口

    #让redis支持远程访问,默认只允许本地访问
    #bind    127.0.0.1      #注释掉该行,允许所有主机访问redis
    protected-mode no       #关闭保护模式

    requirepass itany       #配置redis密码,使用时需要输入:auth itany进行认证,认证后才能操作redis

三、Redis数据类型

1. 简介

​ Redis数据就是以key-value形式来存储的,key只能是字符串类型,value可以是以下五种类型:String、List、Set、Sorted-Sets、Hash

2. String类型

2.1 简介

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

2.2 操作

  • set/get/append/strlen

    $ redis-cli 
    127.0.0.1:6379> select 0                  #切换到第1个数据库,默认共有16个数据库,索引从0开始
    OK
    127.0.0.1:6379> keys *                        #显示所有的键key
    (empty list or set)
    127.0.0.1:6379> set name tom              #设置键
    OK
    127.0.0.1:6379> get name                  #获取键对应的值
    "tom"
    127.0.0.1:6379> exists mykey                  #判断该键是否存在,存在返回1,不存在返回0
    (integer) 0
    127.0.0.1:6379> append mykey "hello"          #如果该键不存在,则创建,返回当前value的长度
    (integer) 5
    127.0.0.1:6379> append mykey " world"         #如果该键已经存在,则追加,返回追加后value的长度
    (integer) 11
    127.0.0.1:6379> get mykey                   #获取mykey的值
    "hello world"
    127.0.0.1:6379> strlen mykey                  #获取mykey的长度
    (integer) 11
    #EX和PX表示失效时间,单位为秒和毫秒,两者不能同时使用;NX表示数据库中不存在时才能设置,XX表示存在时才能设置
    127.0.0.1:6379> set mykey "this is test"  EX 5 NX  
    OK
    127.0.0.1:6379> get mykey
    "this is test"

    ​ 注:命令不区分大小写,但key和value区分大小写

  • incr/decr/incrby/decrby

    127.0.0.1:6379> flushdb                       #清空数据库
    OK
    127.0.0.1:6379> set mykey 20
    OK
    127.0.0.1:6379> incr mykey                #递增1
    (integer) 21
    127.0.0.1:6379> decr mykey                #递减1
    (integer) 20
    127.0.0.1:6379> del mykey                     #删除该键
    (integer) 1
    127.0.0.1:6379> decr mykey
    (integer) -1
    127.0.0.1:6379> del mykey
    (integer) 1
    127.0.0.1:6379> INCR mykey
    (integer) 1
    127.0.0.1:6379> set mykey 'hello'         #将该键的Value设置为不能转换为整型的普通字符串
    OK
    127.0.0.1:6379> incr mykey                #在该键上再次执行递增操作时,Redis将报告错误信息
    (error) ERR value is not an integer or out of range
    127.0.0.1:6379> set mykey 10
    OK
    127.0.0.1:6379> incrby mykey 5                #递增5,即步长
    (integer) 15
    127.0.0.1:6379> decrby mykey 10               #递减10
    (integer) 5
  • getset/setex/setnx

    #   getset    获取的同时并设置新的值
    127.0.0.1:6379> incr mycount                  #将计数器的值原子性的递增1
    (integer) 1
    127.0.0.1:6379> getset mycount 666            #在获取计数器原有值的同时,并将其设置为新值
    "1"
    127.0.0.1:6379> get mycount
    "666"
    
    # setex   设置过期时间
    127.0.0.1:6379> setex mykey 10 "hello"        #设置指定Key的过期时间为10秒,等同于set mykey hello ex 10
    OK
    127.0.0.1:6379> ttl mykey                 #查看指定Key的过期时间(秒数)
    (integer) 8
    
    # setnx   当key不存在时才能设置  
    127.0.0.1:6379> del mykey
    (integer) 0
    127.0.0.1:6379> setnx mykey "aaa"             #key不存在,可以设置,等同于set mykey aaa nx
    (integer) 1
    127.0.0.1:6379> setnx mykey "bbb"         #key存在,不能设置
    (integer) 0
    127.0.0.1:6379> get mykey
    "aaa"
  • setrange/getrange 设置/获取指定索引位置的字符

    127.0.0.1:6379> set mykey "hello world"
    OK
    127.0.0.1:6379> get mykey
    "hello world"
    127.0.0.1:6379> setrange mykey 6 dd           #从索引为6的位置开始替换(索引从0开始)
    (integer) 11
    127.0.0.1:6379> get mykey
    "hello ddrld"
    127.0.0.1:6379> setrange mykey 20 dd        #超过的长度使用0代替
    (integer) 22
    127.0.0.1:6379> get mykey
    "hello ddrld\x00\x00\x00\x00\x00\x00\x00\x00\x00dd"
    127.0.0.1:6379> getrange mykey 3 12           #获取索引为[3,12]之间的内容
    "lo ddrld\x00\x00"

    setbit/getbit 设置/获取指定位的BIT值,应用场景:考勤打卡

    127.0.0.1:6379> del mykey
    (integer) 1
    127.0.0.1:6379> setbit mykey 7 1              #设置从0开始计算的第七位BIT值为1,返回原有BIT值0
    (integer) 0
    127.0.0.1:6379> get mykey                 #获取设置的结果,二进制的0000 0001的十六进制值为0x01
    "\x01"
    127.0.0.1:6379> setbit mykey 6 1              #设置从0开始计算的第六位BIT值为1,返回原有BIT值0
    (integer) 0
    127.0.0.1:6379> get mykey                 #获取设置的结果,二进制的0000 0011的十六进制值为0x03
    "\x03"
    127.0.0.1:6379> getbit mykey 6            #返回了指定Offset的BIT值
    (integer) 1
    127.0.0.1:6379> getbit mykey 10               #如果offset已经超出了value的长度,则返回0
    (integer) 0
  • mset/mget/msetnx

    127.0.0.1:6379> mset key1 "hello" key2 "world"    #批量设置了key1和key2两个键。
    OK
    127.0.0.1:6379> mget key1 key2                    #批量获取了key1和key2两个键的值。
    1) "hello"
    2) "world"
    #批量设置了key3和key4两个键,因为之前他们并不存在,所以该命令执行成功并返回1
    127.0.0.1:6379> msetnx key3 "itany" key4 "liu" 
    (integer) 1
    127.0.0.1:6379> mget key3 key4                   
    1) "itany"
    2) "liu"
    #批量设置了key3和key5两个键,但是key3已经存在,所以该命令执行失败并返回0
    127.0.0.1:6379> msetnx key3 "hello" key5 "world" 
    (integer) 0
    #批量获取key3和key5,由于key5没有设置成功,所以返回nil
    127.0.0.1:6379> mget key3 key5                   
    1) "itany"
    2) (nil)

3. List类型

3.1 概述

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

3.2 操作

  • lpush/lpushx/lrange

    127.0.0.1:6379> flushdb
    OK
    #创建键mykey及与其关联的List,然后将参数中的values从左到右依次插入
    127.0.0.1:6379> lpush mykey a b c d
    (integer) 4
    #获取从位置0开始到位置2结束的3个元素
    127.0.0.1:6379> lrange mykey 0 2
    1) "d"
    2) "c"
    3) "b"
    #获取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素
    127.0.0.1:6379> lrange mykey 0 -1
    1) "d"
    2) "c"
    3) "b"
    4) "a"
    #获取从倒数第3个到倒数第2个的元素
    127.0.0.1:6379> lrange mykey -3 -2
    1) "c"
    2) "b"
    #lpushx表示键存在时才能插入,mykey2键此时并不存在,因此该命令将不会进行任何操作,其返回值为0
    127.0.0.1:6379> lpushx mykey2 e       
    (integer) 0
    #可以看到mykey2没有关联任何List Value
    127.0.0.1:6379> lrange mykey2 0 -1
    (empty list or set)
    #mykey键此时已经存在,所以该命令插入成功,并返回链表中当前元素的数量
    127.0.0.1:6379> lpushx mykey e
    (integer) 5
    #获取该键的List中的第一个元素
    127.0.0.1:6379> lrange mykey 0 0
    1) "e"
  • lpop/llen

    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> lpush mykey a b c d
    (integer) 4
    #取出链表头部的元素,该元素在链表中就已经不存在了
    127.0.0.1:6379> lpop mykey
    "d"
    127.0.0.1:6379> lpop mykey
    "c"
    #在执行lpop命令两次后,链表头部的两个元素已经被弹出,此时链表中元素的数量是2
    127.0.0.1:6379> llen mykey
    (integer) 2
  • lrem/lindex/lset/ltrim

    127.0.0.1:6379> flushdb
    OK
    #准备测试数据
    127.0.0.1:6379> lpush mykey a b c d a c
    (integer) 6
    #从头部(left)向尾部(right)操作链表,删除2个值等于a的元素,返回值为实际删除的数量
    127.0.0.1:6379> lrem mykey 2 a
    (integer) 2
    #查看删除后链表中的全部元素
    127.0.0.1:6379> lrange mykey 0 -1
    1) "c"
    2) "d"
    3) "c"
    4) "b"
    #获取索引值为1(头部的第二个元素)的元素值
    127.0.0.1:6379> lindex mykey 1
    "d"
    #将索引值为1(头部的第二个元素)的元素值设置为新值e
    127.0.0.1:6379> lset mykey 1 e
    OK
    #查看是否设置成功
    127.0.0.1:6379> lindex mykey 1
    "e"
    #索引值6超过了链表中元素的数量,该命令返回nil
    127.0.0.1:6379> lindex mykey 6
    (nil)
    #设置的索引值6超过了链表中元素的数量,设置失败,该命令返回错误信息。
    127.0.0.1:6379> lset mykey 6 h
    (error) ERR index out of range
    #仅保留索引值0到2之间的3个元素,注意第0个和第2个元素均被保留。
    127.0.0.1:6379> ltrim mykey 0 2
    OK
    #查看trim后的结果
    127.0.0.1:6379> lrange mykey 0 -1
    1) "c"
    2) "e"
    3) "c"
  • linsert

    127.0.0.1:6379> del mykey
    (integer) 1
    #准备测试数据
    127.0.0.1:6379> lpush mykey a b c d e
    (integer) 5
    #在a的前面插入新元素a1
    127.0.0.1:6379> linsert mykey before a a1
    (integer) 6
    #查看是否插入成功,从结果看已经插入
    127.0.0.1:6379> lrange mykey 0 -1
    1) "e"
    2) "d"
    3) "c"
    4) "b"
    5) "a1"
    6) "a"
    #在e的后面插入新元素e2,从返回结果看已经插入成功
    127.0.0.1:6379> linsert mykey after e e2
    (integer) 7
    #再次查看是否插入成功
    127.0.0.1:6379> lrange mykey 0 -1
    1) "e"
    2) "e2"
    3) "d"
    4) "c"
    5) "b"
    6) "a1"
    7) "a"
    #在不存在的元素之前或之后插入新元素,该命令操作失败,并返回-1
    127.0.0.1:6379> linsert mykey after k a
    (integer) -1
    #为不存在的Key插入新元素,该命令操作失败,返回0
    127.0.0.1:6379> linsert mykey1 after a a2
    (integer) 0
  • rpush/rpushx/rpop/rpoplpush

    127.0.0.1:6379> del mykey
    (integer) 1
    #从链表的尾部插入参数中给出的values,插入顺序是从左到右依次插入
    127.0.0.1:6379> rpush mykey a b c d
    (integer) 4
    #查看链表中的元素,注意元素的顺序
    127.0.0.1:6379> lrange mykey 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    #该键已经存在并且包含4个元素,rpushx命令将执行成功,并将元素e插入到链表的尾部。
    127.0.0.1:6379> rpushx mykey e
    (integer) 5
    #由于mykey2键并不存在,因此该命令不会插入数据,其返回值为0。
    127.0.0.1:6379> rpushx mykey2 e
    (integer) 0
    #查看链表中所有的元素
    127.0.0.1:6379> lrange mykey 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    5) "e"
    #从尾部(right)弹出元素,即取出元素
    127.0.0.1:6379> rpop mykey
    "e"
    #查看链表中所有的元素
    127.0.0.1:6379> lrange mykey 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    #创建mykey2
    127.0.0.1:6379> lpush mykey2 m n 
    (integer) 2
    #将mykey的尾部元素弹出,然后插入到mykey2的头部(原子性的完成这两步操作)
    127.0.0.1:6379> rpoplpush mykey mykey2
    "d"
    #通过lrange命令查看mykey在弹出尾部元素后的结果
    127.0.0.1:6379> lrange mykey 0 -1
    1) "a"
    2) "b"
    3) "c"
    #通过lrange命令查看mykey2在插入元素后的结果
    127.0.0.1:6379> lrange mykey2 0 -1
    1) "d"
    2) "n"
    3) "m"
    127.0.0.1:6379> 
    #将source和destination设为同一键,将mykey中的尾部元素移到其头部
    127.0.0.1:6379> rpoplpush mykey mykey
    "c"
    #查看结果
    127.0.0.1:6379> lrange mykey 0 -1
    1) "c"
    2) "a"
    3) "b"

4. Set类型

4.1 概述

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

4.2 操作

  • sadd/smembers/sismember/scard

    #由于该键myset之前并不存在,因此参数中的三个成员都被正常插入
    127.0.0.1:6379> sadd myset a b c
    (integer) 3
    #查看集合中的元素,从结果可以,输出的顺序和插入顺序无关(无序的)
    127.0.0.1:6379> smembers myset
    1) "a"
    2) "c"
    3) "b"
    #由于参数中的a在myset中已经存在,因此本次操作仅仅插入了d和e两个新成员(不允许重复)
    127.0.0.1:6379> sadd myset a d e
    (integer) 2
    #查看插入的结果
    127.0.0.1:6379> smembers myset
    1) "a"
    2) "c"
    3) "d"
    4) "b"
    5) "e"
    #判断a是否已经存在,返回值为1表示存在
    127.0.0.1:6379> sismember myset a
    (integer) 1
    #判断f是否已经存在,返回值为0表示不存在
    127.0.0.1:6379> sismember myset f
    (integer) 0
    #获取集合中元素的数量
    127.0.0.1:6379> scard myset
    (integer) 5
  • srandmember/spop/srem/smove

    127.0.0.1:6379> del myset
    (integer) 1
    #准备测试数据
    127.0.0.1:6379> sadd myset a b c d
    (integer) 4
    #查看集合中的元素
    127.0.0.1:6379> smembers myset
    1) "c"
    2) "d"
    3) "a"
    4) "b"
    #随机返回一个成员,成员还在集合中
    127.0.0.1:6379> srandmember myset
    "c"
    #取出一个成员,成员会从集合中删除
    127.0.0.1:6379> spop myset
    "b"
    #查看移出后Set的成员信息
    127.0.0.1:6379> smembers myset
    1) "c"
    2) "d"
    3) "a"
    #从Set中移出a、d和f三个成员,其中f并不存在,因此只有a和d两个成员被移出,返回为2
    127.0.0.1:6379> srem myset a d f
    (integer) 2
    #查看移出后的输出结果
    127.0.0.1:6379> smembers myset
    1) "c"
    
    127.0.0.1:6379> del myset
    (integer) 1
    #为后面的smove命令准备数据
    127.0.0.1:6379> sadd myset a b
    (integer) 2
    127.0.0.1:6379> sadd myset2 c d
    (integer) 2
    #将a从myset移到myset2,从结果可以看出移动成功
    127.0.0.1:6379> smove myset myset2 a
    (integer) 1
    #再次将a从myset移到myset2,由于此时a已经不是myset的成员了,因此移动失败并返回0。
    127.0.0.1:6379> smove myset myset2 a
    (integer) 0
    #分别查看myset和myset2的成员,确认移动是否真的成功。
    127.0.0.1:6379> smembers myset
    1) "b"
    127.0.0.1:6379> smembers myset2
    1) "c"
    2) "d"
    3) "a"
  • sdiff/sdiffstore/sinter/sinterstore/sunion/sunionstore

    127.0.0.1:6379> flushdb
    OK
    #准备测试数据
    127.0.0.1:6379> sadd myset a b c d
    (integer) 4
    127.0.0.1:6379> sadd myset2 c
    (integer) 1
    127.0.0.1:6379> sadd myset3 a c e
    (integer) 3
    #获取多个集合之间的不同成员,要注意匹配的规则
    #先将myset和myset2进行比较,a、b和d三个成员是两者之间的差异成员,然后再用这个结果继续和myset3进行差异比较,b和d是myset3不存在的成员
    127.0.0.1:6379> sdiff myset myset2 myset3
    1) "d"
    2) "b"
    127.0.0.1:6379> sdiff myset3 myset2 myset
    1) "e"
    #将3个集合的差异成员存储到与diffkey关联的Set中,并返回插入的成员数量
    127.0.0.1:6379> sdiffstore diffkey myset myset2 myset3
    (integer) 2
    #查看一下sdiffstore的操作结果
    127.0.0.1:6379> smembers diffkey
    1) "d"
    2) "b"
    #获取多个集合之间的交集,这三个Set的成员交集只有c
    127.0.0.1:6379> sinter myset myset2 myset3
    1) "c"
    #将3个集合中的交集成员存储到与interkey关联的Set中,并返回交集成员的数量
    127.0.0.1:6379> sinterstore interkey myset myset2 myset3
    (integer) 1
    #查看一下sinterstore的操作结果
    127.0.0.1:6379> smembers interkey
    1) "c"
    #获取多个集合之间的并集
    127.0.0.1:6379> sunion myset myset2 myset3
    1) "b"
    2) "c"
    3) "d"
    4) "e"
    5) "a"
    #将3个集合中成员的并集存储到unionkey关联的set中,并返回并集成员的数量
    127.0.0.1:6379> sunionstore unionkey myset myset2 myset3
    (integer) 5
    #查看一下sunionstore的操作结果
    127.0.0.1:6379> smembers unionkey
    1) "b"
    2) "c"
    3) "d"
    4) "e"
    5) "a"

4.3 应用范围

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

5. Sorted-Sets类型

5.1 概述

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

5.2 操作

  • zadd/zrange/zcard/zrank/zcount/zrem/zscore/zincrby

    #添加一个分数为1的成员
    127.0.0.1:6379> zadd myzset 1 "one"
    (integer) 1
    #添加两个分数分别是2和3的两个成员
    127.0.0.1:6379> zadd myzset 2 "two" 3 "three"
    (integer) 2
    #通过索引获取元素,0表示第一个成员,-1表示最后一个成员。WITHSCORES选项表示返回的结果中包含每个成员及其分数,否则只返回成员
    127.0.0.1:6379> zrange myzset 0 -1 WITHSCORES
    1) "one"
    2) "1"
    3) "two"
    4) "2"
    5) "three"
    6) "3"
    #获取myzset键中成员的数量  
    127.0.0.1:6379> zcard myzset
    (integer) 3
    #获取成员one在集合中的索引,0表示第一个位置
    127.0.0.1:6379> zrank myzset one
    (integer) 0
    #成员four并不存在,因此返回nil
    127.0.0.1:6379> zrank myzset four
    (nil)
    #获取符合指定条件的成员数量,分数满足表达式1 <= score <= 2的成员的数量
    127.0.0.1:6379> zcount myzset 1 2
    (integer) 2
    #删除成员one和two,返回实际删除成员的数量
    127.0.0.1:6379> zrem myzset one two
    (integer) 2
    #查看是否删除成功
    127.0.0.1:6379> zcard myzset
    (integer) 1
    #获取成员three的分数。返回值是字符串形式
    127.0.0.1:6379> zscore myzset three
    "3"
    #由于成员two已经被删除,所以该命令返回nil
    127.0.0.1:6379> zscore myzset two
    (nil)
    #将成员three的分数增加2,并返回该成员更新后的分数
    127.0.0.1:6379> zincrby myzset 2 three
    "5"
    #将成员three的分数增加-1,并返回该成员更新后的分数
    127.0.0.1:6379> zincrby myzset -1 three
    "4"
    #查看在更新了成员的分数后是否正确
    127.0.0.1:6379> zrange myzset 0 -1 withscores
    1) "three"
    2) "4"
  • zrangebyscore/zremrangebyscore/zremrangebyrank

    127.0.0.1:6379> del myzset
    (integer) 1
    127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four
    (integer) 4
    #通过分数获取元素,获取分数满足表达式1 <= score <= 2的成员
    127.0.0.1:6379> zrangebyscore myzset 1 2
    1) "one"
    2) "two"
    #-inf表示第一个成员,+inf表示最后一个成员,limit后面的参数用于限制返回成员的数量,
    #2表示从位置索引(0-based)等于2的成员开始,取后面3个成员,类似于MySQL中的limit
    127.0.0.1:6379> zrangebyscore myzset -inf +inf withscores limit 2 3
    1) "three"
    2) "3"
    3) "four"
    4) "4"
    #根据分数删除成员,删除分数满足表达式1 <= score <= 2的成员,并返回实际删除的数量
    127.0.0.1:6379> zremrangebyscore myzset 1 2
    (integer) 2
    #看出一下上面的删除是否成功
    127.0.0.1:6379> zrange myzset 0 -1
    1) "three"
    2) "four"
    #根据索引删除成员,删除索引满足表达式0 <= rank <= 1的成员
    127.0.0.1:6379> zremrangebyrank myzset 0 1
    (integer) 2
    #查看上一条命令是否删除成功
    127.0.0.1:6379> zcard myzset
    (integer) 0
  • zrevrange/zrevrangebyscore/zrevrank

    127.0.0.1:6379> del myzset
    (integer) 0
    127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four
    (integer) 4
    #按索引从高到低的方式获取成员
    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,并以此类推
    127.0.0.1:6379> zrevrange myzset 1 3
    1) "three"
    2) "two"
    3) "one"
    #按索引从高到低的方式根据分数获取成员,分数满足表达式3 >= score >= 0的成员
    127.0.0.1:6379> zrevrangebyscore myzset 3 0
    1) "three"
    2) "two"
    3) "one"
    #limit选项的含义等同于zrangebyscore中的该选项,只是在计算位置时按照相反的顺序计算和获取
    127.0.0.1:6379> zrevrangebyscore myzset 4 0 limit 1 2
    1) "three"
    2) "two"
    #获取成员one在集合中的索引,由于是从高到低的排序,所以one的位置是3
    127.0.0.1:6379> zrevrank myzset one
    (integer) 3
    #由于是从高到低的排序,所以four的位置是0
    127.0.0.1:6379> zrevrank myzset four
    (integer) 0

5.3 应用范围

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

6. Hash(哈希)类型

6.1 概述

可以将Redis中的Hash类型看成具有String Key和String Value的map容器。所以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash可以存储4294967295个键值对。

6.2 操作

  • hset/hget/hlen/hexists/hdel/hsetnx

    #给键值为myhash的键设置字段为field1,值为itany
    127.0.0.1:6379> hset myhash field1 "itany"
    (integer) 1
    #获取键值为myhash,字段为field1的值
    127.0.0.1:6379> hget myhash field1
    "itany"
    #myhash键中不存在field2字段,因此返回nil
    127.0.0.1:6379> hget myhash field2
    (nil)
    #给myhash关联的Hashes值添加一个新的字段field2,其值为liu
    127.0.0.1:6379> hset myhash field2 "liu"
    (integer) 1
    #获取myhash键的字段数量
    127.0.0.1:6379> hlen myhash
    (integer) 2
    #判断myhash键中是否存在字段名为field1的字段,由于存在,返回值为1
    127.0.0.1:6379> hexists myhash field1
    (integer) 1
    #删除myhash键中字段名为field1的字段,删除成功返回1
    127.0.0.1:6379> hdel myhash field1
    (integer) 1
    #再次删除myhash键中字段名为field1的字段,由于上一条命令已经将其删除,因为没有删除,返回0
    127.0.0.1:6379> hdel myhash field1
    (integer) 0
    #通过hsetnx命令给myhash添加新字段field1,其值为itany,因为该字段已经被删除,所以该命令添加成功并返回1
    127.0.0.1:6379> hsetnx myhash field1 "itany"
    (integer) 1
    #由于myhash的field1字段已经通过上一条命令添加成功,因为本条命令不做任何操作后返回0
    127.0.0.1:6379> hsetnx myhash field1 "itany"
    (integer) 0
    
  • hincrby

    127.0.0.1:6379> del myhash
    (integer) 1
    #准备测试数据
    127.0.0.1:6379> hset myhash field 5
    (integer) 1
    #给myhash的field字段的值加1,返回加后的结果
    127.0.0.1:6379> hincrby myhash field 1
    (integer) 6
    #给myhash的field字段的值加-1,返回加后的结果
    127.0.0.1:6379> hincrby myhash field -1
    (integer) 5
    #给myhash的field字段的值加-10,返回加后的结果
    127.0.0.1:6379> hincrby myhash field -10
    (integer) -5  
  • hmset/hmget/hgetall/hkeys/hvals

    127.0.0.1:6379> del myhash
    (integer) 1
    #为该键myhash,一次性设置多个字段,分别是field1 = "hello", field2 = "world"
    127.0.0.1:6379> hmset myhash field1 "hello" field2 "world"
    OK
    #获取myhash键的多个字段,其中field3并不存在,因为在返回结果中与该字段对应的值为nil
    127.0.0.1:6379> hmget myhash field1 field2 field3
    1) "hello"
    2) "world"
    3) (nil)
    #返回myhash键的所有字段及其值,从结果中可以看出,他们是逐对列出的
    127.0.0.1:6379> hgetall myhash
    1) "field1"
    2) "hello"
    3) "field2"
    4) "world"
    #仅获取myhash键中所有字段的名字
    127.0.0.1:6379> hkeys myhash
    1) "field1"
    2) "field2"
    #仅获取myhash键中所有字段的值
    127.0.0.1:6379> hvals myhash
    1) "hello"
    2) "world" 

猜你喜欢

转载自blog.51cto.com/12402007/2154871