Redis缓存

1.Redis

在这里插入图片描述

1.1Redis介绍

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)

nosql: 非关系型数据库 redis ,mongdb,hbase;
消息中间件: 主要的作用是为了实现数据的平滑过渡,让程序调用更加流畅(润滑剂)

1.2 Redis安装

1.上传安装包,之后解压
2.说明:在redis的根目录中执行
1). make
2). make install

3.修改配置文件-编辑redis.conf
1).命令 vim redis.conf
2).修改ip绑定
在这里插入图片描述

3).修改保护模式
在这里插入图片描述

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

1.3redis服务器命令

1.启动redis redis-server redis.conf
2.进入客户端 redis-cli -p 6379
3.关闭redis redis-cli -p 6379 shutdown
简化操作: 如果操作的redis是默认的端口 则可以省略不写.

1.4入门案例

1.4.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>

1.4.2测试类

	package com.jt.test;

import org.junit.jupiter.api.Test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.params.SetParams;

public class TestRedis {
	
	/**
	 * 1.spring整合redis
	 * 报错说明:
	 * 	1).如果测试过程中报错 则检查redis配置文件  改3处
	 *  2).检查redis启动方式   redis-server redis.conf
	 *  3).检查Linux的防火墙
	 * 	做完的 测试其他命令.
	 */
	@Test
	public void testString01() {
		
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		//2.操作redis
		jedis.set("a", "redis入门案例");
		String value = jedis.get("a");
		System.out.println(value);
	}
	
	@Test
	public void testString02() {
		
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		//2.判断当前数据是否存在
		if(jedis.exists("a")) {
			System.out.println(jedis.get("a"));
		}else {
			
			jedis.set("a", "测试是否存在的方法");
		}
		
	}
	
	/**
	 * 1.能否简化是否存在的判断
	 * 2.如果该数据不存在时修改数据,否则不修改
	 * setnx方法:  只有当数据不存在时赋值.
	 */
	@Test
	public void testString03() {
	
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		jedis.flushAll(); //清空所有的redis缓存
		jedis.setnx("a", "测试setnx方法1");
		jedis.setnx("a", "测试setnx方法2");
		System.out.println(jedis.get("a"));
	}
	
	/**
	 * 为数据添加超时时间
	 * @throws InterruptedException 
	 * setex方法  保证赋值操作和添加超时时间的操作的原子性
	 * 原子性: 要么同时成功,要么同时失败(类似事务)
	 */
	@Test
	public void testString04() throws InterruptedException {
	
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		jedis.flushAll(); //清空所有的redis缓存
		
		jedis.set("a", "aaaa"); //如果程序报错,则超时方法将不会执行,改数据将永不超时
		//程序报错,意外终止!!!!!!!
		jedis.expire("a", 20);	//添加超时时间    不是原子性操作
		Thread.sleep(2000);
		System.out.println("剩余存活时间:"+jedis.ttl("a"));
		
		//2.实现原子性操作
		jedis.setex("b", 20, "原子性测试");
		System.out.println(jedis.get("b"));
		
	}
	
	
	/**
	 *
	 * 1.只有数据不存在时允许修改
	 * 2.要求实现添加超时时间,并且是原子性操作
	 * SetParams 参数说明:
	 * 	1.NX   只有key不存在时才能修改
	 * 	2.XX   只有key存在时,才能修改
	 *  3.PX   添加的时间单位是毫秒
	 *  4.EX   添加的时间单位是秒
	 */
	@Test
	public void testString05(){
	
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		jedis.flushAll(); //清空所有的redis缓存
		SetParams params = new SetParams();
		params.xx().ex(20);
		jedis.set("aa", "测试A", params);	
		jedis.set("aa", "测试B", params);	
		System.out.println(jedis.get("aa"));
	}
	
	
	
	/**
	 * 存储一类数据时,可以使用hash.
	 */
	@Test
	public void testHASH(){
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		jedis.flushAll(); //清空所有的redis缓存
		jedis.hset("user", "name", "tomcat");
		jedis.hset("user", "id", "100");
		System.out.println(jedis.hgetAll("user"));
	}
	
	
	@Test
	public void testList(){
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		jedis.flushAll(); //清空所有的redis缓存
		jedis.lpush("list", "1","2","3","4");
		System.out.println(jedis.rpop("list"));
		
	}
	
	//控制事务
	@Test
	public void testTx(){
		//1.创建jedis对象
		Jedis jedis = new Jedis("192.168.126.129", 6379);
		jedis.flushAll(); //清空所有的redis缓存
		
		Transaction transaction = jedis.multi();		//2.开启事务
		try {
			transaction.set("aaa", "aaa");
			transaction.set("bbb", "bbbbb");
			transaction.set("ccc", "cccccc");
			transaction.exec();			//事务提交
		} catch (Exception e) {
			e.printStackTrace();
			transaction.discard();		//事务回滚
		}
	}
}
	
	

2.Spring整合Redis

2.1编辑redis.properties文件

	redis.host=192.168.126.129
	redis.port=6379

2.2编辑配置类

	@Configuration 	//我是一个配置类    一般都会与@Bean联用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
	
	@Value("${redis.host}")
	private String host;
	@Value("${redis.port}")
	private Integer port;
	
	//将返回值的结果交给spring容器进行管理,如果以后想要使用该对象则可以直接注入.
	@Bean
	public Jedis jedis() {
		
		return new Jedis(host, port);
	}
	
}

2.3json格式转化问题

封装ObjectMapperUtil将对象转化为JSON,将JSON转化为对象,同时优化异常处理

	package com.jt.util;

import org.springframework.util.StringUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class ObjectMapperUtil {
	
	//json与对象的转化   优化异常处理
	private static final ObjectMapper MAPPER = new ObjectMapper();
	
	//1.将对象转化为JSON
	public static String toJSON(Object target) {
		
		if(target == null) {
			throw new NullPointerException("taget数据为null");
		}
		
		try {
			return MAPPER.writeValueAsString(target);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
			throw new RuntimeException(e); //如果转化过程中有问题则直接抛出异常
		}
		
	}


	//2. 将json串转化为对象   用户传递什么样的类型,就返回什么样的对象!!!
	// <T>  定义了一个泛型对象  代表任意类型
	public static <T> T toObject(String json,Class<T> targetClass) {
		
		if(StringUtils.isEmpty(json) || targetClass == null) {
			throw new NullPointerException("参数不能为null");
		}
		
		try {
			return MAPPER.readValue(json, targetClass);
		} catch (JsonProcessingException e) {
			
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
}

2.4商品分类缓存实现

思路:
1).准备key=“ITEM_CAT::”+parentID
2).准备value="JSON"串
3).首先查询redis缓存
有: 直接获取缓存数据返回给用户.
没有: 直接查询数据库,之后将返回值结果保存到redis中,方便下次使用.

2.4.1 编辑ItemCatController

	/**
	 * 业务:查询商品分类信息,返回VO对象
	 * url地址: /item/cat/list
	 * 参数:  id:一级分类id值
	 * 返回值: EasyUITree对象     
	 * json格式:
	 * 	[{"id":"2","text":"王者荣耀","state":"closed"},{"id":"3","text":"王者荣耀","state":"closed"}]`
	 * sql语句:
	 * 		一级商品分类信息 parent_id=0 SELECT * FROM tb_item_cat WHERE parent_id=0
	 */
	@RequestMapping("/list")
	public List<EasyUITree>  findItemCatByParentId
	(@RequestParam(value = "id",defaultValue = "0") Long parentId){
		//初始化时应该设定默认值.
		//1.查询一级商品分类信息   
		//Long parentId = id==null?0L:id;
		
		//return itemCatService.findItemCatByParentId(parentId);
		//通过缓存的方式获取数据.
		return itemCatService.findItemCatByCache(parentId);
	}

2.4.1 编辑ItemCatService

	/**
	 * 通过缓存的方式查询数据库.
	 * 1).定义key
	 * 2).根据key查询redis.
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<EasyUITree> findItemCatByCache(Long parentId) {
		//1.定义key
		String key = "ITEM_CAT_LIST::"+parentId;
		List<EasyUITree> treeList = new ArrayList<EasyUITree>();
		Long  startTime = System.currentTimeMillis();
		//2.判断redis中是否有值
		if(jedis.exists(key)) {
			//不是第一次查询,则获取缓存数据之后直接返回
			String json = jedis.get(key);
			Long endTime = System.currentTimeMillis();
			treeList = 
					ObjectMapperUtil.toObject(json, treeList.getClass());
			System.out.println("redis查询缓存的时间为:"+(endTime-startTime)+"毫秒");
		}else {
			//redis中没有这个key,表示用户第一次查询.
			treeList = findItemCatByParentId(parentId);
			Long endTime = System.currentTimeMillis();
			//需要将list集合转化为json
			String json = ObjectMapperUtil.toJSON(treeList);
			//将数据保存到redis中
			jedis.set(key, json);
			System.out.println("查询数据库的时间为:"+(endTime-startTime)+"毫秒");
		}
		return treeList;
	}

3.AOP实现redis缓存

3.1传统项目弊端

说明:
1).由于将redis的操作写到service层中,必须导致业务的耦合性高
2).如果采用上述的方式完成缓存,则改缓存不通用,并且代码冗余.效率低

3.2AOP的核心理念

在这里插入图片描述

3.3切入点表达式

1). bean(bean的ID) 按照指定的bean名称拦截用户的请求,之后执行通知方法. 只能匹配单个bean对象
2).within(包名.类名) 可以按照类通配的方式去拦截用户的请求. 控制粒度较粗.
3).execution(返回值类型 包名.类名.方法名(参数列表)) 方法参数级别 控制粒度较细
4).@annotation(包名.注解名称) 按照注解的方式去拦截用户请求.

3.4通知方法

1.前置通知: 主要在 目标方法执行之前执行
2.后置通知: 在目标方法执行之后执行
3.异常通知: 在目标方法执行的过程中报了异常之后执行.
4.最终通知: 无论什么时候都要执行的通知方法.
上述的通知方法,无法控制目标方法是否执行.所以一般"只做记录不做改变"
5.环绕通知: 一般采用环绕通知 实现对业务的控制.

3.4AOP入门案例

//1.将对象交给容器管理
@Component
//2.定义aop切面
@Aspect
public class CacheAOP {
	
	//公式:   切面 = 切入点表达式 + 通知方法.
	/**
	 * 业务需求: 要求拦截ItemCatServiceImpl类中的业务
	 * @Pointcut 切入点表达式 可以理解为就是一个if判断,只有满足条件,才能执行通知方法.
	 */
	//@Pointcut("bean(itemCatServiceImpl)")  //按类匹配,控制的粒度较粗   单个bean
	//@Pointcut("within(com.jt.service..*)")  //按类匹配,控制的粒度较粗     多个bean
	@Pointcut("execution(* com.jt.service..*.*(..))") //细粒度的匹配方式
	public void pointCut() {
		
	}
	
	//joinPoint 方法执行切恰好被切入点表达式匹配,该方法的执行就称之为连接点.
	@Before("pointCut()")
	public void before(JoinPoint joinPoint) {
		System.out.println("我是前置通知!!!!");
		String typeName = 
				joinPoint.getSignature().getDeclaringTypeName();
		String methodName = joinPoint.getSignature().getName();
		Object[] objs = joinPoint.getArgs();
		Object target = joinPoint.getTarget();
		System.out.println("方法执行的全路径为:"+typeName+"."+methodName);
		System.out.println("获取方法参数:"+objs);
		System.out.println("获取目标对象:"+target);
	}
	
	
	//添加环绕通知  可以控制目标方法执行 要求添加参数
	@Around("pointCut()")
	public Object around(ProceedingJoinPoint joinPoint) {
		
		System.out.println("我是环绕通知开始");
		try {
			//Object result = joinPoint.proceed();
			System.out.println("我是环绕通知结束");
			return null;
		} catch (Throwable e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}  //指定目标方法
		
	}
	
}


3.5实现AOP 缓存处理

3.5.1 自定义注解

@Target(ElementType.METHOD)	//标识注解 对谁生效
@Retention(RetentionPolicy.RUNTIME) //注解使用的有效期
public @interface CacheFind {
	
	public String key();			  //标识存入redis的key的前缀
	public int seconds() default 0;  //标识保存的时间 单位是秒
	
}

3.5.2 注解标识

在这里插入图片描述

3.5.3 AOP实现缓存业务处理

	package com.jt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;

import redis.clients.jedis.Jedis;

//1.将对象交给容器管理
@Component
//2.定义aop切面
@Aspect
public class CacheAOP {
	
	@Autowired(required = false)
	private Jedis jedis;
	/**
	 * 实现思路:  拦截被@CacheFind标识的方法 之后利用aop进行缓存的控制
	 * 通知方法:  环绕通知
	 * 实现步骤:
	 * 		1.准备查询redis的key   ITEM_CAT_LIST::第一个参数
	 *      2.@annotation(cacheFind) 动态获取注解的语法.
	 *        拦截指定注解类型的注解并且将注解对象当做参数进行传递.
	 */
	@SuppressWarnings("unchecked")
	@Around("@annotation(cacheFind)")
	public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) {
		
		//1.获取用户注解中的key     ITEM_CAT_LIST::0
		String key = cacheFind.key();
		//2.动态获取第一个参数当做key
		String firstArg = joinPoint.getArgs()[0].toString();
		key += "::"+firstArg; 
		
		Object result = null;
		//3.根据key查询redis.
		if(jedis.exists(key)) {
			
			//根据redis获取数据信息
			String json = jedis.get(key);
			//如何获取返回值类型
			MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
			result = ObjectMapperUtil.toObject(json, methodSignature.getReturnType());
			System.out.println("aop查询redis缓存");
		}else {
			//如果key不存在,则证明是第一次查询.  应该查询数据库
			try {
				result = joinPoint.proceed(); //目标方法返回值
				System.out.println("AOP查询数据库获取返回值结果");
				//将数据保存到redis中
				String json = ObjectMapperUtil.toJSON(result);
				int seconds = cacheFind.seconds();
				if(seconds>0) 
					jedis.setex(key, seconds, json);
				else 
					jedis.set(key, json); 
				
			} catch (Throwable e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
		return result;
	}
}

3.5.4 AOP和代理的关系

在这里插入图片描述

3.5.5 完成商品分类的缓存实现

在这里插入图片描述

4.Redis分片

4.1 为什么使用分片

1).说明: 虽然redis可以扩展内存空间的大小.但是如果需要存储海量的数据一味的扩大内存,其实效率不高.
2).分片介绍: 准备多台redis,共同为用户提供缓存服务.在保证效率的前提下,实现了内存的扩容.
用户在使用分片机制时,将多台redis当做1台使用.

4.2 分片搭建

由3台redis构成 端口号分别为6379/6380/6381, 如果需要准备多台redis则准备多个配置文件即可,注意其中的端口号.

准备多台redis
在这里插入图片描述

4.2.1修改redis端口

vim 6381.conf
在这里插入图片描述

4.2.2启动多台redis

命令: [root@localhost shards]# redis-server 6379.conf & redis-server 6380.conf & redis-server 6381.conf &
检查服务是否启动:
在这里插入图片描述

4.2.3 spring整合Redis入门案例

	ublic class TestRedisShards {
	
	//思考:key=shards  存储到了哪台redis中? 如何存储的?
	@Test
	public void test01() {
		//1.准备list集合  之后添加节点信息
		List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
		shards.add(new JedisShardInfo("192.168.126.129", 6379));
		shards.add(new JedisShardInfo("192.168.126.129", 6380));
		shards.add(new JedisShardInfo("192.168.126.129", 6381));
		//2.创建分片对象
		ShardedJedis shardedJedis = new ShardedJedis(shards);
		shardedJedis.set("shards", "准备分片操作!!!!!");
		System.out.println(shardedJedis.get("shards"));
	}	
}

4.3一致性hash算法

一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题

4.3.1 一致性hash原理说明

目的:解决数据如何在分布式环境下进行存储!!!
hash取值区间: 8位16进制数 共有 2^32种可能性!!! (24)8=2^32次方

1).数据如何存储
在这里插入图片描述
2).当节点发生变化带来哪些影响
当节点的数量发生了变化时,则节点中的对应的数据可以动态的迁移.
原则: 当发生了节点变化时,应该尽可能小的影响其他节点.
在这里插入图片描述

4.3.2 一致性hash特性

一致性哈希算法是在哈希算法基础上提出的,在动态变化的分布式环境中,哈希算法应该满足的几个条件:平衡性、单调性和分散性 [4] 。

①平衡性(均衡性)是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题 [4] 。 利用虚拟节点实现数据平衡 (平衡数据不能做到绝对平均,只能是相对的)

②单调性是指在新增或者删减节点时,不影响系统正常运行 . 可以实现动态的数据迁移.

③分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据 [4]
鸡蛋不要放到一个篮子里。

4.3.3 关于计算机进制问题

二进制: 规则逢2进1 值: 0 -电位 1 +电位 二进制数:111 ---->十进制=
八进制: 规则逢8进1 值: 0-7 八进制数是3位2进制数
十六进制: 规则逢16进1 值: 0-9 A-F A=10 F=15 十六进制可以换算为4位个2进制数
练习: 二进制数110110110110是十六进制数的多少???

4.4SpringBoot整合Redis分片

4.4.1编辑分片配置文件

#redis.host=192.168.126.129
#redis.port=6379
redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381

4.4.2编辑配置类实现redis整合

	@Configuration 	//我是一个配置类    一般都会与@Bean联用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
	
	@Value("${redis.nodes}")
	private String redisNodes;   //node,node,node
	
	/*整合分片实现Redis内存扩容*/
	@Bean
	public ShardedJedis shardedJedis() {
		String[] nodes = redisNodes.split(",");  //节点数组
		//动态获取Redis节点信息.
		List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
		for (String node : nodes) { //node= host:port ---->[host,port]
			String host = node.split(":")[0];
			int port = Integer.parseInt(node.split(":")[1]);
			list.add(new JedisShardInfo(host, port));
		}
		//返回分片对象
		return new ShardedJedis(list);
	}
}

4.4.3 修改RedisAOP中的注入

在这里插入图片描述

4.5关于redis分片总结

1.当redis节点宕机之后,用户访问必然受到影响.
2.当redis服务宕机之后,该节点中的数据可能丢失
3.Redis分片可以实现内存数据的扩容.
4.Redis分片机制中hash运算发生在业务服务器中.redis只负责存取.不负责计算. 所以效率更高.

4.6Redis属性说明

4.6.1 Redis持久化策略

说明: Redis的数据都保存在内存中,如果断电或者宕机,则内存数据将擦除,导致数据的丢失.为了防止数据丢失,Redis内部有持久化机制.
当第一次Redis服务启动时,根据配置文件中的持久化要求.进行持久化操作.如果不是第一次启动,则在服务启动时会根据持久化文件的配置,读取指定的持久化文件.实现内存数据的恢复.

4.6.2 RDB模式

特点:
1.rdb模式是redis中默认的持久化策略.
2.rdb模式定期持久化.保存的是Redis中的内存数据快照.持久化文件占用空间较小.
3.rdb模式可能导致内存数据丢失

命令:
前提:需要在redis的客户端中执行.

  1. save 命令 立即持久化 会导致其他操作陷入阻塞.
  2. bgsave 命令 开启后台运行. 以异步的方式进行持久化. 不会造成其他操作的阻塞.

持久化周期:

	save 900 1        900秒内,如果用户执行的1次更新操作,则持久化一次
	save 300 10		  300秒内,如果用户执行的10次更新操作,则持久化一次
	save 60 10000	  60秒内,如果用户执行的10000次更新操作,则持久化一次
	save 1 1	 	  1秒内,如果用户执行的1次更新操作,则持久化一次    set 阻塞!!!!

在这里插入图片描述

4.6.2 AOF模式

特点:
1). AOF模式默认条件下是关闭状态. 如果需要开启则需要修改配置文件.
2). AOF模式可以实现数据的实时持久化操作,AOF模式记录的是用户的操作过程.
3). 只要开启了AOF模式,则持久化方式以AOF模式为主.

配置:
开启AOF持久化方式
在这里插入图片描述
在这里插入图片描述
持久化文件策略说明

	appendfsync always    只要用户执行一次操作,则持久化一次.
 	**appendfsync everysec  每秒持久化一次   默认策略**      效率略低于RDB
 	appendfsync no        不主动持久化.

4.6.3持久化总结

1.如果用户允许少量的数据丢失,则可以选用RDB模式. 效率更高
2.如果用户不允许数据丢失,则选用AOF模式.
3.可以2种方式都选, 需要搭建组从结构 , 主机选用RDB模式, 从机选用AOF模式,可以保证业务允许

4.6.4配置多种持久化方式

1).设计 : 6379 当主机 7380当从机.
2).修改主机的配置文件:
要求: 主机使用RDB模式
在这里插入图片描述
3).检查默认模式的状态
命令: info replication

在这里插入图片描述
4).实现主从挂载
编辑从服务器向主机进行挂载
在这里插入图片描述
4).主从测试
1.主机中添加测试数据.
2.检查从机中是否有数据
3.检查持久化文件是否有数据.

总结: 一般条件下主机采用RDB模式,从机采用AOF模式,效率更高.

4.7Redis内存策略

redis服务器运行在内存中,数据也在内存中保存. 如果一直往里存,总有一天内存资源不够用,所以需要研究如何优化内存.

4.7.1LRU算法

维度:T 时间
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面(数据)置换算法,选择最近最久未使用的页面(数据)予以淘汰。该算法赋予每个页面(数据)一个访问字段,用来记录一个页面(数据)自上次被访问以来所经历的时间 t,当须淘汰一个页面(数据)时,选择现有页面(数据)中其 t 值最大的,即最近最少使用的页面(数据)予以淘汰。

4.7.2 LFU算法

维度:引用次数
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。

4.7.3 随机算法

随机算法: 灭霸的响指

4.7.4 TTL算法

说明: 将设定了超时时间的数据提前删除.

4.7.5 Redis中内存优化策略

volatile-lru 设定超时时间的数据采用lru算法
allkeys-lru .所有的数据采用lru算法
volatile-lfu 设定超时时间的数据采用LFU算法
.allkeys-lfu 所有的数据才能lfu算法
volatile-random 设定了超时时间的数据采用随机算法
allkeys-random 所有数据采用随机算法
volatile-ttl 设定超时时间的数据采用TTL算法
noeviction 该配置为模式配置 表示内存满时 只报错,不删除数据.
在这里插入图片描述

5.Redis集群

5.1redis入门案例

	public class TestCluster {
	
	/**
	 * 通过程序操作redis   3主机   读写    3从机 数据备份 
	 */
	@Test
	public void test01() {
		Set<HostAndPort> nodes = new HashSet<HostAndPort>();
		nodes.add(new HostAndPort("192.168.126.129", 7000));
		nodes.add(new HostAndPort("192.168.126.129", 7001));
		nodes.add(new HostAndPort("192.168.126.129", 7002));
		nodes.add(new HostAndPort("192.168.126.129", 7003));
		nodes.add(new HostAndPort("192.168.126.129", 7004));
		nodes.add(new HostAndPort("192.168.126.129", 7005));
		//利用程序操作redis集群
		JedisCluster jedisCluster = new JedisCluster(nodes);
		jedisCluster.set("AAAAA", "redis集群测试");
		System.out.println(jedisCluster.get("AAAAA"));
		
	}
}

5.2SpringBoot整合Redis集群

5.2.1编辑redis.properties配置文件

redis.nodes=192.168.126.129:7000,192.168.126.129:7001,192.168.126.129:7002,192.168.126.129:7003,192.168.126.129:7004,192.168.126.129:7005

5.2.2编辑配置类

@Configuration 	//我是一个配置类    一般都会与@Bean联用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
	
	/**
	 * spring整合Redis集群
	 */
	@Value("${redis.nodes}")
	private String redisNodes;
	
	@Bean
	public JedisCluster jedisCluster() {
		Set<HostAndPort> nodeSet = new HashSet<HostAndPort>();
		String[] clusters = redisNodes.split(",");
		for (String cluster : clusters) {	//host:port
			String host = cluster.split(":")[0];
			int port = Integer.parseInt(cluster.split(":")[1]);
			nodeSet.add(new HostAndPort(host, port));
		}
		return new JedisCluster(nodeSet);
	}
}

5.2.3编辑AOP注入

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值