Redis-day04 Redis学习高级部分(三 收尾 架构相关)
Redis学习高级部分(三 收尾 架构相关)
1. Redis 主从复制
1.1 主从复制
主从复制架构仅仅用来解决数据的冗余备份,从节点仅仅用来同步数据
无法解决: 1.master节点出现故障的自动故障转移
1.2 主从复制架构图

1.3 搭建主从复制(做之前拍快照)(带同学们结合官网看redis7.0.0的做法)
第一步,创建三个目录代表三台机器,master,node1,node2
拷贝源码中的redis.conf分别到master,node1,node2中[root@master redis-install]# cp redis-7.0.0/redis.conf ./master/ [root@master redis-install]# cp redis-7.0.0/redis.conf ./node1/ [root@master redis-install]# cp redis-7.0.0/redis.conf ./node2/

# 1.准备3台机器并修改配置,修改端口号,开启远程连接,配置主节点是谁
- master
port 6379
protected-mode no
- node1
port 6380
protected-mode no
replicaof <masterip> <masterport> # replicaof 192.168.40.110 6379
- node2
port 6381
protected-mode no
replicaof <masterip> <masterport> # replicaof 192.168.40.110 6379
# 2.启动3台机器进行测试
- cd /usr/local/soft/bigdata17/redis-install
- redis-server ./master/redis.conf
- redis-server ./node1/redis.conf
- redis-server ./node2/redis.conf
2. Redis哨兵机制
2.1 哨兵Sentinel机制
Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。简单的说哨兵就是带有自动故障转移功能的主从架构。
无法解决: 1.单节点并发压力问题 2.单节点内存和磁盘物理上限
2.2 哨兵架构原理

3. Redis集群
3.1 集群
Redis在3.0后开始支持Cluster(模式)模式,目前redis的集群支持节点的自动发现,支持slave-master选举和容错,支持在线分片(sharding shard )等特性。reshard
PING PONG协议(心跳机制)
3.2 集群架构图

3.3 集群细节(需要画图带同学理解)
- 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
- 节点的fail是通过集群中超过半数的节点检测失效时才生效.(半数机制,后面hadoop也会说到 )
- 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
- redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value

3.4 集群搭建
判断一个是集群中的节点是否可用,是集群中的所用主节点选举过程,如果半数以上的节点认为当前节点挂掉,那么当前节点就是挂掉了,所以搭建redis集群时建议节点数最好为奇数,搭建集群至少需要三个主节点,三个从节点,至少需要6个节点。
# 1.准备环境安装ruby以及redis集群依赖
- yum install -y ruby rubygems
# https://rubygems.org/gems/redis/versions
- gem install redis-xxx.gem

# 2.在一台机器创建7个目录

# 3.每个目录复制一份配置文件
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/redis.conf ./7000/
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/redis.conf ./7001/
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/redis.conf ./7002/
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/redis.conf ./7003/
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/redis.conf ./7004/
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/redis.conf ./7005/
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/redis.conf ./7007/

# 4.修改不同目录配置文件
- port 7000 ..... //修改端口
- # bind 127.0.0.1 -::1 //开启远程连接
- protected-mode no
- daemonize yes //开启守护进程
- dbfilename dump-7000.rdb //每台机器的文件不能一样
- cluster-enabled yes //开启集群模式
- cluster-config-file nodes-port.conf //集群节点配置文件
- cluster-node-timeout 5000 //集群节点超时时间
- appendonly yes //开启AOF持久化
- appendfilename "appendonly-7000.aof" //修改aof文件名
- appenddirname "appendonlydir-7000"
# 5.指定不同目录配置文件启动七个节点(7003,7004的忘记修改了守护进程)
[root@master redis-cluster]# redis-server 7000/redis.conf
[root@master redis-cluster]# redis-server 7001/redis.conf
[root@master redis-cluster]# redis-server 7002/redis.conf
[root@master redis-cluster]# redis-server 7003/redis.conf
[root@master redis-cluster]# redis-server 7004/redis.conf
[root@master redis-cluster]# redis-server 7005/redis.conf
[root@master redis-cluster]# redis-server 7006/redis.conf
# 6.查看进程
- [root@localhost bin]# ps aux|grep redis

1.创建集群
# 1.复制集群操作脚本到bin目录中
[root@master redis-cluster]# cp /usr/local/soft/bigdata17/redis-install/redis-7.0.0/src/redis-trib.rb /usr/local/soft/redis/bin/
# 2.创建集群
redis7.0.0之前的命令:redis-trib.rb create --replicas 1 192.168.40.110:7000 192.168.40.110:7001 192.168.40.110:7002 192.168.40.110:7003 192.168.40.110:7004 192.168.40.110:7005
redis7.0.0之后的命令:redis-cli --cluster create 192.168.40.110:7000 192.168.40.110:7001 192.168.40.110:7002 192.168.40.110:7003 192.168.40.110:7004 192.168.40.110:7005 --cluster-replicas 1

# 3.集群创建成功出现如下提示

2.查看集群状态
# 1.查看集群状态 check [原始集群中任意节点] [无]
redis-cli --cluster check 192.168.40.110:7000
# 2.集群节点状态说明
- 主节点
主节点存在hash slots,且主节点的hash slots 没有交叉
主节点不能删除
一个主节点可以有多个从节点
主节点宕机时多个副本之间自动选举主节点
- 从节点
从节点没有hash slots
从节点可以删除
从节点不负责数据的写,只负责数据的同步
使用集群(演示其中一个主节点宕机的状态,然后从节点接管)
3.添加主节点
# 1.添加主节点 add-node [新加入节点] [原始集群中任意节点]
redis-cli --cluster add-node 192.168.40.110:7006 192.168.40.110:7000 --cluster-slave
- 注意:
1.该节点必须以集群模式启动
2.默认情况下该节点就是以master节点形式添加
4.添加从节点
# 1.添加从节点 add-node --slave [新加入节点] [集群中任意节点]
- redis-trib.rb add-node --slave 192.168.40.110:7006 192.168.40.110:7000
- 注意:
当添加副本节点时没有指定主节点,redis会随机给副本节点较少的主节点添加当前副本节点
# 2.为确定的master节点添加主节点 add-node --slave --master-id master节点id [新加入节点] [集群任意节点]
- redis-trib.rb add-node --slave --master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7006 127.0.0.1:7000
5.删除副本节点
# 1.删除节点 del-node [集群中任意节点] [删除节点id]
- redis-trib.rb del-node 192.168.40.110:7002 0ca3f102ecf0c888fc7a7ce43a13e9be9f6d3dd1
- 注意:
1.被删除的节点必须是从节点或没有被分配hash slots的节点
6.集群在线分片
# 1.在线分片 reshard [集群中任意节点] [无]
- redis-trib.rb reshard 192.168.40.110:7000
3.5 java和springboot分别操作集群
1. java操作
引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>redis-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
代码编写
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("192.168.40.110", 7000));
jedisClusterNode.add(new HostAndPort("192.168.40.110", 7001));
jedisClusterNode.add(new HostAndPort("192.168.40.110", 7002));
jedisClusterNode.add(new HostAndPort("192.168.40.110", 7003));
jedisClusterNode.add(new HostAndPort("192.168.40.110", 7004));
jedisClusterNode.add(new HostAndPort("192.168.40.110", 7005));
jedisClusterNode.add(new HostAndPort("192.168.40.110", 7006));
JedisCluster jedisCluster = null;
try {
//connectionTimeout:指的是连接一个url的连接等待时间
//soTimeout:指的是连接上一个url,获取response的返回等待时间
jedisCluster = new JedisCluster(jedisClusterNode, config);
Set<String> keys = jedisCluster.keys("*");
System.out.println(keys);
System.out.println(jedisCluster.set("age", "18"));
System.out.println(jedisCluster.get("age"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedisCluster != null)
jedisCluster.close();
}
2. Springboot操作
引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shujia</groupId>
<artifactId>pringboot-redis-demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>pringboot-redis-demo1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--引入spring data redis 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改配置文件
server:
port: 8989
spring:
redis:
# host: 192.168.40.110
# port: 7000
# database: 0
cluster:
nodes: 192.168.40.110:7000,192.168.40.110:7001,192.168.40.110:7002,192.168.40.110:7003,192.168.40.110:7004,192.168.40.110:7005,192.168.40.110:7006
编写代码
Set<String> keys = stringRedisTemplate.keys("*");
System.out.println(keys);
4、Springboot整合mysql,Redis案例
1、新建项目



或者从官网创建

2、引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shujia</groupId>
<artifactId>springboot-anlidemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-anlidemo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--让内嵌tomcat具有解析jsp功能-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--c标签库-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--添加MySql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--添加JDBC依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--使用thymelaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--mybatis相关-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
<!-- 获取页面session对象request对象response对象 HttpServletXXX jar包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 获取页面session对象request对象response对象 HttpServletXXX jar包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、修改配置文件
# =========================修改服务器相关============================
server:
port: 8083
servlet:
context-path: /bigdata17
tomcat:
uri-encoding: UTF-8
# =====================新增数据库配置=============================
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.40.110:3306/bigdata17?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
thymeleaf:
cache: false # 关闭缓存
prefix: classpath:/templates/ #指定模板位置
suffix: .html #指定后缀
redis:
# host: 192.168.40.110
# port: 7000
# database: 0
cluster:
nodes: 192.168.40.110:7000,192.168.40.110:7001,192.168.40.110:7002,192.168.40.110:7003,192.168.40.110:7004,192.168.40.110:7005,192.168.40.110:7006
# ====================新增mybits配置=========================
mybatis:
configuration:
map-underscore-to-camel-case: true #下划线驼峰设置
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL语句
# ==================设置日志级别==============================
logging:
level:
root: info #指定根日志级别(一般不推荐修改根日志,输出信息太多,推荐使用子日志)
4、创建分层

5、运行测试没有问题后,一切准备就绪
6、第一个案例,用户登录案例(springboot结合mysql)
1. 创建数据库表,用户表(注意,主键设置成自增,方便后续查找或者删除)

2. 创建实体类,对应数据库表结构(规范是成员变量与表字段一样,大小写可以不一样)
package com.shujia.entity;
public class User {
private Integer id;
private String username;
private String password;
public User(Integer id, String username, String password) {
this.id=id;
this.username=username;
this.password=password;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
3. 编写对用户的方法接口(在service层中)
package com.shujia.service;
import com.shujia.entity.User;
import java.util.List;
public interface UserService {
//根据账户名和密码查找
User findByNameAndPwd(String name,String password);
}
4. 编写对接口的实现类(在service层中的impl包中),同时编写dao层的数据库操作类
package com.shujia.dao;
import com.shujia.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserDao {
//根据账户名和密码查找
@Select("select * from user where username=#{username} and password=#{password}")
public User findByNameAndPwd(String username,String password);
}
package com.shujia.service.impl;
import com.shujia.dao.UserDao;
import com.shujia.entity.User;
import com.shujia.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User findByNameAndPwd(String name,String password) {
return userDao.findByNameAndPwd(name,password);
}
}
5. 编写控制类
package com.shujia.controller;
import com.shujia.entity.User;
import com.shujia.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Controller
public class UserController {
@Autowired
private UserService userService;
//新增登录页面
@GetMapping("/index.html")
public String userLogin(){
return "login";
}
//查询用户
@PostMapping("/login")
public String login(User user,Model model) {
// userService.save(user);
User byName = userService.findByNameAndPwd(user.getUsername(),user.getPassword());
if(byName!=null){
//表示重置index.html界面并且跳转到index.html界面
return "index";
}else {
return "login";
}
}
}
6. 编写登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<style>
a{
color: #ffffff;
}
h2{
/*文字对齐*/
text-align: center;
}
</style>
<body>
<h2>17期springboot案例-登录</h2>
<div style="width:600px;height:100%;margin-left:270px;">
<form th:action="@{/login}" method="post">
<!-- form-control给input添加这个class后就会使用bootstrap自带的input框-->
用户名:<input class="form-control" type="text" th:value="${username}" name="username"><br>
<!--注意参数的拼接-->
密 码:<input class="form-control" type="password" th:value="${password}" name="password"><br>
<button class="btn btn-primary btn-lg btn-block">保存</button>
</form>
</div>
</body>
</html>
首页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<style>
a{
color: #ffffff;
}
h1{
/*文字对齐*/
text-align: center;
}
</style>
<body>
<!--一开始写一个登录成功-->
<h2>登录成功</h2>
</body>
</html>










浙公网安备 33010602011771号