redis搭建主从集群模式+整合springboot

发布时间:2024年01月19日

供兄弟们使用:项目测试代码地址:https://gitee.com/crqyue/springboot-redis-default.git

建议先看教程哦!!

1. 基本介绍

最简单的,最常见的模式。

在主从复制中,数据库分为两类:

  • 主数据库(master)
  • 从数据库(slave)

在这里插入图片描述

其中主从复制有如下**特点**:

  • 主数据库可以进行读写操作,当读写操作导致数据变化时会自动将数据同步给从数据库
  • 复制的数据流是单向的,只能由主节点复制到从节点。
  • 从数据库一般都是只读的,并且接收主数据库同步过来的数据
  • 一个master可以拥有多个slave,但是一个slave只能对应一个master
  • slave挂了不影响其他slave的读和master的读和写,重新启动后会将数据从master同步过来
  • master挂了以后,不影响slave的读,但redis不再提供写服务,master重启后redis将重新对外提供写服务
  • master挂了以后,不会在slave节点中重新选一个master

redis复制原理

redis 的复制分为两部分操作 同步(SYNC)和 命令传播(command propagate)

  • 同步(SYNC)用来将从服务器的状态 更新到 和主服务器 一致。白话文解释就是从服务器主动获取 主服务器的数据。保持数据一致。具体实现是,主服务器收到SYNC命令后,生成RDB快照文件,然后发送给从服务器。

  • 命令传播 (command propagate)用于在主服务器数据被修改后,主从不一致,为了让从服务器保持和主服务器状态一致,而做的命令传播。白话文解释就是主服务器收到客户端修改数据命令后,数据库数据发生变化,同时将命令缓存起来,然后将缓存命令发送到从服务器,从服务器通过载入缓存命令来达到主从数据一致。这就是所谓的命令传播。

  • 为什么需要有同步和命令传播的两种复制操作: 当只有同步操作时候,那么在从服务器向主服务器发送SYNC命令时候,主服务器在生成RDB快照文件时候,仍然会收到客户端的命令修改数据状态,这部分数据如果不能传达给从服务器,那么就会出现主从数据不一致的现象。这时候就出现了命令传播,主服务器收到从服务器的SYNC命令后,生成RDB快照文件同时,将此段时间内收到的命令缓存起来,然后使用命令传播的操作发送从服务器。来达到主从数据一致。

    上面介绍了redis复制的两种操作,而redis得主从复制正式基于 同步 和 命令传播 来实现得。下面两张图展示了redis复制的流程:

在这里插入图片描述

2. 基本使用(基于Linux的centos系统) (window下的操作也差不多)

2.1完成redis安装 D:\me\Typoraio\databasework\Linux系列\CenOS系列\Linux安装配置Redis.md(个人使用)

2.1完成redis安装 [Centos9下安装配置Redis-CSDN博客](https://blog.csdn.net/m0_74021233/article/details/135463757)(csdn博客使用)

2.2 在redis目录文件下找到redis.conf文件并且复制到其他地方-【复制三份】

命名文件:

  • redis6380.conf
  • redis6381.conf
  • redis6382.conf

在这里插入图片描述

2.3 redis6380.conf【主库】 略

简介配置看:2.6条目录

2.4 redis6381.conf 【从库】略

2.5 redis6382.conf 【从库】略

这里配置文件过于长了,直接拉取拿过来用即可:
gitee地址:https://gitee.com/crqyue/springboot-redis-conf.git
在这里插入图片描述

2.6 总结一下

三个服务中

6380主库必须的配置:就是如下几行做下更改即可

bind 127.0.0.1
port 6380
daemonize yes
logfile "6380.log"

# 主从复制配置
replicaof no one  # 主库不是从其他实例复制数据

6381和6382从库:

bind 127.0.0.1
port 6381 # 6382就改为port 6382即可
daemonize yes
logfile "6381.log"

# 主从复制配置
replicaof 127.0.0.1 6380  # 从库复制主库的数据

3. 启动这三个redis配置服务

注意:建议直接进入到文件存放目录再运行命令

redis-server redis6380.conf

redis-server redis6381.conf

redis-server redis6382.conf

4. 查看服务启动情况

ps -ef|grep redis|grep -v grep

在这里插入图片描述

5. redis连接工具进行连接 【工具安装就略过了蛤】

在这里插入图片描述

填写主机IP等信息
在这里插入图片描述

其他两个同理

最后连接效果
在这里插入图片描述

这时切换到主库往下滑找到role这一行,
在这里插入图片描述

此效果证明主从配置搭建没问题

6. redis主从模式整合springboot简单的单元测试

6.1 pom依赖

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

<!-- jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.10.0</version>
</dependency>

6.2 yaml配置redis连接

spring:
  datasource: # oracle数据库连接 - 可以切换为自己的数据库
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@localhost:1521:orcl
    username: oa
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
  redis: # 主从配置
    master:
      host: 192.168.6.128
      port: 6380
    slave1:
      host: 192.168.6.128
      port: 6381
    slave2:
      host: 192.168.6.128
      port: 6382
#  redis: # redis哨兵配置
#    sentinel:
#      master: mymaster
#      nodes: 192.168.6.128:6301,192.168.6.128:6302,192.168.6.128:6303
mybatis-plus:
  type-aliases-package: com.cy.entity

6.3 config包下的RedisConfig类

注意点:注意点:注意点:

有时可能会出现这样的问题:

在这里插入图片描述

解决方案:
在这里插入图片描述

解释一下:@Primary注解的作用

@Primary 注解用于标识 Spring 容器中的候选 bean,当存在多个相同类型的 bean 时,被标记为 @Primary 的 bean 会被优先选择作为默认的 bean,除非通过 @Qualifier 明确指定其他的 bean。

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
	
	@Bean
	public RedisConnectionFactory masterConnectionFactory() {
		JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
		jedisConnectionFactory.setHostName("192.168.6.128");
		jedisConnectionFactory.setPort(6380);
		return jedisConnectionFactory;
	}
	
	@Bean
	public RedisConnectionFactory slave1ConnectionFactory() {
		JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
		jedisConnectionFactory.setHostName("192.168.6.128");
		jedisConnectionFactory.setPort(6381);
		return jedisConnectionFactory;
	}
	
	@Bean
	public RedisConnectionFactory slave2ConnectionFactory() {
		JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
		jedisConnectionFactory.setHostName("192.168.6.128");
		jedisConnectionFactory.setPort(6382);
		return jedisConnectionFactory;
	}

	@Primary
	@Bean("redisTemplate")
	@Qualifier("master")
	public RedisTemplate<String, Object> masterRedisTemplate(RedisConnectionFactory masterConnectionFactory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(masterConnectionFactory);
		// 设置序列化等其他配置
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // 设置序列化器
		GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
		
		redisTemplate.setKeySerializer(stringRedisSerializer);
		redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
		redisTemplate.setHashKeySerializer(stringRedisSerializer);
		redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
		return redisTemplate;
	}
	
	@Bean
	@Qualifier("slave1")
	public RedisTemplate<String, Object> slave1RedisTemplate(RedisConnectionFactory slave1ConnectionFactory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(slave1ConnectionFactory);
		// 设置序列化等其他配置
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // 设置序列化器
		GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
		
		redisTemplate.setKeySerializer(stringRedisSerializer);
		redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
		redisTemplate.setHashKeySerializer(stringRedisSerializer);
		redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
		return redisTemplate;
	}
	
	@Bean
	@Qualifier("slave2")
	public RedisTemplate<String, Object> slave2RedisTemplate(RedisConnectionFactory slave2ConnectionFactory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(slave2ConnectionFactory);
		// 设置序列化等其他配置
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // 设置序列化器
		GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
		
		redisTemplate.setKeySerializer(stringRedisSerializer);
		redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
		redisTemplate.setHashKeySerializer(stringRedisSerializer);
		redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
		return redisTemplate;
	}
}

6.4 util包下的RedisUtil类

该类的作用:就是对RedisTemplate类中一些读写方法的集中处理。使其在测试时可以直接通过该类来实现同样的效果。毕竟它原本的读写操作需要去点的东西太多了嘛

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

@Component
public class RedisUtil {
	
	@Autowired
	@Qualifier("master")
	private RedisTemplate<String, Object> masterRedisTemplate;
	
	@Autowired
	@Qualifier("slave1")
	private RedisTemplate<String, Object> slave1RedisTemplate;
	
	@Autowired
	@Qualifier("slave2")
	private RedisTemplate<String, Object> slave2RedisTemplate;
	
	// 根据业务需求选择使用哪个 RedisTemplate
	private RedisTemplate<String, Object> chooseRedisTemplate() {
		// 这里简单地选择使用 masterRedisTemplate
		return masterRedisTemplate;
	}
	
	// 字符串操作
	public void set(String key, Object value) {
		chooseRedisTemplate().opsForValue().set(key, value);
	}
	
	public Object get(String key) {
		return chooseRedisTemplate().opsForValue().get(key);
	}
	
	// 哈希操作
	public void hSet(String key, String field, Object value) {
		chooseRedisTemplate().opsForHash().put(key, field, value);
	}
	
	public Object hGet(String key, String field) {
		return chooseRedisTemplate().opsForHash().get(key, field);
	}
	
	// 集合操作
	public void sAdd(String key, Object... values) {
		chooseRedisTemplate().opsForSet().add(key, values);
	}
	
	public Set<Object> sMembers(String key) {
		return chooseRedisTemplate().opsForSet().members(key);
	}
	
	// 列表操作
	public void lPush(String key, Object value) {
		chooseRedisTemplate().opsForList().leftPush(key, value);
	}
	
	public List<Object> lRange(String key, long start, long end) {
		return chooseRedisTemplate().opsForList().range(key, start, end);
	}
	
	// 有序集合操作
	public void zAdd(String key, Object value, double score) {
		chooseRedisTemplate().opsForZSet().add(key, value, score);
	}
	
	public Set<Object> zRange(String key, long start, long end) {
		return chooseRedisTemplate().opsForZSet().range(key, start, end);
	}
	
	// 其他操作...
	
}

6.5 test包下的RedisTest单元测试类


import com.cy.springbootredisdefault.SpringbootRedisDefaultApplication;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest(classes = SpringbootRedisDefaultApplication.class)
@Slf4j
public class RedisTest {
	
	@Autowired
	@Qualifier("master")
	private RedisTemplate<String, Object> masterRedisTemplate;
	
	@Autowired
	@Qualifier("slave1")
	private RedisTemplate<String, Object> slave1RedisTemplate;
	
	@Autowired
	@Qualifier("slave2")
	private RedisTemplate<String, Object> slave2RedisTemplate;
	
	@Test
	public void testRedisOperations() {
		// 在主库添加键值对
		masterRedisTemplate.opsForValue().set("testKey", "testValue");
		
		// 从主库获取键值对
		String valueFromMaster = (String) masterRedisTemplate.opsForValue().get("testKey");
		System.out.println("Value from master: " + valueFromMaster);

//		// 在从库1添加键值对
//		slave1RedisTemplate.opsForValue().set("testKey", "testValueSlave1");
		
		// 从从库1获取键值对
		String valueFromSlave1 = (String) slave1RedisTemplate.opsForValue().get("testKey");
		System.out.println("Value from slave1: " + valueFromSlave1);

//		// 在从库2添加键值对
//		slave2RedisTemplate.opsForValue().set("testKey", "testValueSlave2");
		
		// 从从库2获取键值对
		String valueFromSlave2 = (String) slave2RedisTemplate.opsForValue().get("testKey");
		System.out.println("Value from slave2: " + valueFromSlave2);
	}
}

6.6 我们向主库存了一个testKey键的testValue值

打开redis连接工具查看主库和从库是否具有测key

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

或者

直接单元测试也行
在这里插入图片描述

至此,redis主从集群模式整合springboot基本使用收工!!

OK !!! 收尾!!!

若有误区或不能解决,私信我,远程协助你!!!

文章来源:https://blog.csdn.net/m0_74021233/article/details/135619574
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。