跟随杠精的视角一起来了解Redis的主从复制

2024-02-03 18:40

本文主要是介绍跟随杠精的视角一起来了解Redis的主从复制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

此文转载自:https://my.oschina.net/leonsh/blog/4767309

不想弹好吉他的撸铁狗,都不是好的程序猿

虽然说单机的Redis性能很好,也有完备的持久化机制,那如果你的业务体量真的很大,超过了单机能够承载的上限了怎么办?不做任何处理的话Redis挂了怎么办?带着这个问题开始我们今天的主题-Redis高可用,由于篇幅原因,本章就只聊聊主从复制。

为啥要先从主从复制开始聊,是因为主从复制可以说是整个Redis高可用实现的基石,你可以先有这么一个概念,至于具体为什么是基石,这个后面聊到Sentinel和Redis集群的时候会说到。

首先我们需要知道,对于我们开发人员来说,为什么需要主从架构?一个Redis实例难道不行吗?

其实除了开篇提到的负载超过了Redis单机能够处理的上限,还有一种情况Redis也无法保证自身的高可用性。那就是即便Redis能够扛住所有流量,但是如果这个Redis进程所在的机器挂了呢?请求会直接调转枪口,大量的流量会瞬间把你的DB打挂,然后你就可以背个P0,打包回家了。

而且,假设你对Redis的需求真的超过了单机的容量,你怎么办?搞多台独立的Redis实例吗?那如果用户缓存的数据这一次存在了实例一,下一次如果用户又访问到了实例二,难道又要去走一遍DB吗?除非你能够维护好用户和Redis实例的对应关系(但是通常这样的逻辑比较复杂),否则部署多个Redis实例也就失去了它的意义,没有办法做到横向扩展了。

那换成主从架构就能解决这个问题吗?

我们可以从一个图来直观的了解一下。

Redis主从复制

在主从同步中,我们将节点的角色划分为masterslave,形成一主多从。slave对外提供读操作,而master负责写操作,形成一个读写分离的架构,这样一来就能够承载更多的业务请求。

在多数的业务场景下,对于Redis的读操作都要多于写操作,所以当读请求量特别大的时候,我们可以通过增加slave节点来使Redis扛住更多的流量。

你这不行啊老弟,你往master写数据,那我要是连接到slave上去了,不就拿不到之前的数据了?

我这个小标题的不是写了吗?主从复制,slave会按照某种策略从master同步数据。Redis中我们可以通过slaveof命令让一个Redis实例去复制(replicate)另外一台Redis的状态。被复制的Redis实例就是master节点,而执行slaveof命令的机器就是slave节点。

Redis的主从复制分为两个步骤,分别是同步命令传播

同步操作用于将Master节点内存状态复制给Slave节点,而命令传播则是在同步时,客户端又执行了一些操作改变了服务器的状态,此时master节点的状态与同步操作执行的时候不一致了,所以需要命令传播来使master和slave状态重新一致。

同步的大致的流程如下:

  • slave节点向master节点发送sync命令
  • master收到sync命令之后会执行bgsave命令,Redis会fork出一个子进程在后台生成RDB文件,同时将同步过程中的写命令记录到缓冲区中
  • 文件生成后,master会把RDB文件发送给slave,从服务器接收到RDB文件会将其载入内存
  • 然后master将记录在缓冲区的所有写命令发送给slave,slave对这些命令进行重放,将其数据库的状态更新至和master一致

为了让大家更加清晰的认识到这个过程,我们通过图再来了解一下。

Redis主从复制

🐂🍺,那如果同步完了之后slave又挂了咋办?slave重启之后很可能就又跟maste不一致了?

的确是这样,这就涉及到一个名词叫断点续传了。上面讨论的是slave第一次连接到master,会执行全量复制,而针对上面这种情况,Redis新老版本处理方式不一样。

Redis2.8之前,当主从完成了同步之后,slave如果断线重连,向master发送sync命令,master会将全量的数据再次同给slave。

但是我们会发现一个问题,就是大部分数据都是有序的,再次全量同步显得没有必要。而在 Redis2.8之后,为了解决这个问题,便使用了psync命令来代替sync

简单来说psync命令就是将slave断线期间master接收到的写命令全部发送给slave,slave重放之后状态便与master一致了。

呵呵,就这?那你知道psync具体怎么实现的吗?还是说就只会用用?

psync的实现依赖于主从双方共同维护的offset偏移量。

每次master向slave进行命令传播,传播了多少个字节的数据,就将自己的offset加上传播的字节数。而slave每次收到多少字节的数据,也会同样的更新自己的offset。

基于offset,只需要简单的比对就知道当前主从的状态是否是一致的了,然后基于offset,将对应偏移量所对应的指令传播给slave重放即可。所以即使同步的时候slave挂掉了,基于offset,也能达到断点续传的效果。

不是吧不是吧,那master也挂了呢?你slave重新启动之后master的数据也更新了,按照你的说法,这两永远不可能达到数据一致了

这个问题Redis的确也有想到,实际上除了offset之外,slave断线重连之后还会带上上一个master的实例的runid,每个服务实例都有自己的唯一的runid,只要Redis服务重启,其runid就会发生改变。

master收到这个runid之后会判断是否与自己当前的runid一致,如果一致说明断线之前还是与自己建立的连接,而如果不一致就说明slave断线期间,master也发生了宕机,此时就需要将数据全量同步给slave了。

redis-runid

就算你能解决这个问题,但是你就维护了一个偏移量,偏移量对应的命令从哪儿来?天上掉下来吗?我哪儿知道这些命令是啥?

的确,我们需要通过这个offset去拿到真正需要的数据—也就是指令,而Redis是通过复制积压缓冲区来实现的。

名字高大上,实际上就是一队列。就跟什么递归、轮询、透传一样,听着高大上,实际上简单的一匹。言归正传,复制积压缓冲区的默认大小为1M,Redis在进行命令传播时,除了将写命令发送给slave,还会将命令写到复制积压缓冲区内,并和当前的offset关联起来。这样一来就能够通过offset获取到对应的指令了。

redis-backlog

但是由于缓冲区的大小有限,如果slave的断线时间太久,复制积压缓冲区内早些时候的指令就已经被新的指令覆盖掉了,此处可以理解为一个队列,早些时候入队的元素已经被出队了。

由于没有相对应的offset了,也就无法获取指令数据,此时Redis就会进行全量同步。当然,如果offset还存在于复制积压缓冲区中,则按照对应的offset进行部分同步

基于以上的全量、增量的主从复制,能够在master出现故障的情况下,进行主从的切换,保证服务的正常运行。除此之外还能解决异常情况下数据丢失的问题。基于读写分离的策略还能够提高整个Redis服务的并发量。

可别吹了,你说的这个什么主从复制就没啥缺点吗?

其实是有的,例如刚刚提到的主从的切换,如果不用现成的HA框架,这个过程需要程序员自己手动的完成,同时通知服务调用方Redis的IP发生了变化,这个过程可以说是十分的复杂,甚至还可能涉及到代码配置的改动。而且之前的slave复制的可都是挂掉的master,还得去slave上更改其复制的主库,就更加复杂了。

除此之外,虽然实现了读写分离,但是由于是一主多从的架构,集群的读请求可以扩展,但是写请求的并发是有上限的,那就是master能够扛住的上限,这个没有办法扩展。

好了,本期的分享就到此结束了,我们下期再见。

如果你觉得这篇文章对你有帮助,还麻烦点个赞关个注分个享留个言

也可以微信搜索公众号【SH的全栈笔记】,关注公众号提前阅读其他的文章

往期文章

  • Redis基础—剖析基础数据结构及其用法
  • Redis基础—了解Redis是如何做数据持久化的
  • 简单了解一下K8S,并搭建自己的集群
  • WebAssembly完全入门——了解wasm的前世今生
  • 浅谈JVM与垃圾回收

这篇关于跟随杠精的视角一起来了解Redis的主从复制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/675042

相关文章

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

速了解MySQL 数据库不同存储引擎

快速了解MySQL 数据库不同存储引擎 MySQL 提供了多种存储引擎,每种存储引擎都有其特定的特性和适用场景。了解这些存储引擎的特性,有助于在设计数据库时做出合理的选择。以下是 MySQL 中几种常用存储引擎的详细介绍。 1. InnoDB 特点: 事务支持:InnoDB 是一个支持 ACID(原子性、一致性、隔离性、持久性)事务的存储引擎。行级锁:使用行级锁来提高并发性,减少锁竞争

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物,今天我们实现一下人物实现移动和跳起,依次点击,我们准备创建一个C#文件 创建好我们点击进去,就会跳转到我们的Vision Studio,然后输入这些代码 using UnityEngine;public class Move : MonoBehaviour // 定义一个名为Move的类,继承自MonoBehaviour{private Rigidbo

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置

Unity3D自带Mouse Look鼠标视角代码解析。

Unity3D自带Mouse Look鼠标视角代码解析。 代码块 代码块语法遵循标准markdown代码,例如: using UnityEngine;using System.Collections;/// MouseLook rotates the transform based on the mouse delta./// Minimum and Maximum values can

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

PHP: 深入了解一致性哈希

前言 随着memcache、redis以及其它一些内存K/V数据库的流行,一致性哈希也越来越被开发者所了解。因为这些内存K/V数据库大多不提供分布式支持(本文以redis为例),所以如果要提供多台redis server来提供服务的话,就需要解决如何将数据分散到redis server,并且在增减redis server时如何最大化的不令数据重新分布,这将是本文讨论的范畴。 取模算法 取模运

laravel框架实现redis分布式集群原理

在app/config/database.php中配置如下: 'redis' => array('cluster' => true,'default' => array('host' => '172.21.107.247','port' => 6379,),'redis1' => array('host' => '172.21.107.248','port' => 6379,),) 其中cl