Redis篇-19--运维篇1-主从复制(主从复制,读写分离,配置实现,实战案例)

1、概述

Redis的主从复制(Master-Slave Replication)是一种数据冗余机制,它允许将一台Redis服务器的数据复制到其他Redis服务器。在主从复制中,有一台主服务器(Master)和一个或多个从服务器(Slave)。主服务器负责写操作,而从服务器可以用于读操作,从而实现读写分离,减轻主服务器的负载压力。

示例图:
在这里插入图片描述

2、主从复制中的基本概念

- 主服务器(Master):负责处理写操作(如SET、DEL等),并将这些操作同步给从服务器。
- 从服务器(Slave):只读服务器,默认情况下不允许写操作(可以通过配置修改)。从服务器会定期从主服务器获取最新的数据更新,并保持与主服务器的数据一致。
- 数据流向:数据的复制是单向的,只能由主服务器到从服务器。从服务器不能将数据写回主服务器。

3、主从复制的作用

- 数据冗余:通过主从复制,可以在多台服务器上保存相同的数据副本,提供热备份,防止因单点故障导致数据丢失。
- 故障恢复:当主服务器出现问题时,可以从服务器可以接管服务,确保系统的高可用性。
- 负载均衡:通过读写分离,主服务器处理写操作,从服务器处理读操作,分担主服务器的负载,提升系统的并发处理能力。
- 高可用性基础:主从复制是Redis集群和哨兵(Sentinel)系统的基础,支持自动故障转移和高可用性部署。

4、主从复制的工作原理

Redis的主从复制分为两个主要阶段:
初次同步(全量同步)和命令传播(增量同步)。

(1)、初次同步(Full Resynchronization)

初次同步发生条件:

- 第一次建立主从关系:当从服务器首次连接到主服务器时,需要进行全量同步,以确保从服务器拥有与主服务器完全一致的数据集。
- 部分同步失败:如果从服务器与主服务器之间的连接中断时间过长,导致无法通过部分同步恢复数据一致性,也会触发全量同步。

全量同步(初始同步)的过程:

1、从服务器发起请求:从服务器通过配置文件的slaveof(或replicaof)检查自身需要同步的主节点信息。之后通过PSYNC命令携带replid(唯一身份标识符),offset(处理数据的偏移量)参数向主服务器发起数据同步请求。
2、主服务器确认请求:主服务器会校验从服务器的replid是否存在,不存在即表示第一次同步。主服务器会返回replid和offset和从服务器建立连接。
3、主服务器生成RDB文件:同时,主服务器会在后台执行BGSAVE操作,生成一个RDB快照文件。同时,主服务器会开启一个缓冲区,记录从BGSAVE期间的所有写命令。
4、传输RDB文件:BGSAVE完成后,主服务器将生成的RDB文件发送给从服务器。
5、加载RDB文件:从服务器接收到RDB文件后,会清空本地数据,加载RDB文件,重建与主服务器一致的数据集。
6、重放命令:从服务器加载完RDB文件后,主服务器会将缓冲区中的写命令发送给从服务器,确保数据完全一致。

全量过程原理示例如下:
在这里插入图片描述

简单总结:

首次同步是从服务发起,之后RDB文件同步,缓冲区写日志同步,都是主服务主动发起给从服务器的。

(2)、命令传播(Partial Resynchronization)

初次同步完成后,主服务器和从服务器之间会进入命令传播阶段。在这个阶段,主服务器会将所有的写命令实时发送给从服务器,确保两者的数据保持一致。

命令传播(增量同步)的过程:

1、主服务器记录命令:每当主服务器执行写命令时,它会将这些命令记录到一个缓冲区中。
2、从服务器请求命令:从服务器会定期向主服务器发送psync命令,告知主服务器它已经处理到的命令的偏移量offset。
3、主服务器发送命令:主服务器根据从服务器的偏移量offset,将未处理的命令发送给从服务器。
4、从服务器执行命令:从服务器接收到命令后,会立即执行这些命令,确保与主服务器的数据保持一致。
在这里插入图片描述

简单总结:

命令传播是从服务发起(携带自己身份和偏移量),主服务根据偏移量获取缓冲区写日志记录,发送给从服务器,这个过程是从服务主动发起数据请求的。

(3)、部分同步(Partial Resynchronization)

Redis从2.8版本开始引入了部分同步机制,以减少全量同步的频率。部分同步允许从服务器在与主服务器断开连接后,重新连接时只获取断开期间丢失的命令,而不是重新进行全量同步。其目的是为了优化全量同步的性能问题,减少全量同步的频率。

部分同步的过程:

1、从服务器保留复制偏移量offset:从服务器会记录最后一次成功同步的命令偏移量(offset)以及主服务器的replid(唯一标识符)。
2、从服务器发起部分同步请求:当从服务器重新连接到主服务器时,它会通过PSYNC 命令请求部分同步。
3、主服务器检查缓冲区:主服务器会检查它的复制积压缓冲区(Replication Backlog),判断是否包含从服务器请求的命令。
4、发送命令:如果缓冲区中包含从服务器请求的命令,主服务器会将这些命令发送给从服务器,完成部分同步;否则,主服务器会触发全量同步。

5、复制积压缓冲区(Replication Backlog)

复制积压缓冲区是一个固定大小的循环缓冲区,主服务器会将所有写命令记录到这个缓冲区中。它主要用于支持部分同步机制,确保从服务器在断开连接后能够快速恢复数据一致性。

  • 默认大小:1MB(可以通过repl-backlog-size参数调整)。
  • 作用:当从服务器与主服务器短暂断开连接时,主服务器可以通过复制积压缓冲区将断开期间的命令发送给从服务器,避免频繁的全量同步。

6、主从复制的优缺点

优点:

  • 数据冗余:提供热备份,防止数据丢失。
  • 高可用性:支持故障恢复和自动故障转移(结合 Redis Sentinel 使用)。
  • 负载均衡:通过读写分离,减轻主服务器的负载,提升系统性能。
  • 简单易用:配置简单,易于维护。

缺点:

  • 数据一致性问题:由于主从复制是异步的,从服务器可能会存在一定的延迟,导致主从数据不完全一致。
  • 单点故障:如果主服务器发生故障且没有及时切换到从服务器,可能会导致服务中断。
  • 网络带宽消耗:主服务器需要将写命令实时发送给从服务器,可能会占用较多的网络带宽。

7、主从复制的优化建议

1、启用持久化:为了防止主服务器崩溃后数据丢失,建议为主服务器启用持久化( RDB或AOF),并定期备份数据。
2、合理配置复制积压缓冲区:根据业务需求调整复制积压缓冲区的大小,确保从服务器在断开连接后能够快速恢复。
3、监控主从延迟:定期监控主从服务器之间的延迟,确保从服务器能够及时同步主服务器的数据。
4、使用哨兵系统:结合Redis Sentinel系统,实现自动故障检测和主从切换,提升系统的高可用性。
5、限制从服务器的数量:过多的从服务器可能会增加主服务器的负担,建议根据实际需求合理配置从服务器的数量。

8、配置示例

1、主节点配置

第一步:配置ip和port
在这里插入图片描述
第二步:指定配置文件启动redis服务:
启动命令如:

redis-server.exe redis.windows.conf

在这里插入图片描述

2、从节点配置

第一步:配置ip和port
在这里插入图片描述
第二步:配置密码
如果主服务器设置了密码,从服务需要配置主服务器的密码

masterauth master_password

在这里插入图片描述
第三步:配置从节点所属主节点的信息
注意:5.0版本后的redis使用replicaof替代了slaveof。配置和功能是相同的。
在这里插入图片描述
第四步:指定配置文件启动从服务redis
如:redis-server.exe redis.windows.conf
启动从节点后,可以在日志中看到主从复制的相关日志,如下图:
在这里插入图片描述

3、验证主从复制

如下图:左边为主节点redis-cli,右边为从节点redis-cli。
主节点设置aaa1的key值,从节点没有设置该key。
在从节点redis-cli中,直接查看aaa1的key信息,可以正确访问。
说明主从同步关系配置是正确且正常工作的。
在这里插入图片描述

9、代码实现

1、导入依赖

说明下:springboot自带了LettuceConnectionFactory连接工厂,无需再次引入依赖。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置类,注入工厂

这里直接在配置类中写死了配置,如果真实开发建议把配置放到配置文件中。
需要注入主节点连接工厂(用于写操作)和从节点连接工厂数组(用于读操作)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;@Configuration
public class RedisConfig {// 主服务器连接工厂@Bean(name = "masterConnectionFactory")public LettuceConnectionFactory masterConnectionFactory() {RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("127.0.0.1", 6379);
//     config.setPassword("your_master_password");  // 如果主服务器启用了密码保护return new LettuceConnectionFactory(config);}@Bean(name = "slaveConnectionFactories")public RedisConnectionFactory[] slaveConnectionFactories() {RedisStandaloneConfiguration slave1Config = new RedisStandaloneConfiguration("127.0.0.1", 6380);
//        slave1Config.setPassword("your_master_password");  // 如果服务器启用了密码保护RedisStandaloneConfiguration slave2Config = new RedisStandaloneConfiguration("127.0.0.1", 6381);
//        slave2Config.setPassword("your_master_password");  // 如果服务器启用了密码保护return new RedisConnectionFactory[]{new LettuceConnectionFactory(slave1Config),new LettuceConnectionFactory(slave2Config)};}
}

3、自定义注解

用于标注redis操作的方法上,指明该方法走写连接还是读连接。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadWrite {boolean read() default true;  // 默认为读操作
}

4、定义AOP,实现注解类的功能

AOP用于监听Redis操作方法上的自定义注解内容,将读写的标识保存到线程的ThreadLocal中。之后可以根据这里的标识切换读写连接工厂,从而实现读写分离的效果。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;@Aspect
@Component
public class ReadWriteAspect {// 线程局部变量,用于存储当前操作是否为读操作private static final ThreadLocal<Boolean> READ_OPERATION = new ThreadLocal<>();@Around("@annotation(ReadWrite)")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 获取方法上的 @ReadWrite注解ReadWrite readWrite = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(ReadWrite.class);try {// 设置线程局部变量,指示当前操作是读还是写setReadOperation(readWrite.read());// 执行方法return joinPoint.proceed();} finally {// 清除线程局部变量setReadOperation(null);     // 清除ThreadLocal的值}}// 设置线程局部变量,指示当前操作是读还是写public static void setReadOperation(Boolean read) {READ_OPERATION.set(read);     // 保存到ThreadLocal中,用于之后切换数据工厂来源}// 获取线程局部变量,判断当前操作是否为读操作public static Boolean isReadOperation() {return READ_OPERATION.get();}
}

5、读写连接切换实现类

实现RedisConnectionFactory类,并标明@Primary方法,相当于告诉spring容器,在所有容器中的redis连接工厂里,以当前工厂为主。
即:redisTemplate默认使用这个工厂创建redis连接实例,这里我们复写getConnection方法,用于读写连接的自动切换(实现:则是通过ThreadLocal中保存的读写标识)。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisSentinelConnection;
import org.springframework.stereotype.Component;
import java.util.Random;@Component
@Primary  // 标记为默认的连接工厂
public class ReadWriteSplittingConnectionFactory implements RedisConnectionFactory {@Autowired@Qualifier("masterConnectionFactory")private RedisConnectionFactory masterConnectionFactory;  // 主服务器连接工厂@Autowiredprivate RedisConnectionFactory[] slaveConnectionFactories;  // 从服务器连接工厂列表private final Random random = new Random();@Overridepublic org.springframework.data.redis.connection.RedisConnection getConnection() {if (isReadOperation()) {    // 根据线程局部变量ThreadLocal中判断是读还是写// 在从服务器数组中随机选择一个连接int index = random.nextInt(slaveConnectionFactories.length);return slaveConnectionFactories[index].getConnection();} else {// 写操作使用主服务器连接return masterConnectionFactory.getConnection();}}// 判断当前操作是否为读操作private boolean isReadOperation() {// 获取线程局部变量,指示当前操作是读还是写Boolean isRead = ReadWriteAspect.isReadOperation();return isRead != null && isRead;}@Overridepublic boolean getConvertPipelineAndTxResults() {return masterConnectionFactory.getConvertPipelineAndTxResults();}@Overridepublic DataAccessException translateExceptionIfPossible(RuntimeException ex) {return masterConnectionFactory.translateExceptionIfPossible(ex);}@Overridepublic RedisClusterConnection getClusterConnection() {return null;}@Overridepublic RedisSentinelConnection getSentinelConnection() {return null;}
}

6、定义redis的工具类,使用注解

编写Redis公共方法,使用自定义注解标识读写操作。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;@Service
public class RedisService {@Autowiredprivate RedisTemplate<String, String> redisTemplate;// 写操作@ReadWrite(read = false)     // 相当于指定了写的连接工厂创建连接public void set(String key, String value) {redisTemplate.opsForValue().set(key, value);}// 读操作@ReadWrite(read = true)   // 相当于指定了读的连接工厂创建连接public String get(String key) {return (String) redisTemplate.opsForValue().get(key);}
}

7、测试类

编写接口,调用Redis的读写方法,验证

import com.zw.base.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping(value = "redis", method = {RequestMethod.POST, RequestMethod.GET})
public class RedisController extends BaseController {@Autowiredprivate RedisService redisService;@RequestMapping("/set")public String setTest() {redisService.set("aaa3", "zhangsan3");return null;}@RequestMapping("/get")public String getTest() {String aaa2 = redisService.get("aaa2");System.out.println("aaa2:" + aaa2);return aaa2;}}

8、验证结果

测试类中的set和get方法都正常使用。
在这里插入图片描述
但是这并不能直接看出读写到底走的那一个redis服务。这里可以断点看一下,当调用set方法时,可以看到走的逻辑是主节点的连接。
在这里插入图片描述
当调用get方法,如下可以看到,走的逻辑是从节点中任意一个节点的连接。
在这里插入图片描述
如上的验证即可说明,已经达到了读写分离的效果。

学海无涯苦作舟!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/492857.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ELK】Filebeat采集Docker容器日志

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 介绍filebeat是如何工作的 使用部署filebeat 介绍 Filebeat 是一个用于转发和集中日志数据的轻量级传送器。 Filebeat 作为agent安装在服务器上&#xff0c;监视指…

C缺陷与陷阱 — 8 编译与链接

目录 1 程序的编译过程 2 动态链接的优缺点 2.1 动态链接的优点 2.2 动态链接的缺点 2.3 只使用动态链接 3 函数库链接的5个特殊秘密 4 警惕Interpositioning 5 产生链接器报告文件 1 程序的编译过程 程序的编译过程是将源代码转换成计算机可以执行的机器代码的过程。…

QT c++ 测控系统 一套报警规则(上)

本文适用于pc based的测控系统的上位机&#xff0c;定义了一套报警规则。 由5个部分组成&#xff1a;自定义4布尔类、在全局文件定义工位错误结构体和结构体变量&#xff0c;其它地方给此变量的当前值成员赋值&#xff0c;报警线程类、数据库保存类、弹框类。 1.自定义4布尔类…

作业Day4: 链表函数封装 ; 思维导图

目录 作业&#xff1a;实现链表剩下的操作&#xff1a; 任意位置删除 按位置修改 按值查找返回地址 反转 销毁 运行结果 思维导图 作业&#xff1a;实现链表剩下的操作&#xff1a; 1>任意位置删除 2>按位置修改 3>按值查找返回地址 4>反转 5>销毁 任意…

WebSocket入门与结合redis

WebSocket是什么 WebSocket 是一种用于在客户端和服务器之间建立双向通信的协议&#xff0c;它能实现实时、持久的连接。与传统的 HTTP 请求响应模式不同&#xff0c;WebSocket 在建立连接后允许客户端和服务器之间相互发送消息&#xff0c;直到连接关闭。由于 WebSocket 具有…

WSL Ubuntu

文章目录 1. 概述1.1 什么是适用于 Linux 的 Windows 子系统1.2 什么是 WSL 21.3 WSL 2 中的新增功能1.4 比较 WSL 2 和 WSL 1 2. 参考资料3. 修改存储位置4. 网络访问 1. 概述 1.1 什么是适用于 Linux 的 Windows 子系统 适用于 Linux 的 Windows 子系统可让开发人员按原样运…

unity接入coze智能体

官网链接 coze智能体创建、设置 点击创建–选着智能体&#xff0c;随便起一个名字&#xff0c;就可以了 添加令牌 把随便起一个名字&#xff0c;设置时间&#xff0c;把所有选项都勾选上&#xff0c;一定要勾选所有团队空间&#xff0c;否则无法点击确定。 点击确定后&a…

基于51单片机的交通灯设计—夜间、紧急、复位、可调时间、四个数码管显示

基于51单片机的交通灯设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1、采用四方向数码管设计&#xff0c;更加符合真实的交通信号灯设计&#xff1b; 2、左侧按键从上到下依次为…

省略内容在句子中间

一、使用二分查找法 每次查找时&#xff0c;将查找范围分成两半&#xff0c;并判断目标值位于哪一半&#xff0c;从而逐步缩小查找范围。 循环查找 计算中间位置 mid Math.floor((low high) / 2)。比较目标值 target 和中间位置的元素 arr[mid]&#xff1a; 如果 target ar…

Python:动态粒子爱心

预览 代码结构概述 这段代码使用了 pygame 库来创建一个动态的图形窗口&#xff0c;绘制一个心形图案&#xff0c;并在其中显示闪烁的文本。代码主要分为以下几个部分&#xff1a; 初始化和设置心形曲线的计算粒子类的定义生成粒子文本设置主循环 1. 初始化和设置 import p…

springboot449教学资源共享平台(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统教学资源共享平台信息管理难度大&#xff0c;容错率低&am…

DataOps驱动数据集成创新:Apache DolphinScheduler SeaTunnel on Amazon Web Services

引言 在数字化转型的浪潮中&#xff0c;数据已成为企业最宝贵的资产之一。DataOps作为一种文化、流程和实践的集合&#xff0c;旨在提高数据管道的质量和效率&#xff0c;从而加速数据从源头到消费的过程。白鲸开源科技&#xff0c;作为DataOps领域的领先开源原生公司&#xf…

【大模型】GraphRAG技术原理

核心概念 GraphRAG 的核心在于用大模型构建知识图谱知识图谱聚类社区化RAG RAG就是输入&#xff08;问题知识&#xff09;到大模型 1-大模型自动从海量数据中构建知识图谱&#xff08;提取合并实体关系&#xff09; 2-聚类算法从知识图谱中聚类社区并生成社区摘要 3-输入问题…

揭秘区块链隐私黑科技:零知识证明如何改变未来

文章目录 1. 引言&#xff1a;什么是零知识证明&#xff1f;2. 零知识证明的核心概念与三大属性2.1 完备性&#xff08;Completeness&#xff09;2.2 可靠性&#xff08;Soundness&#xff09;2.3 零知识性&#xff08;Zero-Knowledge&#xff09; 3. 零知识证明的工作原理4. 零…

王佩丰24节Excel学习笔记——第十二讲:match + index

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章小技巧】 vlookup与match&#xff0c;index 相结合使用match,index 结合&#xff0c;快速取得引用的值扩展功能&#xff0c;使用match/index函数&#xff0c;结合照相机工具获取照片 一、回顾…

探秘C语言:从诞生到广泛应用的编程世界

引言 在编程的广袤天地里&#xff0c;C 语言宛如一颗璀璨的恒星&#xff0c;持久而耀眼地散发着光芒。自诞生以来&#xff0c;它就以独特的魅力和强大的功能&#xff0c;深深扎根于软件开发的各个层面。无论是构建复杂的操作系统&#xff0c;还是操控微小的嵌入式设备&#xff…

【Python】pandas库---数据分析

大学毕业那年&#xff0c;你成了社会底层群众里&#xff0c;受教育程度最高的一批人。 前言 这是我自己学习Python的第四篇博客总结。后期我会继续把Python学习笔记开源至博客上。 上一期笔记有关Python的NumPy数据分析&#xff0c;没看过的同学可以去看看&#xff1a;【Pyt…

常见异构程序设计语言

目录 一、OpenMP 二、MPI 三、CUDA/HIP 四、OpenACC 五、Athread 六、OpenCL 七、oneAPI 20世纪80年代&#xff0c;异构计算技术就已经诞生了。异构就是CPU、DSP、GPU、ASIC、协处理器、FPGA等各种计算单元、使用不同的类型指令集、不同的体系架构的计算单元&#xff0c…

番外篇 Git 的原理与使用

PS&#xff1a;本篇是个长篇&#xff0c;但是阅读完&#xff0c;可以基本了解 Git 在实际开发中的绝大部分常用操作。 前言&#xff1a;什么是Git 我们在日常工作 / 学习时&#xff0c;对于某些文档 / 代码&#xff0c;可能会存在多个版本需要维护&#xff0c;但是随着版本的…

音频开发中常见的知识体系

在 Linux 系统中&#xff0c;/dev/snd 目录包含与声音设备相关的文件。每个文件代表系统中的一部分音频硬件或音频控制接口。以下是你列出的文件及其含义&#xff1a; 一.基本术语 样本长度(sample)&#xff1a;样本是记录音频数据最基本的单位&#xff0c;计算机对每个通道采…