这里写目录标题
Redis 2
1、底层数据结构
1.1、SDS 简单字符串
1.2、跳表
2、高级功能
2.1、RDB
RDB概述
RDB 可以将 Redis 在内存中的数据库状态保存到磁盘里面,避免数据丢失,具体来说就是将某个时间点上的数据库状态保存到一个 RDB文件中,RDB 文件是一个经过压缩的二进制文件,我们通过这个文件可以还原生成 RDB 文件时的数据库的状态,而且这个还原是服务器启动的时候自动执行的,只要 Redis 服务器在启动的时候检测到 RDB 文件的存在,就会自动载入 RDB 文件
RDB 持久化可以手动执行也可以通过配置定期执行,手动执行的话有两个命令:
- save:SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
- bgsave:BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程继续处理命令请求
bgsave 对 Redis 也是有一定影响的,bgsave 期间,客户端发送的 save 命令和 bgsave 命令会被拒绝;如果 bgsave 正在执行,客户端发送 bgrewriteaof 命令需要等到 bgsave 执行完毕后才会执行;如果 bgrewriteaof 正在执行,客户端发送 bgsave 命令会被拒绝
bgsave 配置了三个定期执行参数:
- save 900 1:服务器在900秒之内,对数据库进行了至少1次修改
- save 300 10:服务器在300秒之内,对数据库进行了至少10次修改
- save 60 10000:服务器在60秒之内,对数据库进行了至少10000次修改
bgsave实现原理
bgsave 主要是通过 Redis 中的 redisServer 类来实现的,redisServer 对象中维护了一个 saveparams 数组,这个数组的每一个元素都是一个 saveparam 对象,这个 saveparam 对象有两个属性,一个是秒数,一个是修改数,这个对象就是用来保存 bgsave 的配置参数的;
class redisServer {
saveparam[] saveparams; //参数配置数组
long long dirty; //修改计数器
Date lastsave; //上一次执行bgsave的时间
}
redisServer 中还维护了两个属性,一个是修改计数器,就是说在上一次 bgsave 之后,进行的修改次数;另外一个属性是上一次bgsave的时间,通过这两个属性跟 saveparams 数组中保存的配置参数进行对比判断是否执行 bgsave
RDB 文件结构
2.2、AOF
AOF概述
AOF 能够减少服务器异常状态而导致的数据丢失,举个比较极端的例子,60S 内对 Redis 修改了10000次,即将触发 RDB,这个时候服务器突然挂了,那么这个时候这10000次的修改就丢失了,而 AOF 能够最大限度的降低对数据的损失
AOF 持久化是通过保存 Redis 服务器所执行的写命令来记录数据库状态的,这些命令是可以我们可以直接读懂的,但这同样带来一个弊端,就是,我们能读懂的东西 Redis 是读不懂的,所在在读取 AOF 文件还原数据库的时候,Redis 需要创建一个假客户端来把这些命令执行一遍
AOF 也是依托于 Redis 中的 redisServer 类来实现的,redisServer 类中维护了一个 sds 类型的属性叫做 AOF 的缓冲区,当服务器在执行完一个写命令后,会将被执行的写命令追加 redisServer 对象的缓冲区的末尾
AOF 持久化功能的实现可以分为:命令追加、文件写入、文件同步三个步骤
- 命令追加:当 AOF 持久化功能处于打开状态时,服务器在执行完一个写命令后,会将被执行的写命令追加 redisServer 对象的缓冲区的末尾
- 文件写入、文件同步:服务器每次结束一个事件循环之前,它都会进行文件的写入,文件的写入行为由服务器配置的 appendfsync 选项来决定
appendfsync选项配置
appendfsync选项有三种配置:
- always: 服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,所以always的效率是最慢的;但always也是最安全的,因为即使出现故障,只会丢失一个事件循环中所产生的命令数据
- everysec: 服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,并且每隔一秒就要在子线程中对AOF文件进行一次同步。everysec模式足够快,并且就算出现故障停机,也只丢失一秒钟的命令数据
- no:服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,至于何时对AOF文件进行同步,则由操作系统控制
默认配置是 everysec
AOF写入流程与读取流程
AOF写入流程:
- 客户端发起命令操作(查询操作除外)
- 服务端接收命令并将我们的命令进行执行,与此同时,将命令写入 redisServer 对象中的 aof 缓冲区
- 通过我们的 appendfsync 参数的配置进行我们的缓冲区同步到aof磁盘文件的操作
AOF读取流程:
- 创建一个不带网络连接的伪客户端
- 从AOF文件中分析并读取出一条写命令
- 使用伪客户端执行被读出的写命令
- 一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止
AOF重写
2.3、Redis 文件事件
多个客户端连接到 Redis 服务器接收请求的模块上面,然后将这些事件分发给 reactor 反应器,然后分发给不同的 handler 进行处理
2.4、主从模式
主从复制概述
在 Redis 里面,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制另一个服务器,被复制的叫主服务器,对主服务器进行辅助的服务器叫做从服务器
当客户端向从服务器发送SLAVEOF命令,要求从服务器复制主服务器时,从服务器首先需要执行同步操作,也就是将从服务器的数据库状态更新至主服务器当前所处的数据库状态
Redis的复制功能分为同步(sync)和命令传播(command propagate)两个操作:
- 同步:
- 第一次进行主从服务器连接的时候:一定会进行同步操作(主服务器-RDB文件-从服务器)
- 从服务器断线重连的时候:如果断线期间,新的命令都存在主服务器的缓冲区中,就直接发送这部分命令给从服务器就好了;如果新的命令不全部存在于主服务器的缓冲区,则触发同步操作(RDB文件)
- 命令传播:第一次链接完我们的主服务器后,通过RDB文件同步之后的所有的命令,都是采用命令传播的形式;断线重连之后的所有主从数据的同步都采用命令传播
完整重同步
完整重同步其实就是 RDB 文件同步(1、第一次连接;2、断线重连有可能触发 RDB 文件同步)
完整重同步需要主服务器生成一个新的 RDB 文件,这个 RDB 文件时整个 Redis 的数据状态,整个状态的 RDB 文件体积不会太小,传输给从服务器需要网络开销和一定的时间,相比于小体积的命令传播,这个RDB 文件开销大得多
发 PSYNC 有两种情况
- 完整重同步
- 部分重同步
部分重同步
部分重同步是用来处理断线后重复制情况的,当丛服务器在断线后重新连接主服务器的时候,如果条件允许,主服务器可以将主从断开期间内执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新到主服务器的状态
部分重同步功能由三个部分构成:
- 主服务器的复制偏移量和丛服务器的复制偏移量
- 主服务器的复制积压缓冲区
- 服务器的运行ID
偏移量:
主服务器每次向从服务器传播N个字节的命令时就会在自己的偏移量上加N,从服务器每次收到主服务器传播来的N个字节的数据后,就会将自己的复制偏移量加N
复制积压缓冲区:
主服务器进行命令传播的时候,不仅会将写命令发送给从服务器,还会将命令写到复制积压缓冲区中,这个复制积压缓冲区是在主服务器上维护的一个固定长度的先进先出的队列,默认长度是 1MB;
在从服务器断线时,所有断线期间的命令都记录在积压缓冲区中,如果断线的时间不长,写命令都在积压缓冲区中,这个时候就进行部分重同步;如果断线时间过长,积压缓冲区被写满,新命令把老命令顶出去了,就需要 bgsave 发 RDB 文件进行完成重同步
服务器运行ID:
每个 Redis 服务器都有自己的运行 ID,运行 ID 是在启动的时候自动生成的,当从服务器对主服务器进行初次复制时,主服务器会将自己的运行 ID 传送给从服务器,从服务器会将这个运行 ID 保存起来
从服务器断线重连上一个主服务器时,从服务器将向当前联连接的主服务器发送之前保存的运行 ID,如果 ID 一致,主服务器可以从尝试部分重同步;如果不一致,说明之前从服务器连的不是这个主服务器,主服务器必须对从服务器进行完整重同步
PSYNC原理
主从复制流程
心跳监测
2.5、哨兵模式
哨兵概述
哨兵模式是 Redis 的高可用性解决方案,一个或多个哨兵可以监视主服务器和这个服务器下的从服务器,当被监视的主服务下线了,可以自动将下线主服务器的某个从服务器升级为新的主服务器继续处理命令请求
如果主服务器下线了,一个从服务器被成为了新的主服务器,这个时候,因为主服务器ID发生了变化,所以剩余的从服务器都需要做一次完整重同步,那么这个时候其实是要进行一次 bgsave 的;当原来的主服务器作为从服务器重新上线后,也需要做一次完整重同步,还要做一次 bgsave
所以说主服务器下线带来的开销非常的大,会造成服务短时间的不可用和两次服务卡顿;如果从服务器下线重连,其实问题就不是很大,就是断线重连那一套,部分重同步就好了
主观下线和客观下线
Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例发送 PING 命令,并通过实例返回的 PONG 命令回复来判断实例是否在线
Sentinel 配置文件中存在一个配置选项,这个配置选项代表 Sentinel 判断实例进入主观下线所需要的时间长度,如果一个实例在这个事件内连续向 Sentinel 返回无效回复,就会被 Sentinel 判断为主观下线
当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel进行询问,当认为主服务器已经进入下线状态的Sentinel的数量,超过Sentinel配置中设置的quorum参数的值,那么该Sentinel就会认为主服务器已经进入客观下线状态