简介

Redis 全称为 Remote DIctionary Server,本质上是一个 key-value 存储系统,属于跨平台的非关系型数据库。Redis 官方对它的定义是:

Redis is an open source (BSD licensed), in-memory data structure store, used database, cache and message broker.

翻译成中文就是:Redis 是一个开源的(BSD 许可),基于内存的数据结构存储系统,可用作数据库、缓存和消息中间件。从官方的介绍上看,Redis 的功能很丰富,我们平时最常用的功能主要是缓存。

它支持多种类型的数据结构,包括字符串(string)、哈希(hash)、列表(list)、集合(set)、有序集合(sorted set),除了这些基本类型外,还包括范围查询、bitmaps、hyperloglogs和地理空间的索引半径查询。Redis 内置了复制、LUA脚本、LRU驱动事件、事务和不同级别的磁盘持久化,并且通过 Redis 哨兵(Sentinel)和自动分区(Cluster)提供高可用性。

Redis 特点

与其他 key-value 存储系统相比,Redis 具有以下一些特点:

  • 支持数据持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。在磁盘格式上,数据是通过追加的方式产生的,因此不需要进行随机访问。
  • 不仅支持简单的 key-value 的数据缓存,同时还支持 list,set,zset,hash 等数据的存储。所有数据类型对程序员透明,无需进行额外的抽象。
  • 支持 master-slave模式的数据备份。

总的来说,Redis 具有以下优势:

  • 性能极高。读取速度为 110000次/s,写入速度为 81000次/s。
  • 数据类型丰富。支持 string、list、hash、sets、ordered set 等数据类型。
  • 原子性。所有的操作都是原子性的,单个操作是原子性的,多个操作可支持事务,通过 MULTI 和 EXEC 指令封装起来。
  • 特性丰富。Redis 还支持订阅/发布、通知、key 过期等特性。

Redis 持久化

Redis 支持数据持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。Redis 提供了两种持久化的方式,分别为 RDB(Redis DataBase) 和 AOF (Append Only File)。

RDB(Redis DataBase)

RDB 方式是将 Redis 某一时刻的数据生成快照并且存储到磁盘等介质上。具体表现为,Redis 在进行数据持久化的过程中,会先将数据写入到一个临时文件中,等到持久化过程都结束了,才会用这个临时文件替换上次进行持久化的文件。这样的话,快照文件总是完整可用的,因此备份和恢复操作可以随时进行。

RDB方式在性能上具有较大优势。采用 RDB 方式时,Redis 会另外 fork 一个子进程来进行持久化,而主进程是不会进行任何的 IO 操作,从而确保了 Redis 的性能。如果需要进行大规模的数据恢复,而且对数据恢复的完整性要求不是很高,那么 RDB 方式相比 AOF 方式则显得更加的高效。

但是,相对而言 RDB 的缺点就在于,其对数据完整性的保证较弱。假如设置了每隔 10 分钟进行一次持久化,那么当 Redis 故障时,将会有近 10 分钟的数据丢失。在这种情境下,采用 AOF 更优。

AOF(Append Only File)

顾名思义,AOF 通过追加的方式改写文件,将执行过的写入指令记录下来,在数据恢复时按照从前到后的顺序将所有指令都执行一遍。

由于 AOF 采用的是追加的方式,所以经过一段时间之后 AOF 文件会变得越来越大。为此,Redis 提供了 AOF 文件的重写机制,当 AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集。简单来说,就是通过某种机制,将一部分指令合并成一条指令,但仍然保证指令执行后结果的不变性。

默认的 AOF 持久化策略是每隔一秒将缓存中的指令记录到磁盘中,因此即便 Redis 发生故障,也只会丢失最近 1 秒的数据。在重写 AOF 时,依旧是先写临时文件,全部完成后再替换的流程,所以遇到磁盘空间满、inode 满或者断电等情况都不会影响 AOF 文件的可用性,而且 Redis 提供了 redis-check-aof 工具,可以用来进行日志修复。

AOF 重写在内部进行时,Redis 会先 fork 一个负责重写的子进程,这个子进程会先读取现有的 AOF 文件,然后将其包含的指令进行分析并且压缩写入到一个临时文件中。同时,主进程将接收到的写入指令一边累积到内存缓冲区中,一边继续写入到原有的 AOF 文件。这样做是为了保证原有 AOF 文件的可用性,避免在重写过程中出现意外。当子进程完成重写工作后,会给主进程发送一个信号,随后主进程就会将内存中缓存的写入指令追加到新 AOF 文件中。当追加结束后,Redis 就会用新的 AOF 文件替代原有的 AOF 文件。之后出现的新指令,都会追加到新的 AOF 文件中。

redis数据库是干嘛的 redis数据库介绍_redis数据库是干嘛的

实际上,RDB 和 AOF 两种方式可以结合使用,这也是官方所建议的。当 Redis 重启时,由于 AOF 方式的数据恢复完整度更高,会优先采用 AOF 的方式来进行数据恢复。

C/S 架构

根据 Redis 的官方介绍可以知道,Redis 本质上是一个 key-value 的数据存储系统,也就是一个大型的 hash 表。在我们的实际应用中,通常可以通过使用 Redis 的命令行,或者各种语言的 Redis API,在代码里对这个 hash 表进行操作。这个操作的平台就是 Redis 客户端,而 hash 表所在的位置就是 Redis 服务端。因此,Redis 其实是一个 C/S 架构的系统。这个架构中的客户端和服务端,可以在同一台机器上,也可以分别在不同机器上。

redis数据库是干嘛的 redis数据库介绍_redis数据库是干嘛的_02


redis数据库是干嘛的 redis数据库介绍_缓存_03

单线程 Redis

需要注意的是,Redis 的服务端是单线程服务器,基于 event-loop 模式来处理客户端的请求。使用单线程的好处在于:

  • 不需要考虑线程安全问题。很多操作不需要加锁,简化开发过程,提高了性能。
  • 不需要使用各种线程锁。减少线程切换导致的性能消耗。

之所以 Redis 一开始设计成单线程,是因为它的操作都是基于内存的,cpu 一般不会成为 Redis 的瓶颈。为了充分发挥 cpu 性能,通常可以通过在单机启动多个 Redis 实例,也就是启动多个进程来进行完善。由于 Redis 是非关系型数据库,数据之间没有约束,客户端只需要清除 key 与 Redis 进程之间的对应关系即可。

单线程的 Redis 能够快速执行指令,还因为它采用了非阻塞的 IO 多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。从而通过复用同一个线程,在多连接的时候依旧保证了系统的吞吐量。

单线程的核心效率非常高。在当今的计算环境里,随着 Redis 客户端越来越多,一台机器的内存和吞吐量毕竟是有限的,即便是单机多线程也往往不能满足业务的需求,因此单线程多服务器集群化的方案是如今比较常用的解决方案。这对这些方案,客户端的请求会通过负载均衡算法(通常是一致性 hash 算法),分散到不同的 Redis 服务器上,从而降低单个服务器的访问压力。集群的策略,实际上扩大了缓存容量,提升了服务端的吞吐量。

多线程 Redis

不过,在 Redis 6.0 之后的版本抛弃了单线程模型的设计,开始引入了多线程模型。之所以引用多线程,必然是在某些方面,单线程的设计不再具有它的优势。

一个主要的体现在于,读写操作在 Redis 中会占用大部分的 cpu,通过多线程的方式将读写操作分离对性能会有很大的提升。需要注意的是,Redis 的多线程部分只用于处理网络数据的读写的协议解析,执行内部命令依旧是单线程的模式。这样子在引入多线程提升性能的同时,还保持了原有的单线程设计所能带来的优势。

Redis 主从同步

上面我们提到了 Redis 集群化的方案,这种方案能够有效提升 Redis 的工作效率,但任然会存在一些问题,如:

  • 数据可用性差。如果其中一台 Redis 挂了,那么上面全部的缓存数据都会丢失,导致原来可以从缓存中获取的数据,都要通过访问数据库才能获得,增加了数据库的压力。
  • 数据查询缓慢。当某一个数据的访问量非常高,也就是存在大量请求都是去查询一个相同的数据,那么该数据所在的 Redis 机器的访问量就会非常高,吞吐量不足的话难以支撑如此高的负载。

针对上述问题,我们可以逐步进行分析:

  1. 要解决数据可用性问题,我们可以采用数据库常用的 master-slave 模式,给每一台 Redis 机器都加上一台 slave 实现数据备份。如果 master 挂了,slave 可以升级为 master,从而保证数据高可用性。当 master 的访问量过多时,还能转发一部分的请求让 slave 去处理,也就是 master 负责读写或者只负责写,slave 可以只负责读。
  2. 为了让 master-slave 模式发挥更大的威力,我们可以对每一个 master 接入更多的 slave,但是这样的话 master需要进行数据备份的工作量变大了。也就是每增加一个 slave,master 就要多备份一次。因此可以采用 master-slave chain 模式,让 slave 负责其下 slave 的备份工作,从而释放 master。

综合上述分析,我们可以得到最终的 master-slave 架构如下:

redis数据库是干嘛的 redis数据库介绍_redis数据库是干嘛的_04

这样,既可以通过多台一级 slave 协助 master 工作,保证数据可用性和查询效率,又减少了 master 与 slave 进行备份同步的次数,将备份任务下发给更低一级的 slave,降低了 master 的备份压力。