Redis 安装部署,基本数据类型介绍-长文详细介绍

Redis 安装部署,基本数据类型介绍

​ 本文将介绍Redis 基本安装操作,以及基本指令,以及基础数据类型的介绍。

安装部署

您可以在Redis官网上下载最新的 Redis https://redis.io/

源码安装方式

使用 tar xzf 用于解压下载好的Redis安装包。我们一般会通过ln -s的方式指定一个工作目录,有利于后续版本升级。接下来使用make进行安装的操作,(此命令需要依赖gcc环境,如果您的系统没有安装的话,需要先进行安装的操作)。安装完成后可以使用redis-cli -v 的指令查询所安装的Redis的版本。

配置, 启动,操作,停止

接下来我们将介绍一下Redis安装成功之后的几个重要的可执行文件,您可以在当前的目录下的src中或者/usr/local/bin目录下找到它们。

可执行文件作用
redis-server启动 Redis 服务
redis-cliRedis 命令行客户端
redis-benchmarkRedis 基准测试工具
redis-check-aofRedis AOF持久化文件检测和修复工具
redis-check-dumpRedis RDB持久化文件检测和修复工具
redis-sentinel启动 Redis Sentinel

如上所见,就是几个重要的可执行文件的简单介绍,我们通常称它们为Redis shell

启动

接下来我们尝试启动一个Redis服务,您可以执行以下指令

redis-server

看到如下日志,证明您的Redis-server服务端已经启动成功了

4454:M 23 Dec 2021 09:06:58.756 * Ready to accept connections

现在您可以新开一个terminal窗口来使用它了

在新的窗口您可以使用如下指令连接到刚刚启动的服务

redis-cli 

您可以看到已经成功连接到Redis服务

[root@d4d3d679abb8 shell]# redis-cli 127.0.0.1:6379> 

这样重新开启新的窗口然后连接,是不是觉得有点麻烦呢,这是因为当前的Redis是默认前台启动的,我们可以使它后台启动,这样它就会在后台挂起,并且不影响您正常使用。首先我们先关闭刚刚打开的客户端,然后执行ctrl+c的命令,可以退出当前正在运行的Redis服务端。

我们可以通过如下几种方式修改Redis的启动方式,您可以使用上述的方式来测试它是否启动成功。

  1. 1, 我们可以在启动的时候配置启动参数,没有指定的话它会使用默认的配置

    redis-server --daemonize  yes
    

    当然我们还可以指令其他的一些配置,我们在最后会进行介绍

  2. 2,(推荐)我们还可以修改配置文件,并且在启动的时候指定我们修改的配置文件启动。

    现在修改 Redis 的启动方式为后台启动 将 daemonize no 修改为 daemonize yes然后执行启动的指令

    redis-server /home/shell/redis/redis.conf
    
配置
接下来我们简单的介绍一些常见`Redis`的配置项(`Redis`共有60多个配置 详情可见本章节的附件部分)
配置名配置说明
port端口
logfile日志文件
dirRedis 工作目录(存放持久化文件和日志文件)
daemonize是否以守护进程的方式启动

以下是我们的配置文件所在的目录:

 /home/shell/redis/redis.conf 
操作

接下来我们将介绍它的两种连接客户端的方式

  1. 1,交互式方式

    通过 redis-cli -h { host } -p { port } 的方式连接到 Redis 服务,之后的所有操作都是通过交互式的方式实现,例如:(-h默认为 127.0.0.1 -p默认为 6379 所以刚刚的redis-cli是此命令的缩写)

    redis-cli -h 127.0.0.1 -p 6379
    
  2. 2,命令方式

    使用 redis-cli -h { host } -p { port } {command} 可以直接得到命令的返回结果

    [root@86318cd7ea26 shell]# redis-cli -h 127.0.0.1 -p 6379 select 1OK
    
停止

您可以通过如下指令停止redis-sever服务

redis-cli shutdown

常用指令,基本特性介绍

本章节主要介绍一下Redis的一些全局命令,数据库的基本操作,key的基本操作,以及了解一下Redis的单线程命令处理机制,主要是为了后面内容的学习打下一个比较好的基础。主要体现在两个方面:

  1. 第一、Redis的命令上百个,如果纯靠死记硬背比较困难,但是如果理解Redis的一些机制,会发现这些命令有很强的通用性。
  2. 第二、Redis不是万金油,有些数据结构和命令必须在特定场景下使用,一旦使用不当可能会对Redis本身或者应用本身造成致命伤害。
全局命令

为了您可以更好的学习Redis接下来的课程,我们先简单介绍一些通用指令。 如果您执行了关闭redis-server服务操作的话,您需要重新启动redis-server服务,并且使用redis-cli进入客户端才能继续以下的课程。

1,数据库操作

首先我们先了解一下Redis默认拥有16个数据库,默认使用的是第一个也就是0号数据库。各个数据库之间是相互独立的,互不影响,没有任何关联。Redis提供了几个面向Redis数据库的操作,它们分别是selectflushdb/flushall命令,我们依次来介绍一下

  1. 1,切换数据库

    我们已经了解到 使用redis-cli进入Redis客户端后,默认所处的数据库为0号数据库,如果我们想切换所处的数据库的话可以使用如下命令(默认配置是拥有16个数据库,您如果不修改配置文件的话能切换的选择范围是(0-15))

    select 1
    

    我们可以通过查看当前的输入行前缀查看当前当前所处的数据库,当不位于0号数据库之下时,会在前面[ ]里边显示数据库的编号。

    目前Redis已经逐渐弱化这个功能,例如Redis的分布式实现RedisCluster只允许使用0号数据库, 只不过为了向下兼容老版本的数据库功能,该功能没有完全废弃掉, 下面分析一下为什么要废弃掉这个“优秀”的功能呢? 总结起来有三点

    • Redis是单线程的,如果使用多个数据库的话,这些数据库仍然是使用的同一个cpu彼此间还是会受到影响的

    • 多数据库的使用方式, 会让调试和运维不同业务的数据库变的困难,假如有一个慢查询存在, 依然会影响其他数据库, 这样会使得别的业务方定位问题非常的困难 。

    • 部分Redis的客户端根本就不支持这种方式。 即使支持, 在开发的时候来回切换数字形式的数据库, 很容易弄乱。

      笔者建议如果要使用多个数据库功能, 完全可以在一台机器上部署多个Redis实例, 彼此用端口来做区分, 因为现代计算机或者服务器通常是有多个CPU的。 这样既保证了业务之间不会受到影响, 又合理地使用了CPU资源。

  2. 2,flushdb/flushall

    flushdb/flushall命令用于清除数据库,两者的区别的是flushdb只清除当前数据库,flushall会清除所有的数据库。

    *建议大家使用这两个命令的时候一定要谨慎,慎重。

2,键操作

Redis有5种基本数据结构分别是String,Hash,List,Set,Zset, 它们是键值对中的值, 对于键来说有一些通用的命令

  1. 1,查看所有键

    我们可以通过key*来查询当前数据库下的所有键值

    *注意生产环境禁止使用此命令,此命令会检索所有的 key 如果 Redis 存储的量过多的话会造成 Redis 的阻塞,在实验中我们会对Redis的遍历键做简单的分析说明

  2. 键总数

    我们可以通过dbsize来查询当前数据库下的键总数

    *dbsize 指令在计算键总数的时候不会遍历所有键,而是直接获取 Redis 内置的键总数变量,所以 dbsize 的时间复杂度是 0(1) 。

  3. 3,检查键是否存在

    我们可以用exists来检查指定键是否存在,如果键存在则返回 1 否则返回 0

  4. 4,删除键

    我们可以通过del来删除指定键值,操作结果为删除键的个数,支持删除多个。

  5. 5,键过期

    Redis支持对指定键添加过期事件,当超过指定时间后,会自动删除如下(单位时间为秒)

    expire key3 100
    

    此方法会返回受响应的键的数量,我们可以通过ttl命令来查询当前键的剩余过期时间,如下

    ttl key3
    

    返回字段为剩余时间,它有两种特殊的返回

    1. 如果返回-1,证明当前查询的键没有设置过期事件
    2. 如果返回-2,则证明当前的键不存在
  6. 6 ,值的数据结构类型

    我们可以通过type命令来查询指定键的对应值的数据类型如:

    type key3
    

    我们将得到返回值的基本数据类型

数据结构和内部编码

如上type指令返回的就是当前值的基本数据类型。基本数据类型分别是 string (字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合),但这些仅仅是 Redis 对外的数据结构,如下图。

redis数据结构和内部编码

如图所示,这样设计的好处

  1. 1.可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发出更优秀的内部编码,无需改动外部数据结构和命令。

  2. 2.多重内部编码实现可以在不同场景下发挥各自的优势,例如 zipList 比较节省内存,但是在列表元素比较多的情况下,性能会有所下降,这时候 Redis 会根据配置选项将列表类型的内部实现转换为 linkedList

单线程架构

Redis 使用了单线程架构和I/O多路复用模型,来实现高性能的内存数据库服务。本节简单介绍一下Redis的单线程命令处理机制,接着简单分析一下Redis单线程模型为什么性能如果之高,最终您会对Redis的单线程模型有初步的了解。

  1. 一.单线程处理机制

    Redis是单线程来处理命令的,所以一条命令从客户端到达服务端,会进入一个队列中,然后逐个执行,不会存在两条指令同时执行的情况,如图:
    redis请求响应模型

  2. 二.为什么单线程还能这么快。

    1. 1,纯内存访问,Redis 将所有数据都放在内存中,内存的响应时长大约为100纳秒 这是 Redis 达到每秒百万级的重要基础

    2. 2,非阻塞 IO ,Redis 使用 epoll 作为 I/O 多路复用技术的实现,再加上 Redis 本身的时间处理模型将 epoll 中的连接,读写,关闭都转换为事件,不在网络 I/O 上浪费过多的时间

    3. 3,单线程避免了线程切换和竞态产生的消耗。

      *单线程可以简化数据结构和算法的实现,其次避免了线程切换和竞态产生的消耗。但是单线程对于每个命令的执行时间是有要求的,如果某个指令执行时间过长的话,会造成其他命令的阻塞,对于Redis这种高性能的服务是致命的,所以 Redis 是面向快速执行场景的数据库。

Redis基础数据类型 String

首先介绍的是Sring类型,首先键都是字符串类型的,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串能为其他四种数据结构的学习奠定基础。 如图所示,字符串类型的值实际可以是字符串(简单的字符串,复杂的字符串(例如 JSON XML ),数字(整数,浮点数),甚至二进制(图片,音频,视屏))但是值最大不能超过 512MB

常用命令

我们这里只介绍一些详细的命令,在实验中提供了String类型所有的命令,以及示例。

  1. 1,单个设置值,获取值

    Redis可以通过set命令来创建一个String类型的键值对:

    set key value
    

    我们可以通过get命令来获取我们保存的数据:

    get key
    

    我们也可以为我们的数据设置过期事件,我们只需要在set命令后添加EX[过期时间]

    set key value ex 5
    

    这里的时间单位是秒,我们可以等待5秒之后再去查询,就会发现get key的结果为(nil)这个意思是不存在的意思。

    当然我们也可以使用毫秒为单位来设置过期时间,如果我们希望使用毫秒作为过期的时间,我们需要使用px

    set key value px 5000
    

    有些情况下,我们希望设置一个具体的时间,比如说2021-12-31 23:59:59, 很可惜我们不能直接使用这个时间,我们需要用所谓的时间戳。时间戳是计算机用来表达时间的一种方式,它是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。2021-12-31 23:59:59用时间戳表示就是:1640966399:

     set key value EXAT 1640966399
    

    这样,我们键为key的这个数据会在2021-12-31 23:59:59到了后过期。

    同样,我们也可以设置毫秒级别的到期时间,这样会更加准确一些,毫秒级别的到期时间用的是PXAT

    set key value PXAT 1640966399000
    

    有时候为了避免覆盖已经存在的数据,我们可以在设置数据的时候,增加一个NX的标志,NX也就是NOT EXISTS的意思。这时候如果我们准备保存的键值对在Redis数据库中已经存在,它就会报错

    set key value NX
    

    NX相反的是,还有一个标志是XX,它的作用是要求这个键必须存在:

    set key-not-exist value XX
    

    (nil)的意思就是没有成功的意思。

    相反,我们重新设置一下键为key的值:

     set key new-value XX
    

    当然,Redis也为我们提供了语法糖,比如set key value NX 等价于setnx key value,set key value XX等价于setxx key value

  2. 2,批量设置值,获取值

    Redis提供了mset来批量创建多个键值对:

    mset demo1 dsds demo2 dededed demo3 ededede
    

    它会返回你创建成功的个数。同时 Redis也提供了mget用于批量获取值,我们可以用来获取获取我们刚刚存储的所有值:

    mget demo1 demo2 demo3
    

    批量操作可以有效的提高开发效率,可以替换n次的get命令的操作,下述我们简单做了两种命令的时间消耗对比

    操作时间
    1000次get1000 * 1+1000 * 0.1=1000毫秒=1.1秒
    1次mget(组装1000和键值对)1*1+1000 * 0.1=101毫秒=0.101秒

    *需要的注意的是每次批量操作所发送的命令数不是无节制的,如果数量过多可能造成 Redis 阻塞或者网络阻塞。

  3. 3,计数

    我们在开头的时候了解到String类型可以存储整数或者浮点数,Redis对于值的类型为整数或者浮点数的键值对,提供了计数的相关命令,可以对值进行递增,递减的操作。

    给大家介绍的是incr命令,它可以对值进行递增的操作。

     incr  key
    

    如果我们递增了一个不存在的键,它会先创建一个键值对值为0,随后对这个键值对进行递增的操作,返回递增后的结果。

内部编码

接下来我们来了解一下字符串类型的内部编码。

  • 1,int:8个字节的长整型
  • 2,embstr:小于等于39个字节的字符串
  • 3, raw:大于39个字节的字符串

Redis会根据你存储的值的字符串长度,选取合适的内部编码实现。这里我们只做一下基础的了解。

常见使用场景
  1. 1.缓存功能

    Redis 具有支撑高并发的特性,所以一些热点信息使用缓存,可以起到加速读写和降低后端压力的作用

    例如,存储用户信息,商品信息等一些热点信息。一般采用 Json 格式存储

  2. 2.计数

    很多应用会使用 Redis 作为计数的基本工具,它可以实现快速计数,查询缓存的功能。同时数据可以异步落地到其他持久化的数据源。

    例如:电商系统中,商品的浏览量,购买量等都是以天为单位 在 Redis 中计数,然后使用定时任务同步到 ES 分布式搜索引擎 或者 Mysql 中持久化保存。

  3. 3.共享Session

    分布式 Web 服务为了 实现单点登录 可以采用 分布式 Session共享的方式实现。其原理就是将用户的 Session 进行集中管理,而不是分布在各个服务端 进行管理如图:

    redis 集中管理session

  4. 4.限速,降频

    Redis 可以给指定 key 设置过期时间,通过检查指定 key 的存在情况,可以实现一下定时的业务场景。

    例如:常见的 app 进行短信登录的时候会对多次短信发送,做时间间隔的操作,常见的实现方式,发送短信之前,在 Redis 中查看上次发送的短信是否过期。

Redis基本数据类型 Hash

这里只介绍一些常用的命令,在实验中提供了Hash类型所有的命令,以及示例。

  1. 1,单个设置值,获取值

    Redis可以通过hset命令来创建一个Hash类型的键值对,它可以一次设置多个field

    hset hash1 field1 val1 field2 val2
    

    返回的是您新建成功的个数,我们可以通过hget来获取具体的field的值

    hget hash1 field1
    

    当前如果您想获取到指定key下边所有的filed和value的话,您也可以使用如下的命令

    hgetall hash1
    
  2. 2,删除操作

    我们在开始的基础设置以及了解到del可以删除指定的keyRedis针对Hash类型来提供了删除指定field的命令:

    hdel hash1 field1
    

    您可以使用这个命令删除指定的field,当然也支持同时删除多个

    hdel hash1 field1 field2
    
  3. 3,计算field的个数

    Redis也提供了指定key计算field个数的方法

     hlen hash1
    

    返回的为field的个数。

  4. 4,批量设置,获取field-value

    同样Redis也提供了Hash类型批量设置和获取的方法hmsethmget,我们来简单使用一下

    hmset hash1 field1 val1 field2 val2
    

    大家可能会发现这个方法使用和hset方法一致。这个方法在Redis 4.0.0的时候弃用了,如果您的Redis版本低于4.0.0还是需要使用hmset来存储多个field-value的,如下是官方文档介绍

    As per Redis 4.0.0, HMSET is considered deprecated. Please use HSET in new code.
    根据Redis 4.0.0,HMSET被视为已弃用。请在新代码中使用HSET。
    

    获取多个field-value目前还需要使用hmget如下

     hmget hash1 field1 field2 
    
内部编码

接下来我们来了解一下Hash类型的内部编码。

  • 1,zipList (压缩列表)

    当哈希类型元素个数小于 hash-max-ziplist-entries 配置(默认512个)、同时所有值小于 hash-max-ziplist-value 配置(默认64字节)时,Redis 会使用 ziplist 作为哈希的内部实现,ziplist 使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比 hshtable 更加优秀

  • 2,hashtable(哈希表)

    当哈希类型无法满足 ziplist 的所有条件时,Redis 会使用 hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,而 hashtable 的读写时间复杂度为0(1)

常见使用场景
  1. 1,用于缓存

    Redis hash 类型的(key,field,value)的结构与对象的(对象id,属性,值)的结构相似,可以用来存储热点的对象数据。与 string 类型存储的区别如下

    String+jsonhash
    效率很高
    容量
    灵活性
    序列化简单复杂

    当对象的某个属性需要频繁修改时,不合适用 string + json ,因为它不够灵活,每次修改都需要重新将整个对象序列化并赋值,如果使用 hash 类型则可以针对某个属性单独修改。另外使用 string + json 格式存储查询检索方式单一,只能通过 key 的方式查询,如果使用 hash 类型存储则可以使用 key +field 的方式进行检索。

    综上: 一般对象使用 string + json 存储,对象中如果有某些频繁变化的属性建议使用 hash 存储

Redis基本数据类型List

List类型,列表(List) 类型是用来存储多个有序的字符串, 如图所示, a、b、 c、 d、 e五个元素从左到右组成了一个有序的列表, 列表中的每个字符串称为元素(element) , 一个列表最多可以存储232-1个元素。 在Redis中, 可以对列表两端插入(push) 和弹出(pop) , 还可以获取指定范围的元素列表、 获取指定索引下标的元素等。 列表是一种比较灵活的数据结构, 它可以充当栈和队列的角色, 在实际开发上有很多应用场景。

列表插入弹出操作

列表类型有两个特点:

  1. 列表中的元素是有序的;
  2. 列表中的元素是可以重复的;
常用命令

我们先按照对 列表的5种操作类型 ,对所有的api进行了一个分类,然后按照分类进行介绍

操作类型操作
增加rpushlpushlinsert
lrange lindex llen
删除lpop rpop lrem ltrim
修改lset
阻塞操作blpop brpop
  1. 1,添加操作

    首先我们了解到,Redis中的List是一个有序的列表,我们可以在列表的右侧,左侧,或者某个元素的位置上进行插入,相对的Redis给我们提供了以上三个场景的命令,分别是rpushlpushlinsert三个命令,我们依次来演示一下

    • 1,rpush从右侧新增元素(支持单个或者多个),该命令会返回当前所有元素的个数,此时新建的元素在队列的末尾:

      rpush list1 value1 value2 value3
      

      您可以通过lrange list1 0 -1指令来查询列表:

       lrange list1 0 -1
      

      可以看到新插入的数据在列表的末尾。

    • 2,lpush从左侧新增元素(支持单个或者多个),该命令会返回当前所有元素的个数,此时新增的元素在队列的开头位置:

      lpush list1 left1 left2
      

      我们再来查询一下列表:

       lrange list1 0 -1
      

      可以发现新增的元素按照插入时间的倒叙在列表的开头位置。

    • 3,linsert是在指定元素的前或者后插入元素,该命令会返回当前所有元素的个数。首先我们先在上述list1value1前方插入一个元素linsert1

      linsert list1 before value1 linsert1
      

      再次查询列表:

       lrange list1 0 -1
      

      可以看到 linsert1确实位于位于value1的上方

      我们再向上述list1value1后方插入一个元素linsert1

      linsert list1 after value1 linsert1
      

      再查询一下:

       lrange list1 0 -1
      

      如您所见value1的下方也出现了一个linsert1,这也只能的List中的元素是可以重复的

  2. 2,查找

    Redis提供了多种检索List列表的方法,同上述我们使用到的lrange检索指定范围内的元素列表,还提供了获取指定索引元素的方法lindex,以及查询List列表长度的方法llen,下述我们依次介绍一下

    1. 1,lrange获取指定范围内的元素列表

      这个方法我们在上边已经使用过了,我们来解析一下刚刚使用的命令lrange list1 0 -1其中list1是查询的指定键,后面跟的两个参数分别是开始的索引start,和结束的索引endRedis中的索引下标有两个特:第一, 索引下标从左到右分别是0N-1, 但是从右到左分别是-1-N;第二, lrange中的end选项包含了自身, 这个和很多编程语言不包含end不太相同。 例如想获取列表的第2到第4个元素, 可以执行如下操作:

      lrange list1 1 3
      
    2. lindex可以获取指定索引的元素

      这个方法简单,就不赘述了,我们来一个示例

      lindex list1 1
      
    3. llen这个方法可以获取列表长度

      这个方法同我们前面课程介绍的dbsize一样获取的系统参数,并不是检索的内容,时间复杂度为0(1)

      llen list1
      
  3. 3,删除

    同样的Redis也提供了多重删除List中元素的方法分别是:lpop从左侧弹出,rpop从右侧弹出,lrem删除指定元素,ltrim删除指定范围内的元素。老规矩我们还是依次来介绍一下:

    1. 1,lpop从列表的左侧弹出元素,可以指定弹出元素的个数,此结果会返回删除的元素值

      先查询当前的列表

      lrange list1 0 -1
      

      执行指令

      lpop list1 1
      

      再次查询列表:

       lrange list1 0 -1
      
    2. 2,rpop从列表的右侧弹出元素,同样可以指定弹出元素的个数

      rpop list1 1
      

      查询列表:

       lrange list1 0 -1
      

      可以发现在列表的末尾部分,少了一个

    3. lrem删除指定元素

      lrem可以删除指定的元素,它的语法是这样的lrem key count value 这个命令会从列表中找到等于value的元素进行删除, 根据count的不同分为三种情况:

      • count>0, 从左到右, 删除最多count个元素。
      • count<0, 从右到左, 删除最多count绝对值个元素
      • count=0, 删除所有。

      例如:新建一个列表中从左向右插入5个a和两个b。

      lpush list2 a a a a a b b
      

      通过查询可以得知那么当前列表变为“ b b a a a a a”:

      lrange list2 0 -1
      

      下面操作将从列表左边开始删除4个为a的元素:

      lrem list2 4 a
      

      随后我们再次查询列表,看看是否和我们猜想的一样变成了"b b a"

      lrange list2 0 -1
      
    4. 修改操作

      Redis提供了修改指定索引下标元素的方法

      lset list1 0 newval
      
    5. 阻塞操作

      blpop key [key ...] timeout 	brpop key [key ...] timeout
      
        阻塞式弹出有两个命令`blpop`和`brpop`这个两个方法是`lpop`和`rpop`的阻塞版本,它们除了弹出方向不同之后,使用方法基本相同,下面以`blpop`为例子
      
        首先解释一下上述语法:这个方法就是当给定的列表内没有任何元素可供弹出的时候,连接将被 `BLPOP `命令阻塞,直到等待超时或发现可弹出元素为止。
      
      • key [key …] 多个列表的键
      • timeout 阻塞时间(单位秒)

      我们来一个简单的例子实践一下

      首先有两种情况(列表为空和列表不为空的情况)

      我们先创建一个列表

      lpush list3 val3
      

      可以查看一下当前list3中有一个元素val3

       lrange list3 0 -1
      

      我们执行一下BLPOP命令让他阻塞10秒

       blpop list3 10
      

      我们可以发现当列表不为空的时候,它会立即返回结果,并没有进入阻塞等待,返回值为,指定的key以及弹出的值。我现在再来看一下list3的情况

       lrange list3 0 -1
      

      可以发现当前的list3是空的,这时候我们再执行BLPOP命令:

       blpop list3 10
      

      可以看到它在阻塞了10秒之后返回了空,在这期间如果我们插入了元素,那么它会停止阻塞,立即返回,我们来试一下。

      首先我们还是使它处理阻塞等待状态,这次时间稍微长点设置30秒

       blpop list3 30
      

      首先打开新的 terminal窗口点击我打开

      使用指令给list3新建一个元素

      redis-cli lpush list3 new
      

      随后切换为成我们原先的 terminal窗口可以看到,命令并没有阻塞等待30秒自动返回,而是立即返回了我们刚刚新增的new元素

内部编码

接下来我们来了解一下List类型的内部编码。

  • ziplist(压缩列表):当列表的元素个数小于 list-max-ziplist-entries 配置(默认512个),同时列表中每个元素都小于 list-max-ziplist-value 配置时(默认64字节),redis 会选取 ziplist 来作为列表的内部实现来减少内存的使用。

  • linkedlist (链表):当列表类型无法满足 ziplist 的条件是,Redis 会使用 linkedlist 作为列表的内部实现。

    Redis 3.2 版本提供了 quicklist 内部编码,简单地说它是一个 ziplist 为节点的 linkedlist,它结合了 ziplist linkedlist 两者的优势,为列表类型提供了一种更加优秀的内部编码实现,它的设置原理可以参考 Redis 的另一个作者 Matt Stancliff的博客: https://matt.sh/redis-quicklist

使用场景
  1. 消息队列

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

  2. 文章列表

    对于热点数据的文章列表可以考虑使用 Redis List类型存储,因为列表不但是有序的,同时支持按照索引范围获取元素。可能存在问题如下

    • 如果每次获取的文章个数较多,需要执行多次 hgetall 操作 ,此时可以考虑使用 pipeline (管道)批量获取
    • lrange 命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差,此时可以考虑将列表作二级拆分,或者使用 Redis3.2quicklist 内部编码实现,它结合 ziplist linkedlist 的特点,获取列表中间范围的元素时也可以高效完成。

实际上列表的使用场景很多,下述是指令组合能达到的 类似数据类型特点

  • lpush + lpop =Stack (栈)

  • lpush + rpop= Queue (队列)

  • lpush + ltrim =Capped Collection (有限集合)

  • lpush + brpop = Message Queue(消息队列)

Redis基本数据类型Set

Set类型,集合(Set) 类型也是用来保存多个的字符串元素,但是和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,也不是通过索引下标获取元素。如图所示 集合user: 1: follow包含着"it""music""his" "sports"四个元素, 一个集合最多可以存储232-1个元素。 Redis除了支持集合内的增删改查, 同时还支持多个集合取交集、 并集、 差集, 合理地使用好集合类型, 能在实际开发中解决很多实际问题

集合类型

常用命令

下面将按照集合内和集合之间两个维度对集合的常用命令进行介绍

  1. 一,集合内操作

    • 1,单个添加,查询元素

      Redis提供了sadd用户添加Set类型的元素

      sadd set1 demo1
      

      您可以使用smembers命令来返回当前Set中的所有元素,返回的元素是无序的。

      smembers set1
      

      *类似smembersList中的LrangeHash中的hgetall都是比较重的命令,如果元素过多的话存在阻塞Redis的可能性,这时候可以使用scan来完成,在实验的附件中介绍,可以参考。

    • 2,计算元素的个数

      使用scard可以计算当前集合中的元素的个数

      scard set1
      

      scard的时间复杂度为0(1),它不会遍历集合所有元素,而是直接使用Redis内部的变量

    • 3,判断元素是否在集合中

      Redis提供的键是否存在的函数为exists,同时提供了sismemeber用于判断集合中是否有指定的元素

      sismember set1 demo1 
      

      如果给定元素 element 在集合内返回1,反之返回0

    • 4,随机从集合中返回指定个数的元素

      Redis提供srandmember从集合中随机返回指定个数的元素

      srandmember set1 1 
      
    • 5,从集合中随机弹出元素

      Redis提供spop从集合中随机弹出指定个数的元素(注意这里边是弹出就是删除)(最后的个数可以选择不填默认是1个)

      spop set1  1
      
    • 6,删除元素

      Redis提供的删除键的方法是delRedis提供了srem用于删除集合中的指定元素(我们先添加一个)

      sadd set1 demo1  demo2
      
      srem set1 demo1
      

      此方法会返回删除成功的个数

  2. 二,集合间的操作

    Redis提供了多个集合间求交集,并集,差集,以及将这些结果保存的命令,下面我们依次介绍一下

    • 1,求多个集合的交集sinter

      首先我们先新建两个集合

      sadd  set4 a  h b  hb
      
      sadd  set5 b e  berb  rer ebrer
      

      然后我们可以使用sinter来得到两个集合的交集

       sinter set4 set5
      
    • 2,求多个集合的并集

      sunion可以得到多个集合的并集

       sunion set4 set5
      
    • 3,求多个集合的差集

      sdiff可以得到多个集合的差集,

       sdiff set4 set5
      
    • 4,将交集、并集、差集的结果保存

      sinterstore destination key [key ...]
      sunionstore destination key [key ...]
      sdiffstore destination key [key ...]
      

      集合间的运算在元素较多的情况下比较耗时,所以 Redis 提供了上面三个命令,(原命令+ store)将集合间交集,并集,差集的结果保存在 destination key 中。

      我们先执行sunionstore方法将结果输出到myset1

      sunionstore  myset1 set5 set4
      

      我们查询一样结果是否输出到指定键中

      smembers myset1
      

      这里仅仅列举了一个的使用方法,其他两者使用方法一致。

内部编码

接下来我们来了解一下Set类型的内部编码。

集合的内部编码有两种:

  • intset ( 整数结合 ) :当集合中的元素都是整数且元素个数小于 set-max-intset-entries 配置(默认512个)时,Redis 会选用intset 来作为集合的内部实现,从而减少内存的使用。
  • hashtable( 哈希表):当集合类型无法满足 intset 条件时, Redis 会使用 hashtable 作为集合的内部实现
使用场景

​ 集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、 体育比较感兴趣, 另一个用户可能对历史、 新闻比较感兴趣, 这些兴趣点就是标签。 有了这些数据就可以得到喜欢同一个标签的人, 以及用户的共同喜好的标签, 这些数据对于用户体验以及增强用户黏度比较重要。 例如一个电子商务的网站会对不同标签的用户做不同类型的推荐, 比如对数码产品比较感兴趣的人, 在各个页面或者通过邮件的形式给他们推荐最新的数码产品, 通常会为网站带来更多的利益 。

下面使用集合类型实现标签功能的若干功能示例。大家可以尝试一下。

  • 给用户添加标签

    sadd user1:tages  tag1 tag3
    
    sadd user2:tages  tag3 tag3
    
  • 给标签添加用户

    sadd tag1  user1 user2 user3
    
  • 删除用户下的标签

    srem user1:tages tag1 
    
  • 删除标签下的用户

    srem tag1 user2 user3
    
  • 计算用户共同感兴趣的标签

    sinter  user1:tages   user2:tages 
    

    上述只是给出了使用 Redis 结合类型实现标签的基本思路,实际上会复杂很多,不过集合类型的应用场景通常为以下几种:

    • sadd = Tagging (标签)

    • spop/srandmember = Random item (生成随机数,比如抽奖)

    • sadd + sinter = Social Graph (社交需求)

Redis基本数据类型 ZSet

章节叙述
   本章节我们简单介绍一下`Zset`(有序集合)类型,它保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。下图给出了列表、集合、有序集合三者的异同点。
数据结构是否允许重复元素是否有序有序实现方式应用场景
列表索引下标时间轴、消息对列表等
集合标签、社交等
有序结合分值排行榜系统、社交等
常用命令

本节依旧按照集合内和集合外两个维度对有序集合的命令进行介绍。

  1. 一,集合内操作

    • 1,设置元素

      Redis提供了zadd的方法用于添加Zset类型的键值对。我们先介绍一下它的基本语法

      zadd key score member [sore memeber ...]
      

      score代表元素的分值,member列表存储的元素,它支持一次性设置多个元素,返回结果为成功添加成员的个数

      下面的操作将向有序集合zset:user中添加用户tom和他的分数251 ,以及他的小伙伴tom1tom2他两的分数分别为262和263

      zadd zset:user1 200 tom 201 tom1 253 tom2
      

      有关add命令有两点需要注意的:

      • Redis后续版本对zadd方法添加了nxxxchincrgtlt;六个可选项我们来简单了解一下

        1. 1)首先nxxx这两个选项我们在string的章节中已经介绍过了,nx表示目标的元素必须不存在才可以设置成功,而xx表示只有目标元素存在才可以设置成功,用于元素的更新。

          我们简要的使用下述指令测试其中nx使用

          我们先存储一个已经存在的

          zadd zset:user1 nx 261 tom
          

          可以看到没有新增成功返回结果为0 接下来我们新增一个不存在的键

          zadd zset:user1 nx 264 tom4
          
        2. 2)其次incr在之前的章节中了解到用于自增的命令,这里是用于对 score做增加并且返回增加成功的值,增加的数量我们可以自己指定,下述指令中为1。我们简单使用一下

          zadd zset:user1 incr 1 tom
          

          Redis也提供了相应的语法糖zincrby key score value member 下面是简单的示例

          zincrby zset:user1 1 tom
          

          通过返回值可以观察到在上次增加后的结果上又增加了1。我们也可以使用zrange的命令查询指定key所有元素,这个指令我们在接下来会详细介绍

          zrange zset:user1 0 -1 withscores
          

          我们可以观察到tom的分值确实是263

        3. 3)接下来我们了解一下ch选项,这个选项会返回成功修改的个数,请注意原始命令返回的是成功新增的元素个数,我们了解到zset是有序集合,集合中的元素是唯一的,如果添加了相同的元素,但是分值不同会进行替换的操作,这时新增的个数为0,我们可以通过如下来进行测试

          首先设置一个值

          zadd zset:user2 123 tom
          

          通过ch设置和刚刚相同的值

          zadd zset:user2 ch 124 tom
          

          我们再使用普通的方式重新设置一下值

          zadd zset:user2  125 tom
          

          我们可以发现,使用ch设置值会返回覆盖操作的元素个数,而普通方式不会。

        4. 4)gt以及lt这两个选项代表的含义是:如果元素存储在的话,修改的分数必须大于或者小于原先的分数才能修改成功。其中gt表示大于,lt表示小于。请注意这两个选项,与[nx/xx]不可以共用。同样我们来进行简单的测试

          首先设置一个值

          zadd zset:user3 166 tom
          

          然后我们使用gt方式来修改刚刚新建的值为165

          zadd zset:user3 gt ch 165 tom
          

          最后我们使用zrange来查询集合的所有元素

          zrange zset:user3 0 -1 withscores
          

          最后我们可以发现tom的分数还是166 可以确定由于修改的分值小于原先的分值,所以修改并没有生效。

      • 2,计算成员个数

        我们可以通过zcard来查询指定键的元素的个数,当然您值的类型必须是zset类型,否则查询会报错。和集合类型的scard命令一样,zcard的时间复杂度为0(1)

        例如下面的操作我们将返回有序集合zset:user1的成员数。

         zcard zset:user1
        
      • 3,计算某个成员的分数

        您可以通过zscore命令来查询指定成员的分数。

        例如 zset:user3tom的分数为166,:

        zscore zset:user3 tom
        

        我们再查询zset:user3中不存在的用户,会返回(nil)空

        zscore zset:user3 test
        
      • 4,计算成员的排名

        Redis提供了两个查询成员排名的命令:分别是zrank(将分数从低到高返回排名)和zrevrank(将分数从高到低返回排名)。

        例如下边的操作,查询tomzset:user1中的两种排名

        从低到高查询排名 请注意排名是从0开始计数的,通过存在分值一样的会根据元素的字典值来进行排序,具体的可见附件部分。

        zrank zset:user1 tom1
        

        从高到低查询排名

        zrevrank zset:user1 tom1
        
      • 5,删除成员

        Redis提供了zrem用于删除指定成员(支持删除多个)

        以下操作会将tomzset:user1中删除,返回的为成功删除的个数

        zrem zset:user1 tom
        
      • 6,返回指定排名范围的成员

        有序集合是按照分值排名的,`zrange`是从低到高返回,`zrevrange`反之。它的索引排序依旧是`[0,-1]`。如果加上`withscores`选项,同时返回成员的分数。
        

        例如下面示例返回zset:user1中排名最低的三个成员

        zrange zset:user1 0 2 withscores
        
      • 7,返回指定分数范围的成员或个数

        同时Redis还提供了查询指定分数范围的方法zrangebyscore 以及zrevrangebyscore 其中前者是按照分数从低到高排序,后者反之。同样提供了winthscores选项用于返回成员的分数。

        同样我们先查询一下zset:user1 中分数范围是500 -600 的元素名称,发现返回空

        zrangebyscore zset:user1 500 700
        

        我们来查询和260-262中的元素。返回为此区间的元素个数

        zrangebyscore zset:user1 200 300
        
      • 8,我们还可以使用zcount查询指定分数范围内的成员个数。

        我们使用它的查询260-265中的元素个数

         zcount zset:user1 200 300
        
  2. 二,集合间的操作

    1. 首先插入两个有序集合在 Redis 中如图所示

      zadd user:ranking:1 1 kris 91 mike 200 frank 220 tim 250 martin 251 tom
      
      zadd user:ranking:2 8 james 77 mike 625 martin 888 tom
      

      zset双集合图片

    • 1,交集

      首先介绍一下语法

      zinterstore destination numkeys key [key ...] [weights weight [weight ...]]
      [aggregate sum|min|max]
      

      这个命令的参数比较多,下面我们分别介绍说明一下:

      • destination: 交集计算结果保存到这个键 。

      • numkeys: 需要做交集计算键的个数 。

      • key[key...]: 需要做交集计算的键。

      • weights weight[weight...]: 每个键的权重, 在做交集计算时, 每个键中的每个member会将自己分数乘以这个权重, 每个键的权重默认是1。

      • aggregate sum|min|max: 计算成员交集后, 分值可以按照sum( 和) 、min( 最小值) 、 max( 最大值) 做汇总, 默认值是sum

      下面我们列举几个场景来对这些参数加深一下了解

      1. 1)下面操作对user: ranking: 1user: ranking: 2做交集,

        zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2
        

        我们可以发现返回结果为3 这是因为weightsaggregate使用了默认配置,对目标user: ranking: 1_inter_2对分值做了sum操作

        我们来通过zrange命令来查询是否输出到指定键

         zrange user:ranking:1_inter_2 0 -1 withscores
        
      2. 2)如果想让user: ranking: 2的权重变为0.5, 并且聚合效果使用max, 可以执行如下操作:

        zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2 weights 1 0.5
        

        同样我们查询一下是否输出到指定键

        zrange user:ranking:1_inter_2 0 -1 withscores
        
    • 2,并集

      我们还是先来了解一下语法

      zunionstore destination numkeys key [key ...] [weights weight [weight ...]]
      [aggregate sum|min|max]
      

      该命令的所有参数和zinterstore是一致的, 只不过是做并集计算, 例如下面操作是计算user: ranking: 1user: ranking: 2的并集, weightsaggregate使用了默认配置, 可以看到目标键user: ranking: 1_union_2对分值做了sum操作:

      zunionstore user:ranking:1_union_2 2 user:ranking:1 user:ranking:2
      

      我们可以发现返回结果为7 这是因为weightsaggregate使用了默认配置,对目标user: ranking: 1_inter_2对分值做了sum操作

      我们来通过zrange命令来查询是否输出到指定键

      zrange user:ranking:1_union_2 0 -1 withscores
      
内部编码

接下来我们来了解一下Zset类型的内部编码。

有序结合类型的内部编码有两种

  1. ziplist (压缩列表):当有序集合的元素个数小于 zet-max-ziplist-entries 配置(默认128个),同时每个元素的值都小于 zet-max-ziplist-value 配置 (默认64字节)时,Redis 会用 ziplist 来作为有序集合的内部实现,ziplist 可以有效减少内存的使用
  2. skiplist (跳跃表) : 当 ziplist 条件不满足时,有序列表会使用 skiplist 作为内部实现,因为此时 ziplist 的读写效率会下降。
使用场景

有序结合比较典型的应用场景就是排行榜系统,例如视屏网站需要对用户上传的视屏做排行榜,榜单的维度可能是多个方面的: 按照时间、按照播放数量、按照获得的赞数、本节使用赞数这个维度,记录每天用户是上传视屏的排行榜。主要需要实现以下4个功能。

  1. 1,添加用户赞数

    用户 mike 上传一个视屏,并获得了3个赞,可以使用有序集合的 zadd zincrby 功能:

    zadd user:ranking:2021_12_20  3 mike
    

    如果之后再获得了一个赞,可以使用 zincrby

    zincrby user:ranking:2021_12_20  1 mike
    

    2,取消用户赞数

    由于各种原因(例如用户注销,用户作弊)需要将用户删除,可以使用 zrem

    zrem user:ranking:2021_12_20 mike
    
  2. 3,展示获取赞数最多的十个用户

    此功能使用 zrevange 命令实现:

    zervangebyrank user:ranking:2021_12_20 0 9
    

    4,展示用户信息以及用户分数

    此功能将用户名作为键的后缀,将用户信息保存在哈希类型中,至于用户的分数和排名可以使用 zscore zrank 两个功能

    hgetall user:info:tom
    zscore user:ranking:2021-12_20 mike
    zrank  user:ranking:2021_12_20 mike
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值