redis的基本使用

Redis简介

Redis 是完全开源免费的,遵守BSD协议,高性能的基于键值对(key-value)的NoSQL(Not Only SQL)数据库。

  • SQL (Struct Query Lanauge 结构化的查询语言) 。引申含义 RDBMS产品,传统的关系型数据库,存储格式化的表格数据。

  • NOSQL (Not Only SQL )不仅仅只有关系型数据库。引申含义:非关系型数据库。存储半格式化和非格式化的数据,如k-v、json、xml

关系型数据库的特点

关系型数据库特点:

  • 存储结构化数据:多行多列的表结构数据。

  • 强事务:支持完整的ACID事务特性。

  • 在磁盘中存储数据:可通过索引加速检索。

  • 数据量到达一定量级,查询性能就会出现瓶颈:根据表字段不同,在百万或者千万级时会遇到。

  • 支持SQL:大部分的SQL语句都可以跨越数据库。

  • 原生不支持分布式:需要借助第3方中间件分库分表,以应对海量存储需求。

Redis的特点

Redis的特点:

  • 存储非结构化的数据:k-v方式存储数据。

  • 弱事务:支持事务的部分特性。

  • 在内存中存储数据,但能同时持久化数据到磁盘。

  • 查询性能非常好,但不支持连接查询。

  • 不支持SQL,需要使用特定的命令。

  • 天然支持集群、数据分片,扩展容易。

Redis就是一个在内存中,存储k-v格式数据,支持自动持久化的NoSQL型数据库

Redis开发中的应用

典型应用:

  • 在一定程度上代替关系型数据库,保存需要持久化,但对事务要求不高的数据:购物车

  • 充当应用中的缓存,Redis查询性能非常好,可支持的并发远高于关系型数据库。可以优先从Redis中查询数据,缓存为命中后,再从数据库中查询并缓存数据到Redis中,以提升程序性能。

  • Redis提供了丰富的数据类型,可以解决关系型数据库不方便解决的问题,比如:排行榜(热度、时间等维度)、计数器(播放量、浏览数)、社交网络(赞\踩、粉丝、共同好友)

  • 解决分布式系统带来的新问题,比如对tomcat集群的session进行统一的存储管理。

说明:Redis并不是万金油,有很多适合它解决的问题,但是也有很多不合适它解决的问题。

  • 从数据规模的角度上看,数据可以分为大规模数据小规模数据,Redis的数据是存放在内存中的,虽然现在内存已经足够便宜,但是如果数据量非常大,例如每天有几亿的用户行为数据,使用Redis来存储的话,基本上是个无底洞,经济成本相当的高。

  • 从数据冷热的角度上看,数据分为热数据和冷数据。热数据通常是指需要频繁操作的数据,反之为冷数据。如果将这些冷数据放在Redis中,基本上是对于内存的一种浪费,但是对于一些热数据可以放在Redis中加速读写,也可以减轻后端存储的负载,可以说是事半功倍。

Redis的安装

安装Redis

  1. 上传 redis-5.x.x.tar.gz 到linux虚拟机的 /opt 文件夹

  2. 安装gcc

    [root@localhost ~]# yum install -y gcc
  3. 解压缩 redis-5.x.x.tar.gz

    [root@localhost opt]# tar xzvf redis-5.0.10.tar.gz
  4. 进入到redis根目录,进行编译、安装

    [root@localhost opt]# cd redis-5.0.10
    [root@localhost redis-5.0.10]# make
    [root@localhost redis-5.0.10]# make install
  5. redis-5.x.x/redis.conf 复制到 /etc/redis/目录下

    [root@localhost redis-5.0.10]# mkdir -p /etc/redis/
    [root@localhost redis-5.0.10]# cp redis.conf /etc/redis/
  6. 启动redis:通过redis-server启动redis

           
    
    [root@localhost redis-5.0.10]# redis-server /etc/redis/redis.conf 
    6255:M 08 Jun 00:19:28.368 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                    _._                                                  
               _.-``__ ''-._                                             
          _.-``    `.  `_.  ''-._           Redis 5.0.10 (00000000/0) 64 bit
      .-`` .-```.  ```\/    _.,_ ''-._                                   
     (    '      ,       .-`  | `,    )     Running in standalone mode
     |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
     |    `-._   `._    /     _.-'    |     PID: 6255
      `-._    `-._  `-./  _.-'    _.-'                                   
     |`-._`-._    `-.__.-'    _.-'_.-'|                                  
     |    `-._`-._        _.-'_.-'    |           http://redis.io        
      `-._    `-._`-.__.-'_.-'    _.-'                                   
     |`-._`-._    `-.__.-'    _.-'_.-'|                                  
     |    `-._`-._        _.-'_.-'    |                                  
      `-._    `-._`-.__.-'_.-'    _.-'                                   
          `-._    `-.__.-'    _.-'                                     
              `-._        _.-'                                       
                  `-.__.-'                             

说明:redis默认监听端口6379。

Redis的命令行客户端

Redis自带了一个命令行的客户端:redis-cli。下面我们介绍如何使用redis-cli连接、操作Redis服务。

  1. 打开客户端

    # redis-cli -h 主机ip -p 端口号    -h和-p都可以省略,默认后默认为127.0.0.1和6379
    [root@localhost ~]# redis-cli -h 127.0.0.1 -p 6379
    127.0.0.1:6379> 
  2. 连接成功,执行命令

    # 键入各种redis命令, 比如keys * 查询redis中所有的key
    127.0.0.1:6379> keys *
    (empty list or set)
  3. 停止redis服务

    127.0.0.1:6379> shutdown
    not connected> 
  4. 关闭客户端

    not connected> exit
    [root@localhost ~]# 

说明:可以通过redis-cli -h 主机ip -p 端口号 命令的方式,直接执行单个命令。

Redis重大版本

  • Redis2.6(2012):服务端支持lua脚本;键的过期时间支持毫秒。

  • Redis2.8(2013):增加了增量主从复制功能,提高了主从复制性能;Redis Sentinel第二版,相较于2.6版本,次版本已经生产可用。

  • Redis3.0(2015):增加了Redis Cluster,这是官方的分布式实现。

  • Redis3.2(2016):添加了GEO相关功能;从节点读取过期数据保持一致;数据持久化方面更换了新的RDB格式,但是仍然兼容旧的格式。

  • Redis4.0(2017):提供了module功能,方便第三方开发者扩展redis功能;提供了非阻塞del和flushall/flushdb功能,有效解决删除bigkey可能造成的Redis阻塞;提供了RDB-AOF混合持久化格式,充分利用了AOF和RDB各自优势。

  • Redis5.0(2018):新的流数据类型(Stream DataType);RDB新增LFU和LRU信息;集群管理器从Ruby(redis-trib.rb)移植到了redis-cli中的C语言代码。

  • Redis6.0(2020):引入多线程IO,但执行命令仍是单线程;提供了Redis集群代理功能;改进了PSYNC2的复制协议;推出了更新的RESP3(Redis Serialization Protocol)即Redis 服务端与客户端之间通信的协议第3版;加强了对集群的支持,包括更好的负载均衡和更快的故障转移。

Redis借鉴了Linux操作系统对于版本号的命名规则:版本号第二位如果是奇数,则为非稳定版本(例如2.7、2.9、3.1),如果是偶数,则为稳定版本(例如2.6、2.8、3.0、3.2)。当前奇数版本就是下一个稳定版本的开发版本,例如2.9版本是3.0版本的开发版本。所以我们在生产环境通常选取偶数版本的Redis,如果对于某些新的特性想提前了解和使用,可以选择最新的奇数版本。

Redis初体验

Redis是k-v数据格式的数据库,其中key固定为String类型,而value则有多种类型,下面演示value为字符串类型时的操作。

  1. 127.0.0.1:6379> set name xushy
    OK
    127.0.0.1:6379> set age 18
    OK
  2. #查看所有的key
    127.0.0.1:6379> keys *
    1) "age"
    2) "name"
    ​
    # 根据key获取value
    127.0.0.1:6379> get name
    "xushy"
    127.0.0.1:6379> get age
    "18"
  3. 127.0.0.1:6379> set name liy
    OK
    127.0.0.1:6379> set age 16
    OK
    127.0.0.1:6379> get name
    "liy"
    127.0.0.1:6379> get age
    "16"
  4. 127.0.0.1:6379> del name
    (integer) 1
    127.0.0.1:6379> del age
    (integer) 1
    127.0.0.1:6379> get name
    (nil)
    127.0.0.1:6379> get age
    (nil)

注意:

  • 字符串外的单引号或双引号可以省略

  • 命令结束直接回车,不要添加分号

Redis中设置key的过期时间

Redis中可以设置数据的过期时间,一旦过期自动删除数据。

  1. 设置过期时间

    127.0.0.1:6379> set name
    "ok"
    //设置10s后过期,expire单位秒
    127.0.0.1:6379> expire name 10 
    //设置10s后过期,pexpire 单位毫秒
    127.0.0.1:6379> pexpire age 10000
    (integer) 1
  2. 查看剩余时间

    # 查看剩余存活时长,单位秒
    127.0.0.1:6379> ttl name
    (integer) 7
    # 查看剩余存活时长,单位毫秒
    127.0.0.1:6379> pttl name
    (integer) 4006
  3. 取消过期

    127.0.0.1:6379> set age 18
    OK
    127.0.0.1:6379> expire age 20
    (integer) 1
    127.0.0.1:6379> ttl age
    (integer) 15
    取消过期
    127.0.0.1:6379> persist age
    (integer) 1
    ttl返回-1表示没有设置过期时间,返回-2表示数据不存在
    127.0.0.1:6379> ttl age
    (integer) -1
    127.0.0.1:6379> get age
    "18"

应用:手机验证码、黑名单、缓存

Redis事务

Redis通过 multiexecdiscardwatch 等命令实现事务功能。Redis的事务功能相对较弱,无法和关系型数据库的事务相媲美。

multi 和 exec命令

语法:

multi  开始事务
命令1
命令2
...
exec    执行事务   
discard 放弃执行事务

示例:

127.0.0.1:6379> multi                   //开始事务
OK
127.0.0.1:6379> set age 18              
QUEUED                  //事务中的命令并不会立刻执行,没有返回结果而是一个QUEUED(队列)
127.0.0.1:6379> getttt  //故意执行一个不存在的命令,使得事务出错
(error) ERR unknown command 'getttt'
127.0.0.1:6379> set sex F   //在错误命令后,再执行一个命令
QUEUED
127.0.0.1:6379> exec    //执行事务,事务失败
(error) EXECABORT Transaction discarded because of previous errors.
​
//事务中,错误命令前和后的正确命令都没有执行
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> get sex
(nil)

watch命令

有些应用场景需要在事务之前,确保事务中的key没有被其他客户端修改过,才执行事务,否则不执行。Redis提供了watch命令来解决这类问题。

watch命令用在 multi命令前,用于监控任意数量的键是否发生改变。当执行 exec命令时,将检查监视的键是否已经被修改,如果已经修改,服务器将拒绝执行事务。

语法:

watch 键
​
multi 开始事务
命令1
命令2
...
exec 执行事务 如果发现watch的键在事务执行前发生改变,则拒绝执行事务

示例:

Redis事务的实现原理

Redis通过一个事务队列完成事务功能。

  • 当一个客户端发送multi命令后,Redis服务器会将该客户端后续的命令保存到一个队列中。

  • 当一个处于事务状态的客户端向Redis服务器发送exec命令时,服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令的结果全部返回到客户端。

  • Redis在执行事务队列的命令前,如果发现入队的命令有语法错误,或者监控的值发生改变,将清空队列中的命令,拒绝执行。

Redis的弱事务性

在传统的关系型数据库中,常常使用A(原子性Atomicity)C(一致性Consistency)I(隔离性Isolation)D(持久性Durability)性质来检验事务功能的可靠性和安全性。而Redis的事务是不满足ACID特性的。

127.0.0.1:6379> set msg 'hello redis'  //添加一个String数据
OK
127.0.0.1:6379> multi               //开始事务
OK
127.0.0.1:6379> set name xiaohei
QUEUED
127.0.0.1:6379> rpush msg 1 2 3     //错误的使用rpush命令添加数据
QUEUED
127.0.0.1:6379> set age 18
QUEUED
127.0.0.1:6379> exec            //运行时出现错误,并不会影响其它的命令
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get name
"xiaohei"
127.0.0.1:6379> get age
"18"

通过上述案例,可以看到redis事务并不能保证事务中的命令要么同时成功,要么同时失败。并且出现运行错误后,也无法回滚事务。

为什么Redis没有实现标准的事务?

  • 会引入额外的复杂性,降低Redis的性能

  • 没有必要引入,Redis适用的功能场景比较简单,使用Redis事务已经足够完成功能。如果需要更加强大的事务,可以通过lua脚本实现。

Redis中常用的数据结构

传统键值存储是关联字符串值到字符串键,但是 Redis 的值不仅仅局限于简单字符串,还可以持有更复杂的数据结构。下面列的是 Redis 支持常用的数据结构,后面将逐一介绍:

  • String(字符串)

  • List(列表)

  • Set(集合)

  • Hash(哈希,键值对集合)

  • SortedSet(zset,排序集合)

String(字符串)

字符串类型是Redis最基础的数据结构。首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种数据结构的学习奠定基础。

Redis中字符串类型可存储的值可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。

读写操作
  1. 设置值:set key value [ex seconds] [px milliseconds] [nx|xx]

    • ex seconds:为键设置秒级过期时间。

    • px milliseconds:为键设置毫秒级过期时间。

    • nx:键必须不存在,才可以设置成功,可用于添加。

    • xx:与nx相反,键必须存在,才可以设置成功,可用于更新。

    # 省略所有可选参数,不存在则直接添加;已存在则更新
    127.0.0.1:6379> set name xushy
    OK
    127.0.0.1:6379> set age 18
    OK
    ​
    # 不存在才添加,也可以直接使用setnx命令
    127.0.0.1:6379> set sex 1 nx
    OK
    127.0.0.1:6379> set age 19 nx
    (nil) 失败
    ​
    #存在才更新,也可以直接使用setxx命令
    127.0.0.1:6379> set sex 0 xx
    OK
    127.0.0.1:6379> set address zhengzhou xx
    (nil)
    ​
    # 添加数据,同时设置过期时间
    127.0.0.1:6379> set score 100.0 ex 10
    OK
  2. 获取值:get key

    127.0.0.1:6379> get score
    OK
  3. 删除: del key [key ...]

    # 可以一次性删除多个key
    127.0.0.1:6379> del name age 
    (integer) 2
    127.0.0.1:6379> get name
    (nil)
批量操作
  1. 批量设置值:mset key value [key value ...]

    127.0.0.1:6379> mset name xiaobai age 20 sex 0
    OK
  2. 批量获取值: mget key [key ...]

    127.0.0.1:6379> mget name age sex
    1) "xiaobai"
    2) "20"
    3) "0"
  3. 批量添加值:msetnx key value [key value ...]

    # 注意,有一个存在则整体失败
    127.0.0.1:6379> msetnx name xiaohei salary 10000.0
    (integer) 0 

说明:批量操作命令可以有效提高开发效率,假如没有mget这样的命令,client和redis交互就需要产生n次的网络开销。而使用mget只会产生一次网络开销。

Redis可以支撑每秒数万的读写操作,但是这指的是Redis服务端的处理能力,对于客户端来说,一次命令指令耗时除了执行时间还是有网络时间,假设网络时间为1毫秒,命令时间为0.1毫秒(按照每秒处理1万条命令算),那么执行1000次get命令和1次mget命令的区别。因为Redis的处理能力已经足够高,对于开发人员来说,网络可能会成为性能的瓶颈。

操作时间
1000次get1000 × 1 + 1000 × 0.1 = 1100 ms = 1.1s
1次mget(组装1000个键)1 × 1 + 1000 × 0.1 = 101ms = 0.101s

学会使用批量操作,有助于提高业务处理效率,但是要注意的是每次批量操作所发送的命令数不是无节制的,如果数量过多可能造成Redis阻塞或者网络拥塞。

计数操作
  1. 自增操作:incr key

    • 值不是整数,返回错误。

    • 值是整数,返回自增后的结果。

    • 键不存在,按照值为0自增,返回结果为1。

    # 不存在返回1
    127.0.0.1:6379> incr video:playCount:1001
    (integer) 1
    # 存在返回自增后的值
    127.0.0.1:6379> incr video:playCount:1001
    (integer) 2
    # 值不是整数,返回错误
    127.0.0.1:6379> incr name
    (error) ERR value is not an integer or out of range
  2. 自增指定数操作:incrby key increment

    # 自增指定的数值
    127.0.0.1:6379> incrby video:playCount:1001 10
    (integer) 12
    ​
    # 还可以加上负数,实现自减
    127.0.0.1:6379> incrby video:playCount:1001 -1
    (integer) 11
  3. 自减操作:decr key

    127.0.0.1:6379> decr video:playCount:1001
    (integer) 10
  4. 自减指定数操作:decrby key decrement

    127.0.0.1:6379> decrby video:playCount:1001 2
    (integer) 8
  5. 自增浮点数操作:incrbyfloat key increment

    127.0.0.1:6379> incrbyfloat score 0.1
    "0.1"
总结

List(列表)

列表(list)类型是用来存储多个有序的字符串,如下图所示,a、b、c、d、e五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素(element),一个列表最多可以存储2^{32} -1个元素。

在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。

基础操作
  1. 插入操作

    • rpush key value [value ...] 从右边插入元素

    • lpush key value [value ...] 左边插入元素

    • linsert key before|after pivot value 向某个元素前后插入元素

    # 从右边插入元素
    127.0.0.1:6379> rpush news:hot:list article1 article2
    (integer) 2
    ​
    # 查看所有元素
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article1"
    2) "article2"
    ​
    # 从左边插入元素
    127.0.0.1:6379> lpush news:hot:list article3 article4
    (integer) 4
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article4"
    2) "article3"
    3) "article1"
    4) "article2"
    ​
    # 向article1 前添加元素article0
    127.0.0.1:6379> linsert news:hot:list before article1 article0
    (integer) 5
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article4"
    2) "article3"
    3) "article0"
    4) "article1"
    5) "article2"
    ​
    # 向article1后添加元素article1.0
    127.0.0.1:6379> linsert news:hot:list after article1 article1.0
    (integer) 6
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article4"
    2) "article3"
    3) "article0"
    4) "article1"
    5) "article1.0"
    6) "article2"

  2. 删除操作

    • lpop key 从列表左侧弹出元素

    • rpop key 从列表右侧弹出元素

    • lrem key count value 删除最多count个等于value的元素。count > 0 从左向右删除;count < 0 从右向左删除; count = 0 删除所有;

    # 从左侧弹出
    127.0.0.1:6379> lpop news:hot:list
    "article4"
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article3"
    2) "article0"
    3) "article1"
    4) "article1.0"
    5) "article2"
    ​
    # 从右侧弹出
    127.0.0.1:6379> rpop news:hot:list
    "article2"
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article3"
    2) "article0"
    3) "article1"
    4) "article1.0"
    ​
    127.0.0.1:6379> lpush news:hot:list article3 article4 article3
    (integer) 7
    127.0.0.1:6379> rpush news:hot:list article1 article2 article1
    (integer) 10
    127.0.0.1:6379> lrange news:hot:list 0 -1
     1) "article3"
     2) "article4"
     3) "article3"
     4) "article3"
     5) "article0"
     6) "article1"
     7) "article1.0"
     8) "article1"
     9) "article2"
    10) "article1"
    # 从左到右删除2个article3
    127.0.0.1:6379> lrem news:hot:list 2 article3
    (integer) 2
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article4"
    2) "article3"
    3) "article0"
    4) "article1"
    5) "article1.0"
    6) "article1"
    7) "article2"
    8) "article1"
    # 从右向左删除2个article1
    127.0.0.1:6379> lrem news:hot:list -2 article1
    (integer) 2
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article4"
    2) "article3"
    3) "article0"
    4) "article1"
    5) "article1.0"
    6) "article2"

下标操作
  1. 根据下标检索元素:lindex key index

    127.0.0.1:6379> lindex news:hot:list 0
    "article4"
  2. 获取列表长度:llen key

    127.0.0.1:6379> llen news:hot:list
    (integer) 6
  3. 截取列表子集:ltrim key start end

    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article4"
    2) "article3"
    3) "article0"
    4) "article1"
    5) "article1.0"
    6) "article2"
    ​
    # 截取部分
    127.0.0.1:6379> ltrim news:hot:list 1 4
    OK
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article3"
    2) "article0"
    3) "article1"
    4) "article1.0"
  4. 修改指定索引元素:lset key index newValue

    127.0.0.1:6379> lset news:hot:list 0 article3.0
    OK
    127.0.0.1:6379> lrange news:hot:list 0 -1
    1) "article3.0"
    2) "article0"
    3) "article1"
    4) "article1.0"
阻塞操作

前面介绍的 LPOP 和 RPOP 命令,在 List 为空的时候会返回 nil,但是有的场景中,需要在 List 没有数据的时候,阻塞等待新元素。

  • brpop key [key ...] timeout 阻塞式的从列表右侧弹出数据,直到返回数据或者等待超时(timeout单位s,为0时一直阻塞)

  • blpop key [key ...] timeout 阻塞式的从列表左侧弹出数据,直到返回数据或者等待超时(timeout单位s,为0时一直阻塞)

127.0.0.1:6379> brpop article:like:list 10
(nil)
(10.18s)
​
# 阻塞式的弹出数据
127.0.0.1:6379> brpop article:like:list 0
1) "article:like:list"
2) "111"
(29.63s)
​
# 同时,开启另外一个client,向队列添加元素
127.0.0.1:6379> lpush article:like:list 111
(integer) 1

说明:可以利用Redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。

总结

Set(集合)

集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。如图下图所示,集合user:1:follow包含着"it"、"music"、"his"、"sports"四个元素,一个集合最多可以存储 2^{32}-1个元素。

Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型,能在实际开发中解决很多实际问题。

集合内操作
  1. 添加元素: sadd key member[member ...]

    # 元素不可以重复
    127.0.0.1:6379> sadd user:1:tags sports game food travel sports
    (integer) 4
  2. 获取所有元素: smembers key

    127.0.0.1:6379> sadd user:1:tags sports game food travel sports
    (integer) 4
    127.0.0.1:6379> smembers user:1:tags
    1) "food"
    2) "sports"
    3) "travel"
    4) "game"
  3. 删除元素:srem key member [member ...]

    127.0.0.1:6379> srem user:1:tags food game
    (integer) 2
  4. 计算元素个数: scard key

    127.0.0.1:6379> scard user:1:tags
    (integer) 2
  5. 判断元素是否在集合中: sismember key member

    127.0.0.1:6379> sismember user:1:tags game
    (integer) 0
    127.0.0.1:6379> sismember user:1:tags sports
    (integer) 1
  6. 随机从集合中返回指定个元素: srandmember key count

    127.0.0.1:6379> srandmember user:1:tags 1
    1) "travel"
    127.0.0.1:6379> srandmember user:1:tags 1
    1) "travel"
    127.0.0.1:6379> srandmember user:1:tags 1
    1) "sports"
  7. 从集合中随机弹出元素: spop key [count]

    127.0.0.1:6379> spop user:1:tags 1
    1) "travel"
    127.0.0.1:6379> spop user:1:tags 1
    1) "sports"
集合间操作

Set 结构除了能支持单个集合内元素的增、删、改、查之外,还提供了一些数学上的集合操作,例如,求两个 Set 的并集、差集或者交集等。

  1. 交集

    • sinter key [key ...] : 求多个集合的交集(共性部分)

    • sinterstore destination key [key ...]: 求多个集合的交集,并保存到一个新的集合中

    # 准备2个集合 user:1:tags 和 user:2:tags 
    127.0.0.1:6379> smembers user:1:tags
    1) "food"
    2) "sports"
    3) "travel"
    4) "game"
    127.0.0.1:6379> sadd user:2:tags sports travel entertainment fashion
    (integer) 4
    ​
    # 求交集 用户1和用户2共同的爱好
    127.0.0.1:6379> sinter user:1:tags user:2:tags
    1) "sports"
    2) "travel"
    ​
    127.0.0.1:6379> sinterstore user:1:2:tags:inter user:1:tags user:2:tags
    (integer) 2
    127.0.0.1:6379> smembers user:1:2:tags:inter
    1) "sports"
    2) "travel"
  2. 并集

    • sunion key [key ...] : 求多个集合的交集(合并起来)

    • sunionstore destination key [key ...] : 求多个集合的交集(合并起来),并保存到一个新的集合中

    # 用户1和用户2所有的爱好
    127.0.0.1:6379> sunion user:1:tags user:2:tags
    1) "entertainment"
    2) "game"
    3) "fashion"
    4) "food"
    5) "sports"
    6) "travel"
    ​
    127.0.0.1:6379> sunionstore user:1:2:tags:union user:1:tags user:2:tags
    (integer) 6
    127.0.0.1:6379> smembers user:1:2:tags:union
    1) "entertainment"
    2) "game"
    3) "fashion"
    4) "food"
    5) "sports"
    6) "travel"
  3. 差集

    • sdiff key1 [key2 ...] : 求多个集合的差集(key1中有key2中没有的部分)

    • sdiffstore destination key1 [key2 ...] : 求多个集合的差集(key1中有key2中没有的部分),并保存到一个新的集合中

    # 用户1和用户2不同的爱好
    127.0.0.1:6379> sdiff user:1:tags user:2:tags
    1) "food"
    2) "game"
    ​
    127.0.0.1:6379> sdiffstore user:1:2:tags:diff user:1:tags user:2:tags
    (integer) 2
总结

Hash(键值对集合)

几乎所有的编程语言都提供了哈希(hash)类型,它们的叫法可能是Map、字典、关联数组。在Redis中,哈希类型是指键值本身又是一个键值对结构,形如value={{field1,value1},...{fieldN,valueN}},Redis键值对和哈希类型二者的关系可以用下图表示。

读写操作
  1. 设置值:hset key field value

    127.0.0.1:6379> hset user:1:info userId 10
    (integer) 1
    127.0.0.1:6379> hset user:1:info username xiaobai
    (integer) 1
    127.0.0.1:6379> hset user:1:info password 123456
    (integer) 1
    127.0.0.1:6379> hset user:1:info mobile 18712345678
    (integer) 1
  2. 获取值:hget key field

    127.0.0.1:6379> hget user:1:info userId
    "10"
  3. 添加值: hsetnx key field value

    # userId已经存在,添加失败返回0
    127.0.0.1:6379> hsetnx user:1:info userId 10
    (integer) 0
    # fans不存在,添加成功返回1
    127.0.0.1:6379> hsetnx user:1:info fans 0
    (integer) 1
  4. 删除:hdel key field [field ...]

    127.0.0.1:6379> hdel user:1:info mobile
    (integer) 1
  5. 获取field个数:hlen key

    127.0.0.1:6379> hlen user:1:info
    (integer) 3
  6. 判断field是否存在: hexists key field

    127.0.0.1:6379> hexists user:1:info userId
    (integer) 1
    127.0.0.1:6379> hexists user:1:info mobile
    (integer) 0
批量操作
  1. 批量设置值: hmset key field value [field value ...]

    127.0.0.1:6379> hmset user:2:info userId 20 username xiaohei password 123456
    OK
  2. 批量获取值:hmget key field [field ...]

    127.0.0.1:6379> hmget user:2:info userId username
    1) "20"
    2) "xiaohei"
  3. 获取所有field: hkeys key

    127.0.0.1:6379> hkeys user:1:info
    1) "userId"
    2) "username"
    3) "password"
  4. 获取所有子value: hvals key

    127.0.0.1:6379> hvals user:1:info
    1) "10"
    2) "xiaobai"
    3) "123456"
  5. 获取所有field-value: hgetall key

    127.0.0.1:6379> hgetall user:2:info
    1) "userId"
    2) "20"
    3) "username"
    4) "xiaohei"
    5) "password"
    6) "123456"
计数操作
  1. 自增指定的数:hincrby key field increment

    # 用户1新增一个粉丝
    127.0.0.1:6379> hincrby user:1:info fans 1
    (integer) 1
  2. 自增指定的浮点数:hincrbyfloat key field increment

    127.0.0.1:6379> hincrbyfloat user:1:info score 4.9
    "4.9"
总结

SortedSet(ZSet 排序集合)

有序集合保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。

如下图所示,该有序集合包含kris、mike、frank、tim、martin,它们的分数分别是1、91、200、220、250,有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能,合理的利用有序集合,能帮助我们在实际开发中解决很多问题。

基础操作
  1. 添加成员: zadd key [NX|XX] [CH] [INCR] score member [score member ...]

    • NX: member必须不存在,才可以设置成功,用于添加。

    • XX: member必须存在,才可以设置成功,用于更新。

    • CH: 返回此次操作后,有序集合元素和分数发生变化的个数

    • INCR: 对score做增加,相当于后面介绍的zincrby。

    # mike 获得了3个赞
    127.0.0.1:6379> zadd user:ranking:20230801 NX 3 mike
    (integer) 1
    ​
    # mike 新增1个赞
    127.0.0.1:6379> zadd user:ranking:20230801 XX INCR 1 mike
    "4"
    ​
    # 如果更新已有的成员的分数,使用CH返回更新的元素数目
    127.0.0.1:6379> zadd user:ranking:20230801 7 mike
    (integer) 0
    127.0.0.1:6379> zadd user:ranking:20230801 CH 8 mike
    (integer) 1
  2. 删除成员:zrem key member [member ...]

    127.0.0.1:6379> zadd user:ranking:20230801 NX 3 lucy
    (integer) 1
    # 删除成员lucy
    127.0.0.1:6379> zrem user:ranking:20230801 lucy
    (integer) 1
  3. 修改分数: zincrby key score member

    127.0.0.1:6379> zincrby user:ranking:20230801 1 mike
    "8"
  4. 获取成员个数: zcard key

    127.0.0.1:6379> zadd user:ranking:20230801 NX 3 lucy
    (integer) 1
    ​
    # 查看成员个数
    127.0.0.1:6379> zcard user:ranking:20230801
    (integer) 2
  5. 获取某个成员的分数:zscore key member

    127.0.0.1:6379> zscore user:ranking:20230801 mike
    "8"
  6. 计算成员的排名: zrank key member

    127.0.0.1:6379> zrank user:ranking:20230801 mike
    (integer) 1
    127.0.0.1:6379> zrank user:ranking:20230801 lucy
    (integer) 0
范围操作
  1. 返回指定下标范围的成员: zrange key start stop [withscores]

    127.0.0.1:6379> zrange user:ranking:20230801 0 -1
    1) "lucy"
    2) "mike"
    ​
    # 查看所有成员,同时查看分数
    127.0.0.1:6379> zrange user:ranking:20230801 0 -1 withscores
    1) "lucy"
    2) "2"
    3) "mike"
    4) "8"
  2. 删除指定下标范围的成员:zremrangebyrank key start stop

    # 删除全部
    127.0.0.1:6379> zremrangebyrank user:ranking:20230801 0 -1
    (integer) 2
  3. 返回指定分数范围的成员: zrangebyscore key min max [withscores] [limit offset count]

    127.0.0.1:6379> zadd user:ranking:20230801 2 lucy 3 mike 4 abel 5 lily
    (integer) 4
    127.0.0.1:6379> zrangebyscore user:ranking:20230801 (2 +inf withscores limit 0 10
    1) "mike"
    2) "3"
    3) "abel"
    4) "4"
    5) "lily"
    6) "5"
  4. 返回指定分数范围的成员个数: zcount key min max

    127.0.0.1:6379> zcount user:ranking:20230801 (2 5
    (integer) 3
  5. 删除指定分数范围的成员: zremrangebyscore key min max

    127.0.0.1:6379> zremrangebyscore user:ranking:20230801 (2 (5
    (integer) 2
总结

Redis中,常用的基本数据类型包括字符串(string)、哈希(hash)、列表(list)、集合(set)和有序集合(sorted set)。Redis本身是一个Map,所有数据都采用key:value的形式存储,而key永远是字符串类型。对于字符串类型,value可以是字符串、整型或浮点型,Redis能够自动识别。除了字符串类型,还有哈希类型,可以存储键值对的集合,对于哈希类型的操作,可以使用HSET、HGET等命令。此外,还有列表类型,用于存储有序的字符串列表,可以使用LPUSH、RPUSH等命令进行操作;集合类型,用于存储不重复的字符串集合,可以使用SADD、SREM等命令进行操作;有序集合类型,用于存储带有权重的有序字符串集合,可以使用ZADD、ZRANGE等命令进行操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Redis的五种常用数据类型的基本使用](https://blog.csdn.net/weixin_43795939/article/details/107815058)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Redis五大基础数据类型的操作](https://blog.csdn.net/weixin_44183721/article/details/126116049)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值