当前位置: 移动技术网 > IT编程>数据库>Redis > 13_ redis数据库高可用

13_ redis数据库高可用

2020年07月26日  | 移动技术网IT编程  | 我要评论

数据库高可用
1.1 数据库高可用说明
当数据库的主库宕机之后.如果没有高可用机制,则可能导致整个服务全部不能正常的使用.
解决策略: 双主模式(双机热备)

1.2 数据库双机热备实现
1.2.1 双机热备的说明
将2台数据库设置为双主模式.互为主从的结构.其中任意的数据库服务器既是主机.也是从机.
在这里插入图片描述

1.2.1 双机热备实现
规划:
之前配置:
192.168.126.129:3306 主机.
192.168.126.130:3306 从机.
优化后的配置
192.168.126.129:3306 主机.从机
192.168.126.130:3306 从机.主机

配置:
1).检查130 主机的状态信息
在这里插入图片描述
2).实现主从的挂载 操作的是129
/129 我是主机 现在当从机/
/实现主从挂载/
CHANGE MASTER TO MASTER_HOST=“192.168.126.130”,
MASTER_PORT=3306,
MASTER_USER=“root”,
MASTER_PASSWORD=“root”,
MASTER_LOG_FILE=“mysql-bin.000001”,
MASTER_LOG_POS=714;

/2.开启主从服务/
START SLAVE

/3.检查主从的状态/
SHOW SLAVE STATUS;

在这里插入图片描述
1.2.2 双机热备的测试
测试A: 修改129中的数据,检查130中是否实现了数据的同步!!
测试B: 修改130中的数据.检查129种是否实现了数据的同步!!!

1.3 数据库高可用实现
1.3.1 Mycat配置

<mycat:schema xmlns:mycat="http://io.mycat/">
	
	<!--name属性是自定义的  dataNode表示数据库的节点信息  jtdb表示逻辑库-->
	<schema name="jtdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="jtdb"/>

	<!--定义节点名称/节点主机/数据名称-->
	<dataNode name="jtdb" dataHost="localhost1" database="jtdb" />
		<!--参数介绍-->
		<!--balance 0表示所有的读操作都会发往writeHost主机 -->  
		<!--1表示所有的读操作发往readHost和闲置的主节点中-->
		<!--writeType=0 所有的写操作都发往第一个writeHost主机-->	
		<!--writeType=1 所有的写操作随机发往writeHost中-->
		<!--dbType 表示数据库类型 mysql/oracle-->
		<!--dbDriver="native"  固定参数 不变-->
		<!--switchType=-1 表示不自动切换, 主机宕机后不会自动切换从节点-->
		<!--switchType=1  表示会自动切换(默认值)如果第一个主节点宕机后,Mycat会进行3次心跳检测,如果3次都没有响应,则会自动切换到第二个主节点-->
		<!--并且会更新/conf/dnindex.properties文件的主节点信息 localhost1=0 表示第一个节点.该文件不要随意修改否则会出现大问题-->
	<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
			  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
		<heartbeat>select 1</heartbeat>

		<!--配置第一台主机主要进行写库操作,在默认的条件下Mycat主要操作第一台主机在第一台主机中已经实现了读写分离.因为默认写操作会发往137的数据库.读的操作默认发往141.如果从节点比较忙,则主节点分担部分压力.
		-->
		<writeHost host="hostM1" url="192.168.126.129:3306" user="root" password="root">
			<!--读数据库1-->
			<readHost host="hostS1" url="192.168.126.130:3306" user="root" password="root" />
			<!--读数据库2-->
			<readHost host="hostS2" url="192.168.126.129:3306" user="root" password="root" />
			
		</writeHost>

			<!--定义第二台主机 由于数据库内部已经实现了双机热备.-->
			<!--Mycat实现高可用.当第一个主机137宕机后.mycat会自动发出心跳检测.检测3次.-->
			<!--如果主机137没有给Mycat响应则判断主机死亡.则回启东第二台主机继续为用户提供服务.-->
			<!--如果137主机恢复之后则处于等待状态.如果141宕机则137再次持续为用户提供服务.-->
			<!--前提:实现双机热备.-->
		
		<writeHost host="hostM2" url="192.168.126.130:3306" user="root" password="root">
			<!--读数据库1-->
			<readHost host="hostS1" url="192.168.126.130:3306" user="root" password="root" />
			<!--读数据库2-->
			<readHost host="hostS2" url="192.168.126.129:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
</mycat:schema>

1.3.2 上传配置文件
在这里插入图片描述
重启mycat.

在这里插入图片描述
1.3.3数据库高可用测试
测试策略:
1).启动服务器检查用户数据是否正确获取.
在这里插入图片描述
2).将Mysql数据库的主库宕机.检查数据是否正确获取
关闭mysql主机.

在这里插入图片描述
修改数据库记录:
在这里插入图片描述

3).将mysql数据库重启,检查mysql数据库是否实现数据的同步.
检查主库记录:

1.4 京淘项目Linux发布(终极)
1.4.1 发布架构图
在这里插入图片描述

1.4.2 项目打包部署
在这里插入图片描述

1.4.3 重启nginx服务器

在这里插入图片描述
1.4.4 京淘后台项目发布测试

在这里插入图片描述
2 Redis 缓存机制
2.1 准备工作
1).还原端口号信息
在这里插入图片描述
2).修改图片上传地址
在这里插入图片描述
3).修改HOSTS文件
在这里插入图片描述
4).修改nginx.conf文件
在这里插入图片描述
修改完成之后,重启nginx服务器.

2.2 为什么要引入缓存
说明:提供用户查询数据的速度.
在这里插入图片描述

2.3 缓存设计的因素
缓存的存储的数据结构应该 K-V结构. key是唯一标识符.
缓存的运行环境 应该让缓存运行在内存中.
缓存的开发语言 C语言开发
缓存中的内存优化策略 LRU算法/LFU算法
缓存数据有效性的设定 多久超时
如何防止内存数据丢失 数据落地(数据进行持久化操作)
2.4 Redis
2.4.1 Redis介绍
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

核心: 内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件
数据类型: 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)
处理速度: 写操作 8.6万/秒 读操作 11.2万/秒 平均10万/秒

2.5 Redis安装
2.5.1 上传安装包
在这里插入图片描述

2.5.2 解压安装包
命令:
1).tar -xvf redis-5.0.4.tar.gz
2).删除安装包,修改文件名称
在这里插入图片描述

2.5.3 安装Redis
说明:跳转到redis根目录中执行如下指令

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

2.5.3 修改redis配置文件
1).编辑配置文件 vim redis.conf
在这里插入图片描述
2).注释IP绑定
在这里插入图片描述
3).关闭保护模式

在这里插入图片描述
4).开启后台启动
在这里插入图片描述

2.5.4redis常规命令
1).启动redis redis-server redis.conf
2).进入redis客户端
在这里插入图片描述
2).关闭redis redis-cli -p 6379 shutdown

小结
1.完成数据库高可用!!!
2.了解为什么需要使用redis
3.redis基本方法 开启 关闭等

2.6 Redis入门案例
2.6.1 引入jar包

<!--spring整合redis -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
		</dependency>

2.6.2 String类型的测试

public class TestRedis {
	
	/**
	 * 如果测试有误,则检查上午修改的三处配置文件. 重启redis.
	 * @throws InterruptedException 
	 */
	@Test
	public void testString() throws InterruptedException {
		Jedis jedis = new Jedis("192.168.126.129",6379);
		jedis.set("aaaa","123456");
		System.out.println(jedis.get("aaaa"));
		
		//判断redis中的key是否存在
		if(!jedis.exists("abc")) {
			jedis.set("abc","123456");
		}
		
		//为数据添加超时时间
		jedis.set("h", "123");
		jedis.expire("h", 10);
		Thread.sleep(2000);
		System.out.println("剩余的存活时间:"+jedis.ttl("h")+"秒");
		
	}
	
	
	/**
	 * 要求:如果key已经存在,则不允许修改!!!
	 * @throws InterruptedException
	 */
	@Test
	public void testStringSetNX() throws InterruptedException {
		Jedis jedis = new Jedis("192.168.126.129",6379);
		jedis.flushAll();	//清空redis
		jedis.setnx("abc", "123");	//只有当数据不存在时才会赋值.
		jedis.setnx("abc", "456");
		System.out.println(jedis.get("abc"));
	}
	
	/**
	 * 虽然expire可以为数据添加超时时间,但是从宏观角度分析,该方法不具备原子性的操作
	 * 使用该方法可能存在风险.
	 * @throws InterruptedException
	 */
	@Test
	public void testStringSetEx() throws InterruptedException {
		Jedis jedis = new Jedis("192.168.126.129",6379);
		jedis.flushAll();	//清空redis
		jedis.setex("abc", 100, "123"); //保证了数据的原子性操作
	}
	
	/**
	 * 1.set操作时,如果该数据存在则不允许赋值
	 * 2.set操作的同时要求添加超时时间
	 * 3.上述的操作,必须同时成功或者同时失败 保证原子性操作.
	 * 
	 * XX = "xx";  当key存在时    才会赋值
  	   NX = "nx";  当key不存在时才会赋值.  
       PX = "px";  添加超时时间的单位  毫秒
  	   EX = "ex";  秒
	 */
	@Test
	public void testStringSet() throws InterruptedException {
		Jedis jedis = new Jedis("192.168.126.129",6379);
		jedis.flushAll();	//清空redis
		SetParams params = new SetParams();
		params.nx().ex(60);
		jedis.set("abc", "123456", params);
	}
}

2.7 商品分类缓存实现原理说明
2.7.1 什么样的数据添加缓存
说明: 变化范围不大的数据,并且需要被频繁查询的数据 可以添加缓存,
常见用法: 省/市/县/乡, 商品分类信息
在这里插入图片描述

2.7.2 缓存实现策略
在这里插入图片描述
说明:
1.商品分类信息采用redis方式进行缓存存取.
2.如果需要存储到Redis中,则必须准备 key(String)-value(String)
3.由于redis通常情况下使用String的数据类型.所以需要将key-value转化为String数据类型.
4.需要将返回值结果List对象转化为JSON数据.

2.8 ObjectMapper学习
2.8.1 入门案例

public class TestObjectMapper {
	
	@Test
	public void testObject() throws JsonProcessingException {
		//1.创建工具API对象
		ObjectMapper objectMapper = new ObjectMapper();
		//2.封装转化对象
		ItemDesc itemDesc  = new ItemDesc();
		itemDesc.setItemId(1001L).setItemDesc("测试json转化")
				.setCreated(new Date()).setUpdated(itemDesc.getCreated());
		//对象------JSON-------String字符串
		//3.对象转化为JSON时,调用的是对象的get()
		String json = objectMapper.writeValueAsString(itemDesc);
		System.out.println(json);  //{key:value,key2:value2}
		
		//4.将json串转化为对象 调用的是对象的set方法为属性赋值....
		ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
		System.out.println(itemDesc2.toString());
	}
	
	
	@Test
	public void testList() throws JsonProcessingException {
		//1.创建工具API对象
		ObjectMapper objectMapper = new ObjectMapper();
		List<ItemDesc> list = new ArrayList<ItemDesc>();
		ItemDesc itemDesc1 = new ItemDesc();
		itemDesc1.setItemId(100L);
		ItemDesc itemDesc2 = new ItemDesc();
		itemDesc2.setItemId(200L);
		list.add(itemDesc1);
		list.add(itemDesc2);
		
		//测试list集合转化为JSON
		String json = objectMapper.writeValueAsString(list);
		System.out.println(json);
		
		//测试json结构,能否转化为List集合
		List<ItemDesc> list2 = objectMapper.readValue(json, list.getClass());
		System.out.println(list2);
	}
	
}

2.8.2 JSON转化的工具API封装
说明:该工具API主要的目的为了简化对象与JSON转化的过程.编辑如下的API

//简化代码而生!!
public class ObjectMapperUtil {
	
	//定义一个常量对象
	private static final ObjectMapper MAPPER = new ObjectMapper();
	
	//1.将对象转化为json串
	public static String toJSON(Object target) {
		
		String json = null;
		try {
			json = MAPPER.writeValueAsString(target);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
			//将检查异常,转化为运行时异常!!!!
			throw new RuntimeException();
		}
		return json;
	}
	
	//2.将json串按照指定的类型转化为对象
	//实现:传递什么类型,就返回什么对象
	// <T> 定义泛型
	public static <T> T  toObj(String json,Class<T> target) {
		T t = null;
		try {
			t = MAPPER.readValue(json, target);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
		return t;
	}
}

2.9 Spring容器管理Redis对象
2.9.2 编辑pro文件
说明:在jt-common中添加配置文件.指定redis的链接地址

redis.host=192.168.126.129
redis.port=6379
在这里插入图片描述

2.9.1 编辑配置类
说明:如果需要将redis对象交给Spring容器管理则必须通过配置文件/配置类的形式管理.
在这里插入图片描述

package com.jt.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import redis.clients.jedis.Jedis;

//代表早期的配置文件
@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
	
	@Value("${redis.host}")
	private String host;
	@Value("${redis.port}")
	private Integer port;
	
	//bean注解  将生成的jedis对象交给Spring容器管理
	@Bean
	public Jedis jedis() {
		
		return new Jedis(host,port);
	}
}

2.10 实现商品分类缓存
2.10.1 编辑ItemCatController

/**
	 * url:http://localhost:8091/item/cat/list 
	 *   参数: 当展现二三级信息时,会传递父级的Id信息,如果展现1级菜单,则应该设定默认值
	 *   返回值: List<EasyUITree>
	 */
	@RequestMapping("list")
	public List<EasyUITree> findItemCatList
	(@RequestParam(name="id",defaultValue="0")Long parentId){
		
		//1.查询一级商品分类信息,所以
		//return itemCatService.findItemCatList(parentId); //数据库操作
		return itemCatService.findItemCatCache(parentId);		   //缓存操作
	}

2.10.2 编辑ItemCatService

/**
	 * 思路:
	 * 	1.定义查询redis的key, key要求唯一的
	 *  2.第一次查询先查询redis.
	 *  	没有数据: 表示缓存中没有数据, 查询数据库,之后将数据保存到redis中
	 *  	有数据:   证明缓存中有值, 直接返回给用户即可.
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<EasyUITree> findItemCatCache(Long parentId) {
		String key = "ITEM_CAT_PARENTID_"+parentId;
		List<EasyUITree> treeList = new ArrayList<>();
		//1.判断redis中是否有记录
		if(jedis.exists(key)) {
			//表示redis中有记录.
			String json = jedis.get(key);
			treeList = 
					ObjectMapperUtil.toObj(json, treeList.getClass());
			System.out.println("实现redis缓存查询");
		}else {
			//redis中没有记录,需要先查询数据库.
			treeList = findItemCatList(parentId);
			//将数据库记录转化为json之后保存到redis中
			String json = ObjectMapperUtil.toJSON(treeList);
			jedis.set(key, json);
			System.out.println("第一次查询数据库!!!!!");
		}
		
		return treeList;
	}

本文地址:https://blog.csdn.net/huitailang857/article/details/107370169

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网