供兄弟们使用:项目测试代码地址: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 !!! 收尾!!!
若有误区或不能解决,私信我,远程协助你!!!