Redis【2】—— Redis特性 与 数据类型
二、Redis 的基本介绍
(一)关于 Redis
简单来说 Redis 是一个在内存中存储数据的中间件。
1. 特性
(1)在内存中存储数据
使用键值对的方式存储数据,其中**key**
都是**string**
,**value**
**都是则可以是不同数据结构的数据。**与 MySQL
相比,Redis
这种以“键值对”的方式组织数据的方式,称为:非关系型数据库。
(2)可编程
可以直接通过简单的 交互式命令 操作,也可以通过 **Lua**
**脚本 **进行操作。
(3)可扩展
Redis
提供了一组API
,可以通过 C
、C++
、Rust
语言编写的动态链接库(可以理解为:函数库。在 **windows**
上是**.ddl**
文件,在**Linux**
上是 **.so**
文件),在原有的功能基础上再进行扩展。
(4)持久化
明明Redis
是在内存中存储数据,那么进程退出 或者 系统重启,都会导致数据的丢失,那怎么能说持久化呢?其实,这里的持久化是指:**Redis**
也会把数据存储在硬盘上,也就说:硬盘上备份了Redis
的数据。重启的**Redis**
只需要加载硬盘中的备份数据即可,这样Redis
的内存就能恢复到重启前的状态。
(5)多集群
一个 Redis
存储的数据是有限的,为了尽可能多地使用到 Redis
的特性,就需要引入多台主机,将**Redis**
部署到多个节点上。这样每个 Redis
都能存储一部分数据。能够被部署在多台机器上的Redis
也就拥有了多集群的特点。
(6)高可用
Redis
支持“主从”结构,从节点就相当于主节点的备份。
(7)快速高效 *
为什么Redis
会快呢?主要是一下四点原因:
- 从访问硬件上看,第一点也是
Redis
特性的第一点,**Redis**
数据存储在内存中,访问内存的速度 比 访问硬盘和数据库的速度快得多。 - 从业务角度上看,
Redis
核心功能的逻辑简单,大部分是操作内存的数据结构。相比起MySQL
还有聚合函数,链表查询等…Redis
的应用场景比较简单。 - 从网络角度上看,
Redis
使用了 **IO多路复用 **的方式(epoll),使用了一个线程管理多个socket
,即:同时去买三份小吃,哪一份完成就先响应哪一份,注意 - 从实现模型上看,
Redis
使用的是 **单线程 模型。**这减少了不必要的线程竞争开销。这里多解释一下,多线程能提高效率是有场景要求的,即 CPU密集性任务 的场景。此时,多个线程才能够充分利用 CPU的多核资源。
2. 应用场景
(1)把 Redis
当数据库
这里指的是把 Redis
当作全量数据库,也就是说 这些数据并不能丢失,而且这部分被存储的数据不仅多,而且需要快速被搜索到。想要这样使用Redis
就需要不少的硬件资源,而这只能发挥公司的“钞”能力了。
(2)把Redis
当Caching
和 session
当作缓存(Caching)的话,其实就是把热点数据提出来存储到**Redis**
中,也就是所谓的**冷热分离。**此时的Redis
存储部分数据,当Redis
重启之后,数据依然可以从MySQL
中读取回来。
当作会话(Session)的意思其实是:使用**Redis**
存储会话信息,所有的服务器最终统一地到 **Redis**
中获取会话信息即可。这样就不需要负载均衡器 根据 **userID**
做会话保持。
(3)把Redis
当消息队列
消息队列是一种在应用程序之间传递消息的通信模式。它允许发送者(生产者)将消息发送到一个队列中,然后接收者(消费者)可以从队列中获取并处理这些消息。消息队列实现了异步通信机制,提供了解耦和缓冲的功能,使得不同的应用程序或服务可以在时间和空间上解耦,以便更高效地进行通信。
1)消息队列通常由以下组件构成:
- 消息:要传递的数据或信息。
- 队列:存储消息的容器,通常是先进先出(FIFO)的数据结构。
- 生产者:将消息发送到队列的应用程序或服务。
- 消费者:从队列中获取消息并进行处理的应用程序或服务。
- 中间件:支持消息传递的中间软件,负责管理和维护队列,确保消息的可靠传递和处理。
2)消息队列的优点包括:
- 异步处理:生产者和消费者之间解耦,不需要即时响应,可以自由选择处理消息的时间和顺序。
- 削峰填谷:通过缓冲消息,消费者可以按照自身处理能力来消化消息,避免了系统因突发高峰而崩溃。
- 可伸缩性:可以根据需求增加或减少生产者和消费者的数量,以适应系统的负载变化。
- 系统解耦:不同的应用程序或服务之间通过消息队列进行通信,解耦了彼此之间的依赖关系,提高了系统的灵活性和可维护性。
- 可靠性:消息队列通常会提供持久化机制来确保消息的可靠传递,防止消息丢失。
消息队列在分布式系统、微服务架构、任务调度等场景下具有广泛的应用。一些常见的消息队列实现包括 RabbitMQ、Apache Kafka、ActiveMQ 等。但是,如果不想使用消息队列的功能,又不想引入过多的中间件,可以考虑只用Redis
做消息队列。
(二)基本指令
redis-server /etc/redis/redis.conf # 启动Redis
redis-cli # 进入客户端
set key1 value1
get key1
get key2 # 不存在返回nil
# 匹配模式
# ? 一个字符
# * 多个字符
# [xxxx] 出现什么匹配什么
# [^xxx] 出现什么排除什么
# [a-z] 出现范围(一位字符)
注意在生产环境上不要随便使用 keys *
,原因很简单:我们能够清晰地感受到这个操作的时间复杂度是 O(n)
,而Redis
是一个单线程服务器,且生产环境上的key
是很多的,那么这个操作执行的时间就很长,整Redis
服务器就会被阻塞住,无法给其他客户端提供服务!此时,其余的Redis
操作就会超时。
exists key1 key2 ...
# 时间复杂度是 O(N) ,N 是 key 的个数
注意下方的两种写法,上方那种优于下方的写法!因为**Redis**
**是一个 客户端-服务器 结构的程序,客户端和服务器之间通过网络来进行通信!**第二种分开写的方法相当于有两次网络请求,两次的开销必然是大于一次的。
keys * # 先查看一下 keys 有多少
del hllo
del hallo hello hzllo # 返回的是个数
删除数据是否危险还是要看场景!在把Redis
当作缓存的场景下,Redis
存储的是热点数据(全量数据是存储在MySQL
中的),如果只是删除一两条,那其实影响不大,因为热点数据也很有可能会更新,实在不行就放过几条去读MySQL
。但如果一下子删除太多,就容易把系统搞挂。因为Redis
是在帮MySQL
分担压力的,如果分担不了那压力自然就给到MySQL
了,大量的请求很可能就把 MySQL
冲击跨了。
set k v
get k
expire k 5 # 单位是秒,pexpire 单位是毫秒
ttl k # 当被删除之后返回的结果就是 -2;如果没有关联过期时间返回结果就是 -1;pttl 单位是毫秒
这个功能还是非常有用的,常见的场景比如:验证码,优惠券过期 等。
那么这个过期删除策略是怎么样的呢?主要有两个:定期删除 + 惰性删除。
所谓惰性删除就是:如果key
已经过期,但是并没有请求来查询,那么这个key
就暂时不会被删除,直到被请求访问的时候Redis
才会删除key
,同时返回一个nil
。但是这样并不靠谱的。**因此还需要结合“定时删除”。**不过,当有很多key
时,全部过滤一遍成本可能会很高,占用的时间可能会边长,占用时间一旦过长,就很可能出现跟上方keys *
一样的情况!因此,定期删除只是抽取一部分**key**
已经检查。
注意:Redis
并没有使用定时删除的策略。就算采用也不必未每一个key
设置一个定时器,未引入的原因很可能是因为Redis
最初是单线程的设计模式,如果使用定时器,势必要引入多线程,这与设计的初衷相悖。
但是上方两种策略依旧不能让人满意,因此**Redis**
还有内存淘汰机制。
# 常见数据类型有以下几种:
# none、string、list、set、zset、hash、stream
keys *
type key1
lpush key2 111 222 333
type key2
sadd key3 111 222 333
type key3
hset key4 k1 v1 k2 v2
type key4
三、数据类型 与 编码方式
(一)数据类型
上方只是常用的Redis
数据类型,Redis
还有其他的若干种数据类型,但适用的场景可能没这么广。这里还需要说明的是:**Redis**
**底层在实现上述数据结构时,会在源码层面针对上述实现进行特定优化,以 节省时间、节省空间。**也就是说:Redis
会承诺相关的操作时间复杂度不变,但背后实现的细节并不一定是所见的标准数据类型,在特定场景下,会采用其他数据类型实现。
(二)内部编码
为什么hash
要压缩为ziplist
?
因为在Redis
中的key
很多,对于的value
为hash
的也很多,但是每个hash
不是很大的情况下压缩成ziplist
就会比较节省空间,整个的空间占用率就会下降 且 查询速度并没有降下来。
跳表是什么?
跳表也是链表,只不过每个节点上有多个指针域。根据设置的特殊规则,使用跳表查询元素的时间复杂度是O(logN)
keys *
type key1
object encoding key1 # error 注意'object' 需要大写
OBJECT encoding key1