7000字长篇,以问题为导向逐步理解Redis持久化,主从复制,哨兵机制

问题导论

❓问题一:Redis的数据保存在内存中,当服务器宕机重启后,内存中的数据就会丢失。

  • 解决方法:将内存中的数据持久化保存到硬盘上,数据丢失后再进行恢复。

❓问题二:1. 服务器发生了宕机,进行数据恢复也需要时间,那么这段时间就无法处理请求。2. 硬盘中的数据也有可能丢失。

  • 解决方法:将数据存储到多台服务器中,注意,这里需要的是多台服务器保存同一份数据,这样一台数据宕机了,其他服务器也可以提供服务。一台服务器硬盘中数据丢失了,数据也会存在于另外几台服务器中。

❓问题三:服务器中的数据是经常变动的,怎么保持这些服务器之间的数据是一致的。

  • 解决方法:主从复制。

❓问题四:主从复制通常是有一个主服务器和多个从服务器,从服务器进行读操作,主服务器负责读写操作,然后将写操作同步给从服务器。 所以,从服务器出现故障了没多大问题,但主服务器出现故障就无法处理写请求了。这个时候就需要人工进行一系列工作更换主服务器,就很麻烦。

  • 解决办法:使用哨兵机制自动更换主服务器。

下面我们按照层次分别介绍这些技术。

Redis持久化

持久化方式简介

  • 进行数据备份保存通常有三种方式
    1. 保存操作命令,需要恢复的时候执行再执行一遍命令即可——AOF(Append Only File)日志
    2. 直接保存服务器中的数据,需要恢复的时候直接将数据拷贝过去即可——RDB(Redis DataBase)快照(Redis默认)
    3. 混合持久化,集成AOF日志和RDB快照的优点。

AOF日志

  • AOF日志中记录的是数据操作命令,具体来说是写操作命令,因为读不影响数据,没必要保存。也就是当开启AOF日志后,执行写操作的时候,要进行两步操作
    1. 执行该命令,修改数据。
    2. 将命令保存追加保存到AOF日志。
  • 开启AOF日志:在redis.conf配置文件中修改
appendonly yes  //默认为no
appendfilenname "appendonly.aof"  //AOF日志文件名称

AOF问题导论

❓ 问题1:多步操作需要考虑执行顺序问题,即先执行后存储,还是先存储后执行。

  • 回答
    1. 先存储后执行: 会出现以下问题
      1. 如果执行的时候发现该命令有问题,那么我们就会存储到一个错误的命令。
      2. Redis中存储和执行都是在主进程中进行,存储命令可能会阻塞写操作的执行。而将命令写到硬盘中时,如果服务器硬盘I/O压力太大,写回硬盘可能会非常慢,阻塞问题就会很严重。
    2. 先执行后存储: 也会出现两种问题:
      1. 如果执行后,命令还没来得及写入磁盘,服务器宕机了,数据就有丢失的风险。
      2. 存储操作虽然不会阻塞当前写操作,但可能给下一个命令造成阻塞。
    3. AOF采取先执行后存储策略。

❓问题2:从问题1可以知道,不管那种策略都会出现阻塞问题。

  • 解决方案:添加一个缓冲区,将命令先写到缓冲区中,然后批量写回磁盘中,降低I/O次数。于是根据写回的时机就分为三种写回策略。但同时,缓冲区在内存中,服务器宕机缓冲区数据也会丢失,所以就要根据实际情况来进行取舍。

❓问题3:持久化是为了备份保存内存中的数据,当内存中的数据意外丢失后,使用备份数据进行恢复。如果AOF日志文件过大,那么恢复起来也会慢,同时文件过大也会占用磁盘空间。

  • 解决方案:AOF重写机制,其中过期的无用的内容删掉,比如对同一内容执行了两次修改操作,那么第一次修改命令就无用了。比如,先添加后修改再删除,那么前面的添加和修改都没用了。

三种写回策略

  • 加入缓冲后,AOF执行流程

    1. 执行完命令,将命令追加到server.aof_buf缓冲区中。
    2. 通过系统调用,将缓冲区的数据拷贝到内核缓冲区
    3. 将内核缓冲区的数据写入硬盘中。
      1. Redis控制第三步写入磁盘时机,调用fsync命令。
      2. 操作系统自己决定写入时机。
  • 三种写回策略:

    • Always:总是,即每次写操作命令执行后,立即执行fsync命令将数据写回磁盘日志。
      • 优点:最大程度保持数据不丢失,可靠性最高
      • 缺点:I/O频繁,阻塞问题最严重,影响主进程性能
    • No:执行完命令后将命令存储到内核缓冲区就结束了,操作系统后续自己决定什么时候写入磁盘。
      • 优点:三种策略中性能最好
      • 缺点:操作系统写入磁盘时机不确定,可能会丢失数据,可靠性最低。
    • Everysec:每秒(注意,Redis每秒可以执行10万左右请求),执行完命令后将命令存储到缓冲区,每隔一秒执行fsync命令将缓冲区的内容写入磁盘。
      • 折中策略。 性能折中,宕机最多会丢失1秒的数据

AOF重写机制。

❓问题1:什么时候进行AOF重写。

  • 解决方案:AOF文件大小有个阈值,超过阈值了就会触发AOF重写机制。

❓问题2:AOF重写是为了减小AOF日志文件大小,删除其中的无用过时内容。怎么实现?

  • 解决方案:读取Redis数据库中所有的键值对,对每个数据模拟一条添加命令,将该命令添加到新的AOF文件中。

❓问题3:AOF重写失败了怎么办?

  • 解决方案:不复用现有的AOF文件,先写到新的AOF文件,写完之后使用新的文件覆盖掉原来的AOF文件。这样即使失败了,对原来文件也没影响。

❓问题4:重写要读取Redis中所有数据,然后将其写入新的磁盘文件中,这如果也在主进程中完成那么无疑会极大的影响性能。

  • 解决方案:将重写过程交由后台子进程bgrewriteaof处理。即后台重写。这样在AOF重写阶段,主进程可以继续处理命令请求,从而避免阻塞主进程。

❓问题5:这里出现了多进程,但是通常会说Redis是单线程的,这两点不矛盾吗?

  • 回答:
    1. 进程与线程的区别:进程中包含了线程,一个进程可以有多个并行的线程;线程是属于进程,一个线程指的是进程中一个单一顺序的控制流。
    2. 通常说的Redis是单线程的是指 处理和响应客户端请求是单线程的,Redis其实还有其它的后台线程来完成其他任务,如上述的后台重写。

❓问题6:为什么是父子进程而不是多线程?

  • 回答:多线程之间会共享内存,修改共享内存数据的时候需要加锁。而父子进程是以只读的方式共享内存数据的。即创建bgrewriteaof子进程的时候,操作系统会把父进程(主进程)的页表复制一份给子进程,页表里面存储的是虚拟地址,即实际物理地址的映射。这样父子进程各拥有一个虚拟地址,都可以读取内存数据,两者互不影响,就不需要加锁。

❓问题7:父子进程以只读形式共享内存数据,子进程是为了读取数据然后写入新的AOF日志文件,其不需要对数据进行写操作,但父进程需要写操作,这个时候怎么办?

  • 解决方案:触发写保护中断,添加数据直接分配物理内存空间, 而修改操作会对原有物理内存进行复制,重新设置映射关系,之后修改父进程的读写权限为可读写,最后对内存进行操作。修改的复制物理内存操作被称为写时复制。即写的时候才复制。触发写时复制或添加数据是父进程的事情。

❓问题8:触发写时复制或添加数据是父进程的事情,那么就会出现父子进程数据不一致情况。

  • 解决方案:会增加一个缓冲区,除了原有的AOF缓冲区,增加AOF重写缓冲区。即在执行AOF重写期间,父进程写操作命令会多执行一次操作
    1. 执行命令
    2. 将命令追加到AOF缓冲区
    3. 将命令追加到AOF追加缓冲区。

❓问题9:AOF重写缓冲区数据什么写入新的AOF日志文件中?

  • 回答:当子进程完成重写工作后,会向父进程(主进程)发送一个信号,这个时候,主进程会将AOF追加缓冲区内容追加到新的AOF文件中,然后覆盖掉旧的AOF文件,完成后台复制。

❓问题10:AOF后台复制使用子进程进行操作,是为了避免阻塞主进程,但真的能一点不影响吗?

  • 回答:还是会有影响,在以下阶段会阻塞主进程,但这与直接在主进程进行AOF重写相比影响小很多。
    1. 创建子进程,复制虚拟内存的时候,虽然通常复制虚拟内存比较快。但当页表越大的时候,阻塞时间也会越长。
    2. 写时复制时,没有AOF后台复制前,主进程直接修改内容就好,而加了后台复制后修改内容时需要复制物理内存,对主进程性能产生影响。复制内存越大,阻塞时间越长
    3. 子进程完成AOF重写后,将AOF追加缓冲区内容写入新的AOF文件中也会发生阻塞

RDB快照

  • 快照备份的是Redis 数据库数据,快照的含义类似于拍照,将某一瞬间的所有信息记录下来。即每一次RDB快照是将数据库中的所有数据全都复制到磁盘中,即全量快照

❓问题1:RDB快照每次复制所有数据,如果这个工作交由主进程进行会阻塞主进程。

  • 解决方案:提供两个命令生成RDB快照文件。save(由主进程执行),bgsave(通过fork()创建一个子进程执行快照工作)

❓问题2:创建子进程会出现和AOF日志一样的问题的?

  • 回答:创建子进程也会复制父进程的虚拟空间,同时也会出现写时复制问题,但不会通过缓冲区解决数据不一致问题,而是交由下一次RDB快照操作。所以RDB快照同样也有创建子进程,写时复制的额外性能开销。

❓问题3:RDB快照期间的数据不一致不处理,交由下一次RDB快照处理,那么两次快照之间服务器宕机就会发生数据丢失,能解决吗?

  • 回答:不能,只能通过设置RDB快照的执行时机来尽可能减少影响。Redis可以通过配置文件的来实现每隔一段时间执行一次快照,通常可以设置为5分钟,但这样最多会丢失5分钟数据。Redis默认是RDB快照,其提供了一些默认值:
save 900 1  //900秒内,对数据库进行了至少一次修改
save 300 10 //300秒内,对数据库进行了至少10次修改
save 60 10000 //60秒内,至少进行了10000次修改。

❓问题4:执行RDB快照也会产生开销,同时其每一次复制所有数据显然会产生更大开销,那么为什么选择RDB作为默认持久化方式。

  • 回答:因为恢复速度快,RDB快照直接将数据从磁盘写入内存中即可,而AOF需要运行其中的命令

混合持久化模式

  • 混合持久化为Redis4.0新增的方式,继承了AOF和RBD的优点。其开启方式如下:
aof-use-rdb-preamble yes   //修改Redis配置文件

❓问题1:AOF和RDB各有什么优点。

  • 回答:AOF日志丢失数据少,设置为折中方案Everysec最多只会丢失一秒数据,而PDB快照丢失数据多,最多可丢失几分钟数据。RDB快照恢复速度快,RDB快照恢复只需要将磁盘数据写入内存中即可,而AOF日志需要执行命令。

❓问题2:混合持久化是怎么工作的

  • 回答:混合持久化以AOF日志模式工作,当AOF重写时,先创建子进程,复制虚拟内存共享数据,这个时候以RDB快照格式将数据复制到AOF文件中,复制完后AOF重写缓冲器的命令,会写入新的AOF文件中,最后用新的AOF文件覆盖旧的。可以看到重写后的AOF文件前半部分记录的是数据本身,后半部分记录的是命令。 这样在继承AOF日志丢失数据少的优点前提下,由于文件前半部分是数据本身,恢复速度也快,即也融合了RDB快照的优势

主从复制

问题导论中指出,当数据都存放在一个服务器上时,会出现以下风险。

  1. 服务器发生了宕机,在数据恢复期间,无法处理来自客户端的请求。
  2. 服务器硬盘发生了故障,可能数据就会丢失了。

解决上述问题可以采取多服务器策略,一个服务器故障,其他服务器也可以继续提供服务,同时增加了数据的安全性。要做到上述功能,那么需要多台服务器中数据是一样的,这样才能不影响服务。

❓问题1:怎么保持多台服务器的数据一致性呢?

  • 解决方法:主从复制,其思路这样的——采取读写分离策略,主服务器负责读写,从服务器负责读,然后主服务器将写操作同步给从服务器。整体运行流程如下:
    • 将B服务器设置为A服务器的从服务器。在B服务器中执行 replicaof <服务器A的IP地址> <A的Redis 端口号> 进行第一次同步(全量复制
      1. 建立链接、协商同步。B服务器发送psync <主服务器的唯一标识id,runID> <复制进度offset>,第一次同步为psync ? -1。 A发送响应命令,同时传输自己的run ID和offset参数。
      2. 主服务器A调用子进程进行RDB快照,生成RDB文件。传输给B服务器,B将数据载入内存中,然后回复一个确认消息给A。
        1. 问题:这一阶段中A依旧会执行写操作,就会造成数据不一致现象。
        2. 解决方案:将写操作命令写入replication buffer缓冲区中。
      3. A将缓冲区中存储的命令发送给B,解决数据不一致问题。
      4. 维持连接,A每次发生写操作都会将该命令传输给从服务器B。可以总结为基于长连接的命令传播

❓问题2:当主从服务器网络断开之后,重连还要进行全量复制吗?

  • 回答:这样开销非常大,这时候从数据库其实有一定数据,可以根据情况选择增量复制。即第一次复制,采用PDB进行全量复制,复制的是数据本身。后续通常会采取增量复制,这个时候复制的是命令。

❓问题3:那么怎么判断什么情况下进行增量复制呢?

  • 回答:通过上述的复制进度offest参数,主服务器进行命令传播的时候,不仅会将数据写入replication buffer中,还会将其写入一个环形缓冲区 repl-backlog_buffer 中。主服务器和从服务器都有一个进度参数offest。进行重连的时候。
    1. 从服务器B发送psync <主服务器的唯一标识id,runID> <复制进度offset>命令,主服务器A根据自己的offest和从服务器的offest进行判断。
      1. 如果从服务器断开期间的修改数据,即主从服务器的不一致数据还在环形缓冲区中,则进行增量复制。将这些增量的数据写入replication buffer,传输给从服务器B。
      2. 如果不在缓冲区了,则进行全量复制,生成PDB快照传输给B。除了这种情况还有replication buffer缓冲池数据溢出了,也会全量复制。

❓问题4:环形缓冲区默认大小为1M,这个大小足够吗?

  • 回答:这个大小通常是不够的,可以通过修改配置文件中的参数进行修改。
repl-backlog-size 10mb

❓问题5:怎么知道服务器是否正常工作?

  • 解决方案:通过发送命令的方式
    1. 主服务器默认每隔10秒发送ping命令,判断从服务器的状态并检测网络连接。
    2. 从服务器每隔1秒发送reolconf ack <offest 命令,告知自己的复制进度,检查复制数据是否丢失,并检测网络连接。

❓问题5:主服务器干的事情好多,除了正常的读写操作,在第一次连接从服务器的时候还要生成pdb快照文件,传输pdb,过程中复制增量命令到从服务器。而每增加一台从服务器,都要进行上述复杂操作。

  • 解决方案:设置从服务器的从服务器,这样主服务器连接的从服务器数量就会变少,减少了主服务器的压力。设置方法如下:
replicaof <从服务器的从服务器IP> 6379  //在从服务器上执行 6379为Redis端口号

❓问题6:主从服务器数据一定可以保持一致吗?

  • 回答:不一定,因为其是异步传输数据,所以无法实现强一致性。同时,命令传输给从服务器,从服务器执行这个过程也需要时间,这个阶段数据也会不一致。
  • 缓解方案:不能彻底解决,只能缓解。
    1. 减少传输时间——尽量保证主从服务器网络连接状态良好。
    2. 减低不一致的影响——开发一个监控程序,通过INFO replication查询主从服务器复制进度,当差值大于某一阈值,我们禁止客户端从这个从服务器中读取数据。

❓问题7:上述提到了两种缓冲区,一个replication buffer, 一个repl_backlog buffer缓冲区,这两个有什么区别?

  • 回答:
    1. 作用不同
      1. replication buffer,在全量复制和增量复制时都会出现,且主服务器给每个连接的从服务器都会分配一个replication buffer。
      2. repl_backlog buffer,在增量复制阶段出现,一个主服务器只有一个repl_backlog buffer。
    2. 影响不同
      1. replication buffer中的数据都是从服务器需要的,其满了,会造成无法通过增量复制解决主从服务器数据不一致问题,会断开连接,删除缓存,重新进行全量复制。
      2. repl_backlog buffer满了,会覆盖起始位置数据,其是通过主从服务器复制进度offest来判断数据是否还在环形缓冲区,不在了进行全量复制。

哨兵机制

❓问题1:在主从复制模式下,主服务器作用很大,一旦主服务器挂了,就无法向客户端提供写服务了,这个时候就需要人工更换主服务器,让其他服务器指向新的主服务器,同时通知客户端,很麻烦?

  • 解决方案:哨兵机制实现主从节点故障转移。将服务器集群中的某些节点设置为哨兵节点。设置方式如下:
sentinel monitor <master-name> <master-ip> <master-prot> <quorum> 
// quorum是用于下面介绍的投票的阈值。

❓问题2:哨兵节点需要进行哪些操作?

  • 回答:
    1. 监控:判断主从节点是否发生故障了,它不仅会监控主节点,也会监控从节点。方便从好的从节点中选出新的主节点。
    2. 选主:选择新的主节点,进行主节点切换。
    3. 通知:将主节点的信息通知给从节点和客户端。

❓问题3:哨兵时如何知道主从节点的信息的,又如何监控?

  • 回答:
    1. 获取信息: 在设置该节点为哨兵节点的时候会输入主节点信息。哨兵会每10秒向主节点发送一次INFO命令,获取所有从节点的信息。
    2. 监控: 哨兵节点每隔一秒给所有主从节点发送Ping命令,如果在规定时间接受的节点没有进行响应,就判断其主观下线,这个规定时间同样在配置文件中配置。

❓问题4:一个哨兵节点就可以吗?有主观下线是否有客观下线。

  • 回答:只有一个哨兵节点有两个问题。1. 哨兵节点故障了哨兵机制就无法起作用了。2. 一个哨兵节点的判断比较主观,可能会出现误判,例如由于哨兵节点网络不稳定,主节点系统压力大等原因,没及时处理某个命令。所以哨兵在部署的时候不会只部署一个节点,而是多个节点部署成哨兵集群,保证哨兵机制的稳定性,降低误判率。 多个节点做出的下线判断叫客观下线。同时,只有主节点有客观下线,从节点只有主观下线。

❓问题5:怎么判定主节点为客观下线呢?

  • 回答:
    1. 当一个哨兵判断主节点主观下线后,就会向其他哨兵发出命令,其他哨兵受到命令后,会根据自己的主观下线判断(自己与主节点的连接状态),做出赞成或者拒绝的响应。
    2. 当赞成票达到设定值quorum时,通常是哨兵数量的二分之一+1(3个哨兵,设定值为2)。这时这个哨兵节点就会判定主节点客观下线。这个时候就要进行主节点的切换了。

❓问题6:多哨兵的情况下,由谁来完成后两步选主和通知工作。

  • 回答:
    1. 做出客观下线判定的哨兵节点成为候选者。可能会有多个候选者。
    2. 候选者会向其他哨兵发送命令,请求成为Leader来执行主从切换,其他哨兵会进行投票。
    3. 投票规则如下:每个哨兵只有一次投票机会,先收到谁的请求就投谁;候选者可以投给自己,其他哨兵只能投给别人。
    4. 如果1. 拿到半数以上票数 2. 票数大于等于quorum。就会称为Leader。

❓问题7:哨兵通常要部署多少个比较好?

  • 回答:3个以上,同时应该是奇数。
    1. 哨兵在判定下线和成为Leader的时候,都会发起投票响应,其中成为Leader会有一个强制半数以上票数的要求。那么如果只有2个哨兵,要成为Leader必须2个都同意,这样风险太大,如果某个哨兵挂了,永远无法完成主从切换。

❓问题8:有Leader哨兵了,怎么进行后两步,选主和通知操作。

  • 回答:
    1. 按照以下策略进行多轮考察,最终选出一个从节点
      1. 过滤网络状态差的从节点:从节点断连次数达到某个值(配置文件配置)就认为其网络状态差。
      2. 根据优先级排序选择。优先级越小越靠前。可以通过slave-priority配置项来设置(人为通过服务器性能,网络状态等进行设置)。
      3. 优先级相同的,比较复制进度,哪个从节点复制进度与主节点复制进度差距最小,即数据不一致性最小,选哪个。
      4. 复制进度也一样,则选择ID号小的。
    2. 进行主从切换。
      1. 向被选定的从节点发送 slaveof no one命令,让从节点升级为主节点。升级的从节点信息从slave变为master。
      2. 发送slaveof no one后,Leader哨兵会每隔一秒发送INFO命令观察该节点的信息,当从slaver变成master的时候,哨兵就知道升级成功了。
      3. Leader哨兵向其他从节点发送slaveof命令,附带新主节点的ip和端口,让他们成为新主节点的从节点。
      4. 通过Redis的发布者/订阅者机制向客户端发送通知。具体,哨兵向+switch-master频道发送新主节点的ip和端口,所有订阅该频道的客户端就可以受到该信息,然后通过新信息进行通信。
      5. 监控原来的主节点,当其上线后,对其发送slaveof命令,将其变成新主节点的从节点。

❓问题9:我们从问题1中可以发现,配置节点为哨兵的时候,仅仅提供了主节点的信息,还有quorum值,那么哨兵之间是怎么知道彼此的。

  • 回答:通过 发布者/订阅者模式

❓问题10:发布者/订阅者是什么?

  • 回答:
    1. 发布者/订阅者是一种消息通信模式,发布者发布消息,订阅者接受消息。
    2. 订阅者可以通过subscribe命令订阅任意数量的频道,每当有新消息发送到被订阅的频道时,消息就会被发送给所有订阅该频道的订阅者。

❓问题11:哨兵怎么通过发布者订阅者模式知道彼此的,发布者/订阅者模式在哨兵机制这一块还有其他应用吗?

  • 回答:
    1. 哨兵节点在设立时,会订阅主节点的sentinel_:hello频道,并将自身的ip地址和端口信息发布到该频道上,这样哨兵之间就会收到彼此的地址和端口号。
    2. 哨兵通过发布者/订阅者向客户端发送主从切换信息。

总结

  • 上述使用了通俗话语解释了什么是持久化,主从复制,哨兵机制。那么在此我们对其进行总结

    1. redis持久化是将内存中的数据持久化保存到磁盘中,防止意外重启丢失数据。
    2. 通过主从复制,哨兵机制实现集群模式优点如下:
      1. 高可用性: Redis集群在某个节点发生故障时,自动进行故障转移,保证了服务的可持续性——自动指的是哨兵模式。
      2. 负载均衡: 可以将客户端请求分发到不同的节点上,有效分摊节点的压力,提高系统的整体性能——主从复制,读写分离。
      3. 容灾恢复: 一个服务器坏了数据丢失了,可以通过其他服务器进行恢复。
      4. 易于扩展: 当业务需求变化,要求增加或减少系统负载时,只需要添加或减少节点。
  • 注:集群模式中还有一个Cluster模式,这是Redis的一种高级集群模式,通过它,可以实现数据分片,突破单节点内存限制,实现更大规模的数据存储。

    • 在主从复制中,主从节点保存的数据是一样的,所以其会受单点内存限制。

参考文章

https://cloud.tencent.com/developer/article/1730906
https://www.cnblogs.com/binlovetech/p/17571929.html
https://pdai.tech/md/db/nosql-redis/db-redis-x-copy.html
https://xiaolincoding.com/redis/storage/aof.html#aof-%E6%97%A5%E5%BF%97
https://pdai.tech/md/db/nosql-redis/db-redis-x-pub-sub.html
https://www.cnblogs.com/yidengjiagou/p/17345831.html

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值