Redis是一个比较常见的内存数据库,支持许多数据结构,可以被用来实现缓存、消息队列和分布式锁。

Redis的架构

客户端-服务端架构

Redis采用CS架构,可以使用redis-cli连接到Redis服务端。也可以通过多种语言的客户端库,如Go的github.com/go-redis/redis/v8。使用redis-cli连接的格式如下:

redis-cli -h <host> -p <port> -a <password>

Linux Redis服务端的配置文件一般在/etc/redis/redis.conf,也可以通过客户端来动态修改。例如:

redis-cli CONFIG SET maxmemory 256mb

单线程模型

6.0版本之前的Redis,是单线程的,由一个线程来完成网络IO、命令解析和数据操作。6.0版本之后,Redis引入多线程处理网络IO,但是命令执行依然是单线程的。

为什么要引入多线程处理网络IO? 但是命令执行还要保持单线程? 使用多线程不是更好?

当时Redis的瓶颈在于网络IO,单线程需要处理所有网络读写操作,当连接数过多时,网络IO占用大量时间,阻塞线程,导致命令执行效率下降和CPU利用率不足。而多线程处理网络IO使得Redis在高并发和大数据传输场景下表现更好。

命令执行保持单线程的原因在于简化实现和避免锁竞争。

Redis有哪些线程?

  • 主线程:命令解析、数据操作、结果返回。
  • I/O线程:处理网络读写,如读取客户端请求、发送响应。
  • 后台线程:处理异步任务。
    • 持久化:将内存数据持久化到磁盘中。
    • 异步删除:删除大对象(大列表、大哈希)或过期键时,异步释放内存。
    • 复制:将主节点数据同步到从节点。
    • 集群通信:在Redis Cluster模式下,负责节点通信,交换状态信息、数据迁移和故障检测(独立于主线程运行)。

客户端-服务端架构和单线程模型,基本已经描述完了一个Redis进程的架构和工作。而现实中,为了保证高性能、高可用,通常需要多个不同状态的Redis进程协同地工作,这些进程之间通过TCP+RESP(Redis Serialization Protocol)协议进行通信。我们经常会看到“主从复制”、“哨兵”和“Redis集群”等字眼,这些都是协同工作的方式。在这篇博客主要关注架构上的考虑,具体实现上的问题,记录到高可用篇。

主从复制架构

主从复制架构中每个Redis进程是怎么工作的?

主从复制架构中有两种Redis进程,即主节点和从节点(本质上是进程)。主节点负责写操作,从节点同步主节点数据,处理读操作。

为什么要有主从复制架构? 一个Redis进程不就行了? 多一个从节点还有通信和同步的开销?

主要的原因在于高并发和高可用:

  • 提升读性能:扩展读性能、减轻主节点的压力、提高系统吞吐量。
  • 读进程单点故障:提高服务可用性、提供数据备份。

解决了读进程的单点故障,如果主节点挂掉,服务依然不可用,怎么解决?(哨兵模式)

  • 哨兵模式:作为主从复制架构的高可用扩展。在哨兵模式中,Redis进程被分为:主节点、从节点和哨兵节点(本质都是进程)。哨兵进程监控主从架构中的节点状态、并在主节点故障时进行自动故障转移。客户端通过哨兵获取主节点地址,但是数据操作都是和主从节点直接交互的。
  • 哨兵集群:一个哨兵进程也会存在单点故障、所以Redis又提供了哨兵集群的机制。但是这个“哨兵集群”和分布式架构中的Redis集群(cluster)是完全不同的机制,并且它们提供的是不同的故障转移机制,一般是不共存的。

分布式架构

如果数据量和并发请求量达到一台机器承受不了,应该怎么保证Redis性能和可用性呢?

Redis提供了cluster模式,以分布式架构来解决单机Redis在存储容量、性能和可用性的限制,Redis集群中的进程只包括主节点、从节点,节点之间通过Gossip协议交换状态信息。客户端可以直接与任意节点通信,节点会将请求路由到正确的节点。分布式架构从两个方面保障Redis性能和可用性。

  • 数据分片:将数据分散到多个主节点,每个主节点只存放部分数据。
  • 单点故障:当某个主节点故障,集群会自动选举一个从节点作为新的主节点。注意集群模式中没有哨兵。

应该怎么选择哨兵模式和集群模式?

  • 哨兵模式适用于小规模、高可用场景:数据量小、读写压力小、高可用需求(自动故障转移),但是不需要分布式存储,简单架构。
  • 集群模式适用于大规模、高并发、分布式场景:数据量大到单个节点无法存储,高并发、分布式存储,支持水平扩展。

总结

Redis的架构可以从CS架构、单线程模型、主从复制架构和分布式集群架构来描述,主要的线索是保证高性能(存储、并发)和高可用(单点故障)。要注意集群模式内置了主从架构,但是不支持哨兵,它们提供的是两套故障转移机制。