Redis常用数据结构及其应用场景解析

1. 概述

Redis 一个开源的基于键值对(Key-Value)NoSQL 数据库。使用 ANSIC 语言编写、支持网络、基于内存但支持持久化。性能优秀,并提供多种语言的 API。

我们要首先理解一点,我们把 Redis 称为 KV 数据库,键值对数据库,那就可以把 Redis 内部的存储视为存在着一个巨大的 Map,对 Map 的操作无非就是get 和 put,然后通过 key 操作这个 key 所对应的 value,而这个 value 的类型可以多种多样,也就是 Redis 为我们提供的那些数据结构,比如字符串(String)、哈希(Hash)等等。

Redis 会将所有数据都存放在内存中,所以它的读写性能非常惊人。不仅如此,Redis 还可以将内存的数据利用快照和日志的形式保存到硬盘上,这样在发生类似断电或者机器故障的时候,内存中的数据不会丢失。

除了上述功能以外,Redis 还提供了键过期、发布订阅、事务、流水线、Lua 脚本等附加功能。

1.1 应用场景

缓存

缓存机制几乎在所有的大型网站都有使用,合理地使用缓存不仅可以加快数据的访问速度,而且能够有效地降低后端数据源的压力。Redis 提供了键值过期时间设置,并且也提供了灵活控制最大内存和内存溢出后的淘汰策略。可以这么说,一个合理的缓存设计能够为一个网站的稳定保驾护航。

排行榜系统

排行榜系统几乎存在于所有的网站,例如按照热度排名的排行榜,按照发布时间的排行榜,按照各种复杂维度计算出的排行榜,Redis 提供了列表和有序集合数据结构,合理地使用这些数据结构可以很方便地构建各种排行榜系统。

计数器应用

计数器在网站中的作用至关重要,例如视频网站有播放数、电商网站有浏览数,为了保证数据的实时性,每一次播放和浏览都要做+1 的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis 天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。

社交网络

赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常比较大,而且传统的关系型数据不太适合保存这种类型的数据,Redis 提供的数据结构可以相对比较容易地实现这些功能。

消息队列系统

消息队列系统可以说是一个大型网站的必备基础组件,因为其具有业务解耦、 非实时业务削峰等特性。Redis 提供了发布订阅功能和阻塞队列的功能,虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功能基本可以满足。(我上家公司就用过 Redis 做消息队列,虽然后面换了其他 MQ)。

1.2 特性

速度快

正常情况下,Redis 执行命令的速度非常快,官方给出的数字是读写性能可以达到 10 万/秒。

基于键值对的数据结构服务器

几乎所有的编程语言都提供了类似字典的功能,例如 Java 里的 map,类似于这种组织数据的方式叫作基于键值的方式,与很多键值对数据库不同的是,Redis 中的值不仅可以是字符串,而且还可以是具体的数据结构,这样不仅能便于在许多应用场景的开发,同时也能够提高开发效率。

Redis 的全称是 Remote Dictionary Server,它主要提供了 5 种数据结构:字符串、哈希、列表、集合、有序集合,同时在字符串的基础之上演变出了位图(Bitmaps)和 HyperLogLog 两种数据结构,并且随着 LBS (Location BasedService,基于位置服务)的不断发展,Redis 中加入有关 GEO(地理信息定位)的功能。

丰富的功能

除了 5 种数据结构,Redis 还提供了许多额外的功能:提供了键过期功能,可以用来实现缓存。

提供了发布订阅功能,可以用来实现消息系统。支持 Lua 脚本功能,可以利用 Lua 创造出新的 Redis 命令。提供了简单的事务功能,能在一定程度上保证事务特性。提供了流水线(Pipeline)功能,这样客户端能将一批命令一次性传到 Redis,减少了网络的开销。

简单稳定

Redis 的简单主要表现在三个方面。

首先,Redis 的源码很少,早期版本的代码只有 2 万行左右,3.0 版本以后由于添加了集群特性,代码增至 5 万行左右。

其次,Redis 使用单线程模型,这样不仅使得 Redis 服务端处理模型变得简单,而且也使得客户端开发变得简单。

最后,Redis 不需要依赖于操作系统中的类库。

Redis 虽然很简单,但是不代表它不稳定。实际的运行中很少出现因为 Redis 自身 bug 而宕掉的情况。

客户端语言多

Redis 提供了简单的 TCP 通信协议,很多编程语言可以很方便地接人到 Redis。

持久化

通常看,将数据放在内存中是不安全的,一旦发生断电或者机器故障,重要的数据可能就会丢失,因此Redis提供了两种持久化方式:RDB 和 AOF,即可以用两种策略将内存的数据保存到硬盘中,这样就保证了数据的可持久性。

主从复制

Redis 提供了复制功能,实现了多个相同数据的 Redis 副本,复制功能是分布式Redis 的基础。

高可用和分布式

Redis Sentinel,它能够保证 Redis 节点的故障发现和故障自动转移。Redis 从 3.0 版本正式提供了分布式实现 Redis Cluster,它是 Redis 真正的分布式实现,提供了高可用、读写和容量的扩展性。

2. 下载安装

目前演示的安装操作是基于 Centos7 下讲解。

官方下载地址:https://redis.io/download

# 先新建一个目录
mkdir /usr/local/redis
cd /usr/local/redis
# 下载
wget https://download.redis.io/releases/redis-6.2.6.tar.gz
# 解压
tar -xzvf redis-6.2.6.tar.gz
# 编译
cd redis-6.2.6
make

2.1 启动

Redis 有三种方法启动 Redis:默认配置、带参数启动、配置文件启动。

1️⃣ 默认配置

进入安装好的 Redis 的 src目录下执行以下命令:

./redis-server 
image-20211215203151805

可以看到直接使用 redis-server 启动 Redis 后,会打印出一些日志,通过日志 可以看到一些信息:

当前的 Redis 版本的是 64 位的 6.2.6,默认端口是 6379。Redis 建议使用配置文件来启动,所以这种方式是不会在生产环境中使用的。

2️⃣ 参数启动

redis-server 加上要修改配置名和值(可以是多对),没有设置的配置将使用默认配置,例如:如果要用 6380 作为端口启动 Redis,那么可以执行:

./redis-server --port 6380 
image-20211215203128782

不过这种方式一般也用得比较少。

3️⃣ 配置文件启动

将配置写到指定文件里,并启动,主要修改的是安装目录下的 redis.conf文件。

./redis-server ../redis.conf
image-20211215203701640

2.2 操作

Redis 服务启动完成后,就可以使用 redis-cli 连接和操作 Redis 服务。

image-20211215203942143

2.3 停止

Redis 提供了 shutdown 命令来停止 Redis 服务,例如我们目前已经启动的 Redis 服务,可以执行:

./redis-cli -p 6379 shutdown 

Redis 服务端将会显示:

2853:M 15 Dec 2021 20:41:26.593 # User requested shutdown...
2853:M 15 Dec 2021 20:41:26.593 * Saving the final RDB snapshot before exiting.
2853:M 15 Dec 2021 20:41:26.594 * DB saved on disk
2853:M 15 Dec 2021 20:41:26.594 * Removing the pid file.
2853:M 15 Dec 2021 20:41:26.594 # Redis is now ready to exit, bye bye...

除了可以通过 shutdown 命令关闭 Redis 服务以外,还可以通过 kill 进程号的方式关闭掉 Redis,但是强烈不建议使用 kill -9 强制杀死 Redis 服务,不但不会做持久化操作,还会造成缓冲区等资源不能被优雅关闭,极端情况会造成 AOF 和复制丢失数据的情况。

shutdown 还有一个参数,代表是否在关闭 Redis 前,生成持久化文件:

./redis-cli -p 6379 shutdown nosave/save

默认是 save,生成持久化文件,如果是 nosave 则不生成持久化文件。

3. 全局命令

在了解 Redis 的数据结构之前,先了解 Redis 的一些全局命令。

命令 说明
keys * 查看所有键,同时也支持通配符,如 keys n*
dbsize 返回当前数据库中键的总数
exists 检查键是否存在,存在返回 1,不存在返回 0,如 exists name
del 删除键,无论值是什么数据结构类型,del 命令都可以将其删除。返回删除键个数,删除不存在键返回 0。同时 del 命令可以支持删除多个键,如 del name age
expire Redis 支持对键添加过期时间,当超过过期时间后,会自动删除键,时间单位秒,如 expire name 10
ttl ttl 命令会返回键的剩余过期时间,若返回 -1 则表示键没设置过期时间,-2 键不存在
type 返回键的数据结构类型
randomkey 随机返回一个键
rename 键重命名,为了防止被强行 rename,Redis 提供了 renamenx 命令,确保只有 newKey 不存在时候才被覆盖。由于重命名键期间会执行 del 命令删除旧的键,如果键对应的值比较大,会存在阻塞 Redis 的可能性

注:

  1. dbsize 命令在计算键总数时不会遍历所有键,而是直接获取 Redis 内置的键总数变量,所以 dbsize 命令的时间复杂度是 O(1)。而 keys 命令会遍历所有键,所以它的时间复杂度是 o(n),当 Redis 保存了大量键时线上环境禁止使用 keys 命令;
  2. 除了 expire、ttl 命令以外,Redis 还提供了 expireat、pexpire,pexpireat、pttl、persist 等一系列命令,可自行查验。

4. 基本数据结构

Redis 提供了一些数据结构供我们往 Redis 中存取数据,最常用的的有 5 种,字符串(String)、哈希(Hash)、列表(list)、集合(set)、有序集合(ZSET)。

4.1 String

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

1️⃣ 常用命令

设置值 set

set key value [ex seconds] [px milliseconds] [nxlxx]
  • ex seconds:为键设置秒级过期时间。
  • px milliseconds:为键设置毫秒级过期时间。
  • nx:键必须不存在,才可以设置成功,用于添加。
  • xx:与 nx 相反,键必须存在,才可以设置成功,用于更新。

其中,ex 参数和 expire 命令基本一样。还有一个需要特别注意的地方是如果一个字符串已经设置了过期时间,然后你调用了 set 方法修改了它,它的过期时间会消失。

除了 set 选项,Redis 还提供了 setex 和 setnx 两个命令:

  • setex key seconds value
  • setnx key value

setex 和 setnx 的作用和 ex 和 nx 选项是一样的。也就是,setex 为键设置秒级过期时间,setnx 设置时键必须不存在,才可以设置成功。

有什么应用场景吗?

以 setnx 命令为例子,由于 Redis 的单线程命令处理机制,如果有多个客户端同时执行 setnx key value,根据 setnx 的特性只有一个客户端能设置成功,setnx 可以作为分布式锁的一种实现方案。

获取值 get

get key

如果要获取的键不存在,则返回 nil

另外,除了单个设置和获取键值,Redis 还支持批量操作。

批量设置值 mset

mset name ayue age 20 sex 男

批量获取值 mget

mget name age sex
image-20211216163431238

如果有些键不存在,那么它的值为 nil,结果是按照传入键的顺序返回。

批量操作命令可以有效提高效率,假如没有 mget 这样的命令,要执行 n 次 get 命令具体耗时如下:

n 次 get 时间 = n 次网络时间 + n 次命令时间

使用 mget 命令后,要执行 n 次 get 命令操作具体耗时如下:

n 次 get 时间 = 1 次网络时间 + n 次命令时间

Redis 可以支撑每秒数万的读写操作,但是这指的是 Redis 服务端的处理能力,对于客户端来说,一次命令除了命令时间还是有网络时间,假设网络时间为 1 毫秒,命令时间为 0.1 毫秒(按照每秒处理 1 万条命令算),那么执行 1000 次 get 命令需要 1.1 秒(1000*1+1000*0.1=1100ms),1 次 mget 命令的需要 0.101 秒 (1*1+1000*0.1=101ms)。

数字运算 incr

incr 命令用于对值做自增操作,返回结果分为三种情况:

  1. 值不是整数,返回错误;
  2. 值是整数,返回自增后的结果;
  3. 键不存在,按照值为 0 自增,返回结果为 1。
incr key
image-20211216170800153

除了 incr 命令,Redis 提供了 decr(自减)、 incrby(自增指定数字)、decrby(自减指定数字)、incrbyfloat(自增浮点数)。

追加指令 append

append 可以向字符串尾部追加值。

append key value
image-20211216171635960

strlen

返回字符串长度。

strlen key

截取字符串 getrange

getrange 截取字符串中的一部分,形成一个子串,需要指明开始和结束的偏移量,截取的范围是个闭区间。

image-20211216171944312
命令 说明 时间复杂度
get key 获取值 O(1)
del key [key ...] 删除key O(N)(N是键的个数)
mset key [key value ...] 批量设置值 O(N)(N是键的个数)
mget key [key ...] 批量获取值 O(N)(N是键的个数)
incr key 将 key 中储存的数字值增一 O(1)
decr key 将 key 中储存的数字值减一 O(1)
incrby key increment 将 key 所储存的值加上给定的增量值(increment) O(1)
decrby key increment key 所储存的值减去给定的减量值(decrement) O(1)
incrbyfloat key increment 将 key 所储存的值加上给定的浮点增量值(increment) O(1)
append key value 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾 O(1)
strlen key 返回 key 所储存的字符串值的长度。 O(1)
setrange key offset value 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始 O(1)
getrange key start end 返回 key 中字符串值的子字符 O(N)(N是字符串的长度)

2️⃣ 命令的时间复杂度

字符串这些命令中,除了 del 、mset、 mget 支持多个键的批量操作,时间复杂度和键的个数相关,为 O(n),getrange 和字符串长度相关,也是 O(n),其余的命令基本上都是 O(1)的时间复杂度,在速度上是非常快的。

3️⃣ 使用场景

字符串类型的使用场景很广泛,如下:

1、缓存功能

Redis 作为缓存层,MySQL 作为存储层,绝大部分请求的数据都是从 Redis 中获取。由于 Redis 具有支撑高并发的特性,所以缓存通常能起到加速读写和降低 后端压力的作用。

2、计数

使用 Redis 作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据可以异步落地到其他数据源。

3、共享 Session

一个分布式 Web 服务将用户的 Session 信息(例如用户登录信息)保存在各 自服务器中,这样会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,用户刷新一次访问可能会发现需要重新登录,这个问题是用户无法容忍的。

为了解决这个问题, 可以使用 Redis 将用户的 Session 进行集中管理,在这种模式下只要保证 Redis 是高可用和扩展性的,每次用户更新或者查询登录信息都直接从 Redis 中集中获取。

4、限时

很多应用出于安全的考虑,会在每次进行登录时,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率,例如一分钟不能超过 5 次。一些网站限制一个 IP 地址不能在一秒钟之内访问超过 n 次。或者同一 IP 在短时间内多次浏览谋篇文章浏览次数不会一直增加。点赞次数在短时间内不能重复点赞。

4.2 Hash

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。

Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

1️⃣ 常用命令

基本上,哈希的操作命令和字符串的操作命令很类似,很多命令在字符串类型的命令前面加上了 h 字母,代表是操作哈希类型,同时还要指明要操作的 field 的值。

hset

hset key field value

如果设置成功会返回 1,反之会返回 0。此外 Redis 提供了 hsetnx 命令,它们的关系就像 set 和 setnx 命令一样,只不过作用域由键变为 field。

127.0.0.1:6379> hset hash:test name ayue
(integer) 1
127.0.0.1:6379> 

hget

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空