分布式缓存系统Redis原理解析

Redis作为内存数据库已经广泛应用于大数据领域,已经成为分布式架构下的基础组件。本文主要介绍了Redis内部的实现原理包括IO模型、内存管理、数据持久化等以及三种集群架构,旨在了解其中的实现机制。


1、Redis介绍

Redis是一个开源的、基于内存的分布式key-value数据库,支持多种数据类型以及对数据的原子性操作,并且将数据缓存在内存中提升效率。

1.1 Redis特性
作为一种分布式的内存数据库,与其它key-value数据库对比,Redis具有以下特性:
  • 性能极高:纯内存的操作读速度达到11w+次/s,写的速度达到8w+次/s

  • 支持数据的持久化:将内存中的数据保存到磁盘中,重启的时候再进行加载

  • 支持多种数据类型:包括string、list、set、zset、hash等,并支持多种不同方式的操作包括交集并集以及排序

  • 操作原子性:Redis所有对数据的操作都是原子的,要么成功要么失败。多个操作也支持事务,通过MULTI和EXEC指令包起来

  • 支持数据的备份,即利用主从模式进行的数据备份

  • 丰富的特性:支持publis/subscribe、通知、key过期等特性

另外,Redis作为内存数据库性能如此之高,除了基于内存的操作外,还有以下机制来保证:
  • 单线程操作,避免了频繁的上下文切换。Redis使用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU。

  • 采用了非阻塞的I/O多路复用机制。利用select、poll、epoll可以同时监察多个流的I/O事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。

1.2 Redis基础数据结构
Redis支持5种数据类型:String、List、Hash、Set和ZSet(sorted set有序集合)
  1. String字符串:最基本的数据类型,value值可以是字符串,也可以是数字,也可以是二进制的数据。一个key最多能存储512MB。

    1. String结构使用广泛,典型的应用是缓存用户信息,将用户信息使用JSON序列化为字符串保存到Redis中进行缓存。

  2. List列表:Redis列表相当于LinkList链表,主要功能有push、pop、获取元素等。List是双端链表的结构,按照插入的顺序排序,可以插入一个数据到列表的头部或者尾部。

    1. Redis的列表常用来做异步队列处理,将需要延后处理的任务结构体序列化成字符串保存为Redis列表,另一个线程从列表中轮询数据进行处理

  3. Hash哈希:Hash相当于HashMap是一个无序字典,实现上是string类型的field和value的映射表,也可以看成是数组+链表的二维结构。相比较而言,将对象存储为Hash类型比String类型占用更少的内存空间,并且存取的时候可以按照整个对象进行操作。

    1. 一般使用Hash存放用户信息对象数据,不同于String一次性需要序列化整个对象,Hash结构可以对对象进行部分获取,提高了性能

  4. Set集合:Set是String类型的无序集合,集合的成员是唯一的,不能出现重复数据。在Redis中Set是通过HashTable实现的

    1. Set可以用于存储活动中奖的ID,利用去重功能保证同一个用户不会中奖两次

  5. ZSet有序集合:也是string类型元素的集合,一方面是set保证内部value的唯一性,另一方面为每个value赋予一个score并按照score进行排序。在内部实现上使用的是跳跃列表的结构,类似于层级结构,最下面一层所有元素串联起来,每隔几个单元挑选另外代表再使用另外一级的指针串联起来。

分布式缓存系统Redis原理解析

1.3 Redis应用场景

Redis作为内存分布式数据库,主要配合关系型数据库用作高速缓存,另外可以结合本身特性实现分布式锁、订阅发布以及延迟队列等功能。

分布式缓存系统Redis原理解析

  1. 高速缓存:将高频热点的数据放到缓存中,降低对数据库的IO请求,提高性能

  2. 分布式锁:利用Redis的setnx功能来编写分布式锁,如果设置返回1,说明获取锁成功,否则获取锁失败

  3. 延迟队列:使用rpush/lpush操作如队列,再使用lpop/rpop出队列

  4. 分布式会话:以Redis为中心的session服务,session由session服务及内存数据管理

  5. 限流功能:利用滑动窗口实现简单限流,也有高级的漏斗限流算法实现

2、Redis原理解析

2.1 Redis总体架构

分布式缓存系统Redis原理解析

Redis组件的系统架构包括事件处理、数据存储及管理、用于系统扩展的主从复制及集群管理以及插件化扩展模块
  • 事件处理模块:利用AE事件驱动模型,进行高效的IO读写、命令执行以及时间事件处理,其中IO读写采用的是IO多路复用技术,后面会进行专项介绍

  • 数据管理模块:Redis内存数据存放在redisDB中,支持多种数据类型以key/value的形式存放在字典中;内存中的数据会持久化到磁盘,支持RDB和AOF两种方式

  • 集群扩展模块:Redis单节点扩展到集群有三种方式,包括主从模式、哨兵模式和集群模式

2.2 Redis的IO模型

分布式缓存系统Redis原理解析

1)非阻塞IO

在调用Socket读写方法时,默认都是阻塞的,比如Read方法传递个参数表示读取这么多字节后返回,如果没有读够线程会卡在那里,直到新的数据到来或者连接关闭,read方法才可以返回,线程才能继续处理。一般Write方法不会阻塞,除非写缓冲区写满会阻塞,直到缓存区空闲。非阻塞IO在Socket对象上会设置Non_Blocking选项,打开时读写都不会阻塞,读写的数量取决于分配的缓存区的空余大小。

2)I/O多路复用封装

I/O多路复用其实是在单个线程中通过记录跟踪每个I/O流的状态来管理多个I/O流。在Redis中提供了select、epoll、evport、kqueue几种选择,利用其可以同时监察多个流的I/O事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。

3)事件驱动轮询

事件轮询API用来解决读写不知道何时继续的问题,输入是读写描述符列表,输出是与之对应的可读可写事件,同时提供一个timeout参数。如果没有事件到来,最多等待timeout时间,线程处于阻塞状态,一旦期间有任何事件到来就可以立即返回,超过timeout时间没有事件到来也会立即返回。等到事件后,线程可以继续挨个处理相应的请求,处理完继续轮询。

2.3 Redis持久化
Redis的数据保存在内存中,如果出现宕机数据会全部丢失,因此需要一种机制来保证数据不丢失,这种机制称为Redis持久化。Redis持久化机制有两种:快照和AOF日志
  • 快照:全量备份,内存数据的二进制序列化形式

  • AOF日志:连续的增量备份,记录内存数据修改的指令记录文本。AOF日志在运行过程中变得非常庞大,Redis重启的时候需要加载AOF日志进行重放,这个时间就会变得很长,因此需要定期对AOF日志进行重写

2.3.1 RDB快照
RDB是Redis默认的持久化方法,按照一定的策略把Redis内存中的数据保存为RDB文件,RDB文件是内存数据的二进制序列化表示。Redis提供了两个命令执行持久化操作生成RDB文件:save和BGsave
  • Save命令:执行的时候会阻塞Redis服务器进程,执行过程中Redis服务不能处理其它请求

  • BGSave命令:执行时候会派生出folk子进程,由folk子进程完成RDB文件的创建,父进程继续完成其它的响应

众所周知,Redis是单线程程序,当在处理客户端请求的同时进行内存快照的时候,生成内存快照的过程中的IO操作会影响服务器的请求性能。持久化的同时,内存中的数据也在不断的变化,此时是无法通过单线程在一边接收业务请求的同时处理的。在Redis中引入了操作系统的COW(copy-on-write)机制来实现,在持久化的时候调用folk子进程完成持久化操作,而父进程继续响应服务请求。根据COW的机制,内存中的数据会被复制一份出来,子进程在做数据持久化的时候,不会修改现有的内存数据结构,只是对数据结构进行遍历读取,然后序列化写到磁盘中。

分布式缓存系统Redis原理解析

  • 优点:

    • 只生成一个文件,方便持久化

    • 容灾性好,持久化文件可以安全保存

    • 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化

  • 缺点:

    • RDB快照生成期间出现故障,会导致数据丢失几分钟

    • 生成快照期间如果文件很大,可能会影响客户端响应,对于秒杀等时效性要求高的业务影响较大

2.3.2 AOF日志

AOF(Append-only file)持久化是将Redis服务器所指向的写指令保存下来,记录对内存修改的指令记录。Redis在收到客户端修改指令后,先进行参数校验,如果没问题立即将该指令保存到本地AOF日志中,再执行指令。当Redis重启的时候,通过重新执行文件中AOF日志中的指令恢复内存中的数据。

Redis服务在执行AOF任务时候,会调用flushAppendOnlyFile函数,这个函数执行以下任务:
  • WRITE:根据条件,将aof_buf中的缓存写入到AOF文件

  • SAVE:根据条件,调用fsync或fdatasync函数,将AOF文件保存到磁盘中

AOF保存到磁盘中也有三种方式,根据性能需要选择,默认是每秒fsync一次
  • appendfsync always:收到写命令就立即写入磁盘,最慢,但是保证完全的持久化

  • appendfysnceverysec:每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中

  • appendfysnc no:完全依赖os,持久化没保证

分布式缓存系统Redis原理解析

AOF有个问题是在Redis长期运行过程中,AOF日志会变得越来越大,如果宕机重启,整个重放日志的过程非常耗时。为此Redis提供bgrewriteof指令用于对AOF日志进行瘦身,原理是开辟一个子进程对内存进行遍历转换成一系列的Redis操作指令,然后序列化到新的AOF日志文件中。序列化完毕后再将操作期间发生的增量AOF日志追加到这个新的AOF日志文件中,追加完毕后替换旧的AOF日志文件。

AOF持久化的方式相较于快照数据安全性得到保证,但是同等业务下AOF日志文件要比RDB大,同时fsync的操作会影响性能,在开启AOF后Redis支持的APS要比RDB条件下的低。通常在集群架构下,主节点不会开启持久化操作,而是在从节点进行,因为从节点是备份节点,没有客户端请求压力,操作系统资源也相对充沛,对业务影响较小。

2.4 Redis事务与一致性

Redis中的事务可以一次执行多个指令,所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

2.4.1 Redis事务的实现
在Redis中由几个特殊的指令实现事务:MULTI、EXEC、WATCH、DISCARD
  • MULTI:表示事务的开始,在redis中执行这条语句以后,表示事务的开启,这个时候,所输入的命令并不会立马执行下去,相反,在未出现EXEC特殊字符时候,所有命令的执行都会进入一个队列中。

  • EXEC:表示对进入到队列的语句进行一个执行操作,执行的是先进先出的原则。

  • WATCH:表示监听,可以监听一个或多个健,是一个乐观锁,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令

  • DISCARD:表示清空事务队列,前面我们提到了事务在未被执行的过程中,都会进入到一个队列中,此条操作就会情况事务队列,并放弃执行事务。

> multi 
OK
> SET NAME REDIS THEORY
QUEUED
> SET author San
QUEUED
> exec
1) OK
2) OK

分布式缓存系统Redis原理解析

如上图所示,输入MULTI命令,输入的命令都会依次进入命令队列中,但不会执行。直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。命令队列的过程中可以使用命令DISCARD来放弃队列运行。

2.4.2 Redis事务与ACID
事务的ACID指的是原子性、一致性、隔离性和持久性
  • 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  • 一致性(Consistency):事务前后数据的完整性必须保持一致。

  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行

  • 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

在开发Redis时选用更加简单和快速的方法,不支持事务回滚功能,所以在Redis中事务失败的时候不进行回滚,而是继续执行余下的命令。所以如果一个事务中的命令出现错误,所有的命令都不会执行,但是如果出现运行错误,正确的命令会被执行。
  • Redis事务没有隔离级别概念:所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行

  • Redis是单线程执行,当前事务的执行不会被其它事务干扰,满足事务的隔离性

  • Redis事务单条指令保持原子性,但是事务不保持原子性。虽然Redis不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误

因此在Redis中事务总能保证ACID中的一致性和隔离性

2.5 Redis内存管理

Redis作为内存数据库,时常会存储大量的数据,即使采取了集群部署来动态扩容,也应该及时的整理内存,维持系统性能。

2.5.1 为数据设置超时时间

Redis所有数据结构都可以设置过期时间,时间一到就会自动删除。如果没有设置时间,那缓存就是永不过期;如果设置了过期时间,之后又想让缓存永不过期使用persist key

//设置过期时间
expire key time(以秒为单位)--这是最常用的方式
setex(String key, int seconds, String value) --字符串独有的方式

注:除了字符串独有设置过期时间的方法外,其他方法都需要依靠 expire 方法来设置时间

1)过期的key集合

Redis会将每个设置了过期时间的Key放入到一个独立的字典中,之后会定时遍历这个字典来删除到期的key。除了定时遍历,还会使用惰性策略来删除过期的Key,所谓的惰性策略就是在客户端访问这个key的时候,Redis会对key进行检查,如果过期了就删除。

2)定时扫描策略

Redis默认每秒进行10次过期key的扫描,每次扫描不会遍历字典中的所有key,而是按照如下策略:
  • 从过期字典中随机选择20个key;

  • 删除这20个key中已经过期的数据

  • 如果过期的key的比重超过1/4则重复步骤a)

另外,为了保证过期扫描不会出现过度循环,导致线程卡死,算法增加了扫描时间上限,默认是25ms。当出现大量的key设置相同的过期时间的时候,则会出现连续扫描导致读写请求出现明显的卡顿。因此,对于一些活动系统中可能会出现大量数据过期的,应为key的过期时间设置一个随机数,不能在同一时间内过期。

3)从库的过期策略

从库不会进行过期扫描,从库对过期的处理是被动的。主库在key到期的时候,会在AOF文件中增加一条del指令,这条指令同步到所有的从库后,从库通过执行这条del指令来删除过期的key。当然,从库同步的过程是异步的,会出现主库已经删除而从库中数据尚存在的情况,出现主从数据不一致。

2.5.2 内存淘汰策略

定时删除能够释放内存,却非常消耗CPU,尤其是在大并发的情况下。配合惰性删除,在访问某个key时检查是否过期,能够及时的释放内存。但是根据定时删除的策略会存在没有及时的删除key,而这些key又没有被及时的访问到,也就是惰性删除也没有生效,导致Redis的内存越来越高。因此,需要内存的淘汰策略,及时的释放内存

在Redis中提供了配置参数maxmemory来限制内存超出期望大小,当超出时根据不同的策略释放空间继续提供服务:
  1. noeviction:不会继续服务写请求 (DEL请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。

  2. volatile-lru:尝试淘汰设置了过期时间的key,根据LRU算法最少使用的key优先被淘汰。没有设置过期时间的key不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。

  3. volatile-ttl:淘汰的策略为key的剩余寿命ttl的值,ttl越小越优先被淘汰。

  4. volatile-random:淘汰的key是过期key集合中随机的key。

  5. allkeys-lru:区别于volatile-lru,淘汰的key对象是全体的key集合,而不只是过期的key集合。这意味着没有设置过期时间的key也会被淘汰。

  6. allkeys-random:所有的key随机删除

2.6 Redis缓存雪崩、击穿和穿透
2.6.1 缓存雪崩

1)现象:Redis中的数据设置了过期时间,当缓存的数据过期后,缓存同一时间大面积的失效,导致用户访问的数据不在缓存中前端的请求都落到后端的数据库,造成数据库短时间内承受大量的请求而奔溃,这就是缓存雪崩的问题。

2)解决方法:缓存雪崩是因为访问的数据不在缓存中,因此要避免短时间内所有的key都失效,有以下方法
  • 将缓存数据的过期时间设置为随机,防止同一时间出现大量数据过期

  • 使用互斥锁进行排队和限流,通过加锁或者队列来控制读数据库写缓存的线程数量,但是这样会影响吞吐

  • 数据预热,通过缓存reload机制,预选去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀

2.6.2 缓存击穿

1)现象:当缓存中的热点数据过期后,如果有大量的请求访问该热点数据,此时客户端发现环境数据过期然后从后端数据库访问数据,数据库由于高并发的访问请求奔溃。

2)解决方法:
  • 互斥锁方案:对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询

  • 设置热点数据不过期:将热点数据一直保留在缓存中不过期

2.6.3 缓存穿透

1)现象:当业务访问的数据不存在,既不在缓存中也不在数据库中,导致请求访问缓存时发现数据不存在再去访问数据库,数据库中也没有想要的数据,无法构建缓存。每次查询都要访问后端数据库层,失去了缓存的意义,也就是缓存穿透的问题。

2)解决方法:
  • 接口层增加校验:当有大量恶意请求访问不存在的数据时,也会发送缓存穿透。因此在接口处判断请求的参数是否合理、是否含有非法值,如果不合法直接返回

  • 缓存空值或默认值:当业务访问的数据不存在时,可以针对查询的数据在缓存中设置一个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值,返回给应用,而不会继续查询数据库

  • 使用布隆过滤器在数据写入数据库时做个标记,业务线程确认缓存失效后,可以通过查询布隆过滤器快速判断数据是否存在,如果不存在,则不需要访问数据库。

3、Redis集群架构

3.1 主从模式
主从复制模式包括一个Master与一个或多个Slave,客户端对主节点进行读写操作,对从节点进行读操作,主节点写入的数据会实时的同步到从节点

分布式缓存系统Redis原理解析

3.1.1 主从复制模式工作机制

分布式缓存系统Redis原理解析

主从复制模式包括快照同步和增量同步两个过程,具体工作机制如下:
  1. slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照,并使用缓冲区记录保存快照这段时间内执行的写命令

  2. master将保存的快照文件发送给slave,并继续记录执行的写命令。

  3. slave接收到快照文件后,加载快照文件,载入数据

  4. master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化。

  5. 此后mster每次执行一个写命令都会同步发送给slave完成增量同步,保持master与slave之间数据的一致性。

3.1.2 主从模式优缺点
1)主从复制的优点
  • master能自动将数据同步到slave,可以进行读写分离,分担master的读压力

  • master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求

2)主从复制的缺点
  • 不具备自动容错与恢复功能,master或slave的宕机都可能导致客户端请求失败,需要等待机器重启或手动切换客户端IP才能恢复

  • master宕机,如果宕机前数据没有同步完,切换到slave后会存在数据不一致的问题。

  • 难以支持在线扩容,redis的容量受限于单机配置

  • 快照同步非常的消耗资源,需要在master节点将内存数据快照到磁盘文件中,再将文件传送到slave节点加载,整个过程费时并且资源开销大

  • 同步过程中内存中保存增量的修改记录指令的buffer是有限的,可能会出现写入的指令过多而未及时同步到slave节点的情况发生

3.2 哨兵sentinel模式

主从复制不具备自动恢复能力,当发生故障时需要手动进行主从切换。为此引入了哨兵模式,当发生故障时可以自动进行主从切换。

分布式缓存系统Redis原理解析

3.2.1 哨兵主要功能
Redis Sentinel类似于Zookeeper集群,由3~5节点组成,负责监控主从节点的健康状态,一旦发现问题能够及时处理。主要功能如下:
  1. 监控master和slave运行是否正常

  2. 当master出现故障时,能够自动切换到slave节点

  3. 多个哨兵可以监控同一个redis,哨兵之间也会自动监控

客户端连接Redis集群时,会首先连接sentinel,通过sentinel来查询主节点的地址,然后再去连接主节点进行数据交互。当主节点发生故障时,客户端会重新向sentinel要地址,sentinel会将最新的主节点地址告诉客户端。如此应用程序将无需重启即可自动完成节点切换。

3.2.2 哨兵工作机制
哨兵与master建立连接后,会执行三个操作:
  • 定期向master和slave发送INFO命令。(注:一般10s一次,当master被标注为主动下线时,改为1s一次)。

    • 通过INFO命令,哨兵可以获取主从数据库的最新信息,并进行相应的操作,比如角色变更等

  • 定期向master和slave的_sentinel_:hello频道发送自己的信息

    • 其他哨兵可以通过该信息判断发送者是否是新发现的哨兵,如果是的话会创建一个到该哨兵的连接用于发送PING命令。

    • 其他哨兵通过该信息可以判断master的版本,如果该版本高于直接记录的版本,将会更新。

    • 当实现了自动发现slave和其他哨兵节点后,哨兵就可以通过定期发送PING命令定时监控这些数据库和节点有没有停止服务。

  • 定期(1s一次)向master、slave和其他哨兵发送PING命令

    • PING某个节点超时后,哨兵认为其主观下线

    • 如果下线的是master,哨兵会向其它哨兵发送命令询问是否认为该master主观下线sdown。

    • 如果达到一定数目的投票,则会认为该master已经客观下线odown,并选举领头的哨兵对主从节点发起故障恢复

    • 如果没有足够的哨兵同意master下线,则客观下线状态会被解除;如果master此时回复了哨兵的PING请求,则主观下线状态也会被解除

3.2.3 哨兵模式故障恢复

以下图中master节点异常,原先的主从复制断开,slave节点被提升为新的master节点,其它slave节点和新的master节点建立复制关系。客户端通过新的master节点进行交互。

分布式缓存系统Redis原理解析

1)哨兵领头羊选举,采用Raft算法
  • 发现master下线的哨兵节点A向每个哨兵发送命令,要求对方选自己为领头哨兵。

  • 如果目标哨兵节点没有选其他人,则会同意选举A为领头哨兵。

  • 如果有超过一半的哨兵同意选举A为领头,则A当选。

  • 如果有多个哨兵节点同时参选领头,此时有可能存在一轮投票无竞选者胜出,此时每个参选的节点等待一个随机时间后再次发起参选请求,进行下一轮投票竞选,直至选举出领头哨兵。

2)哨兵对系统进行故障恢复
  • 从所有在线的slave中选择优先级最高的,优先级可以通过slave-priority配置。

  • 如果有多个最高优先级的slave,则选取复制偏移量最大(即复制越完整)的当选。

  • 如果以上条件都一样,选取id最小的slave。

  • 选举完成后,领头的哨兵将选举出来的slave升级为master,然后向其它slave发送命令接受新的master,最后更新数据

  • 旧的master节点变为slave节点,恢复服务后以slave身份继续运行

3.2.4 哨兵模式优缺点
1)哨兵模式的优点
  • 基于主从复制实现,继承了主从复制的优点

  • 哨兵模式下,master节点异常后可以进行自动切换,系统可用性更高

2)哨兵模式的缺点
  • master宕机,如果宕机前数据没有同步完,切换到slave后会存在数据不一致的问题。

  • 难以支持在线扩容,redis的容量受限于单机配置

  • 哨兵模式下slave节点不提供服务

  • 需要额外的资源来启动sentinel进程,实现相对复杂一点

3.3 Cluster集群模式

哨兵模式依然存在难以在线扩容的问题,为此引入了Cluster集群模式。Cluster模式采用去中心化的结构,实现Redis的分布式存储,每台节点存储不同的内容,解决在线扩容的问题。

分布式缓存系统Redis原理解析

3.3.1 Cluster模式工作机制
1)Cluster采用无中心结构,它的特点如下:
  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。

  • 节点的fail是通过集群中超过半数的节点检测失效时才生效。

  • 客户端与redis节点直连,不需要中间代理层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

2)Cluster模式的具体工作机制:
  • 在redis的每个节点上,都有一个插槽(slot),取值范围0-16383。

  • 当我们存取key的时候,redis会根据CRC32的算法得出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,通过这个值,去找到对面的插槽所对应的节点,然后直接自动跳转到这个插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

  • 为了保证高可用,Cluster模式也引入主从复制模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。

  • 当其他主节点PING一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点都宕机了,那么该集群就无法再提供服务了。

  • Cluster模式集群节点最小配置6个节点(3主3从,因为需要半数以上),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

3.3.2 Cluster模式优缺点
1)Cluster模式优点
  • 无中心架构,数据按照slot分布在多个节点

  • 集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。

  • 可线性扩展到1000多个节点,节点可动态添加或删除。

  • 能够实现自动故障转移,节点之间通过gossip协议交换状态信息,用投票机制完成slave到master的角色转换。

2)Cluster模式缺点
  • 客户端实现复杂,驱动要求实现Smart Client,缓存slot mapping信息并及时更新,提高了开发难度,目前仅JedisCluster相对成熟,异常处理还不完善,比如常见的“max redirect exception”。

  • 节点会因为某些原因发生阻塞(阻塞时间大于cluster-node-timeout)被判断下线,这种failover是没有必要的。

  • 数据通过异步复制,不保证数据的强一致性。

  • slave充当“冷备”,不能缓解读压力。

  • 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好。

  • key事务操作支持有线,只支持多key在同一节点的事务操作,多key分布不同节点时无法使用事务功能。

  • 不支持多数据库空间,单机redis可以支持16个db,集群模式下只能使用一个,即db 0。

4、总结

本文首先介绍了Redis的特性、基本操作的数据类型以及适用的场景;接下来对Redis核心的原理进行解析,包括IO模型、内存数据的持久化机制、Redis事务以及内存管理等,了解Redis底层的设计理念以及实现机制;最后对于单机的Redis实例扩展到集群架构,介绍了主从复制、哨兵模式和cluster集群三种架构,以及各自的优缺点对比。Redis作为内存数据库,可以认为是分布式缓存系统,也可以作为NoSQL数据库,适用不同的场景。


参考资料:

  1. 《Redis深度历险:核心原理和应用实践》,钱文品著

  2. 《Redis设计与实现》,黄建宏著

  3. https://blog.csdn.net/annita2019/article/details/107472315

  4. https://blog.csdn.net/weixin_39327556/article/details/124786247

  5. https://www.runoob.com/redis/redis-intro.html

  6. https://blog.csdn.net/realYuzhou/article/details/108963230

原文始发于微信公众号(牧羊人的方向):分布式缓存系统Redis原理解析

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/64930.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!