Redis-2

本文围绕 Redis 展开,探讨了读并发量太大、单机挂掉、写并发与安全等问题的解决办法。分析了 Redis 集群执行流程,介绍了集群搭建过程。还阐述了在 Spring Boot 里使用 Redis 的方法,包括项目创建、依赖选择等,以及使用 Redis 做缓存的不同注解应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redis

一、Redis 的读并发量太大怎么办?

使用读写分离
10w/s
8w/s
在这里插入图片描述

二、单机版的Redis 挂掉怎么办?

在这里插入图片描述

三、需要写并发又要安全

之前我们只有一个主机,写的并发量有限,使用多个主机来做
在这里插入图片描述
在redis 3.0 后,官方发布了集群方案
3.1 中心化
在这里插入图片描述
3.2 去中心化
在这里插入图片描述

四、Redis集群的执行流程分析

16384个key
在这里插入图片描述

五、搭建集群

http://redis.cn/topics/cluster-tutorial.html
5.1 集群原理
去中心化
5.2 集群的规划
在这里插入图片描述

5.3 搭建过程
5.3.1 新建文件夹

在这里插入图片描述
5.3.2 准备一个服务端程序
在这里插入图片描述
5.3.3准备6个redis的配置文件
在这里插入图片描述
Redis-1

daemonize yes
bind 0.0.0.0
port 7000
# 开启集群
cluster-enabled yes
# 集群的配置文件,该文件自动生成
cluster-config-file nodes-1.conf
# 集群的超市时间
cluster-node-timeout 5000
# 打开aof 持久化
appendonly yes

Redis-2

daemonize yes
bind 0.0.0.0
port 7001
# 开启集群
cluster-enabled yes
# 集群的配置文件,该文件自动生成
cluster-config-file nodes-2.conf
# 集群的超市时间
cluster-node-timeout 5000
# 打开aof 持久化
appendonly yes

Redis-3

daemonize yes
bind 0.0.0.0
port 7002
# 开启集群
cluster-enabled yes
# 集群的配置文件,该文件自动生成
cluster-config-file nodes-3.conf
# 集群的超市时间
cluster-node-timeout 5000
# 打开aof 持久化
appendonly yes

Redis-4

daemonize yes
bind 0.0.0.0
port 7003
# 开启集群
cluster-enabled yes
# 集群的配置文件,该文件自动生成
cluster-config-file nodes-4.conf
# 集群的超市时间
cluster-node-timeout 5000
# 打开aof 持久化
appendonly yes

Redis-5

daemonize yes
bind 0.0.0.0
port 7004
# 开启集群
cluster-enabled yes
# 集群的配置文件,该文件自动生成
cluster-config-file nodes-5.conf
# 集群的超市时间
cluster-node-timeout 5000
# 打开aof 持久化
appendonly yes

Redis-6

daemonize yes
bind 0.0.0.0
port 7005
# 开启集群
cluster-enabled yes
# 集群的配置文件,该文件自动生成
cluster-config-file nodes-6.conf
# 集群的超市时间
cluster-node-timeout 5000
# 打开aof 持久化
appendonly yes

在这里插入图片描述

5.3.4 同时启动所有的redis

在这里插入图片描述
在这里插入图片描述
5.3.5 使用脚本创建集群(分配槽)
在这里插入图片描述
使用该脚本就可以创建

使用docker 下载redis-trib的镜像运行
A: 下载镜像
docker pull inem0o/redis-trib
B:创建集群

docker run -t -i inem0o/redis-trib create --replicas 1 192.168.231.143:7000 192.168.231.143:7001 
192.168.231.143:7002 192.168.231.143:7003 192.168.231.143:7004 192.168.231.143:7005 

-t(打开bash)
-i(能输入命令)
在这里插入图片描述
在这里插入图片描述
5.3.6 测试redis集群的路由效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、使用Redis 做缓存和位集合

Map->Redis
@annotation(注解)
6.1 配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxIdle" value="20"></property>
		<property name="maxTotal" value="25"></property>
		<property name="minIdle" value="10"></property>
	</bean>
	
	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
		<constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>
		<constructor-arg name="host" value="192.168.231.143"></constructor-arg>
		<constructor-arg name="port" value="6379"></constructor-arg>
	</bean>
</beans>

6.2 改造代码

@Component
@Aspect
public class CacheAspect {

//	private static Map<String,Object> caches = new HashMap<>();
	
	@Autowired
	private JedisPool pool;
	
	private static final String ALL_MENU_LABEL = "alll-menu-data"; 
	
	@Around("execution (* com.sxt.service.impl.MenuServiceImpl.loadAllMenu())")
	public Object cache(ProceedingJoinPoint ponit) {
		Jedis jedis = pool.getResource();
		if(jedis.exists(ALL_MENU_LABEL)) {
			String menuJson = jedis.get(ALL_MENU_LABEL);
			List<Menu> menu = JSON.parseArray(menuJson, Menu.class);
			return menu ;
		}
//		if(caches.containsKey(ALL_MENU_LABEL)) {
//			System.out.println("缓存里面有");
//			return caches.get(ALL_MENU_LABEL);
//		}
		
		Object result = null ; 
		try {
			System.out.println("执行真实方法的调用");
			result = ponit.proceed(ponit.getArgs()); // 在此实现了真实方法的调用
//			放入缓存
//			caches.put(ALL_MENU_LABEL, result);
			jedis.set(ALL_MENU_LABEL,JSON.toJSONString(result));
		} catch (Throwable e) {
			e.printStackTrace();
		}

		return result;
	}
}

6.3 测试
开启redis(单机) mysql zk
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

七、在SpringBoot 里面使用Redis

7.1 新建项目
7.2 选择依赖
在这里插入图片描述
7.3 spring boot 是一个什么东西

Spring 项目创建的脚手架,里面充分利用习惯大于约束的原则来初始化一些常用对象,使得Spring的开发变得更简洁,可以做到不用写配置文件(或写很少的配置文件就可以实现功能)。
7.4 spring boot 如何加载默认的对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为什么加上redis 的依赖就能创建redis的操作对象,不加就不创建?

7.5 Spring boot的核心
包扫描的使用(扫描文件夹)
7.6 修改配置文件

spring:
  redis:
    host: 192.168.231.143
    port: 6379
    pool:
      max-active: 25 
      max-idle: 20
      min-idle: 10

八、Spring boot 操作Redis

8.1 StringRedisTemplate(使用最多)
在这里插入图片描述

/**
 * spring 和junit 整合的测试框架
 * @author CodeLab
 *
 */
/**
 * 可以把是sping boot的项目启动起来
 * @author CodeLab
 *
 */
@RunWith(SpringRunner.class) 
@SpringBootTest
public class StringRedisTemplateTest {
	
	@Autowired
	private StringRedisTemplate redisTemplate;
	
	/**
	 * redis数据类型为String的操作
	 */
	@Test
	public void testString() {
		// 操作String类型
		ValueOperations<String, String> opsValue = redisTemplate.opsForValue();
		// 给redis 里面set 一个key
		opsValue.set("boot", "spring-boot"); // k -v 都是String
		// 从redis 里面获取key
		String value = opsValue.get("boot");
		System.out.println(value);
		// 从redis 里面或多个key
		List<String> asList = Arrays.asList("boot","alll-menu-data");
		List<String> mulitValues = opsValue.multiGet(asList);
		System.out.println(mulitValues);
//		 redis的自动增长
		Long increment = opsValue.increment("boot-incr", 2);// delta 可以+ 任意的数(步长)
		System.out.println(increment);
	}

	@Test
	public void testHash() {
		HashOperations<String, Object, Object> opsForHash = redisTemplate.opsForHash();
		// hset
		opsForHash.put("object-1", "name", "ltd"); // 后面的2 个参数都是object,但是只支持String 类型
		opsForHash.put("object-1", "age", "27"); // 后面的2 个参数都是object,但是只支持String 类型
		opsForHash.put("object-1", "sex", "man"); // 后面的2 个参数都是object,但是只支持String 类型
		Object value = opsForHash.get("object-1", "sex");
		System.out.println(value);
		// 取多个值
		List<Object> multiGet = opsForHash.multiGet("object-1", Arrays.asList("name","sex"));
		System.out.println(multiGet);
	}
	
	@Test
	public void testZset() {
		ZSetOperations<String, String> opsForZSet = redisTemplate.opsForZSet();
		// 放到zset集合里面
		opsForZSet.add("lol", "ltd", 2500);
		opsForZSet.add("lol", "lz", 0);
		opsForZSet.add("lol", "ln", 1400);
		opsForZSet.add("lol", "ll", -10);
		opsForZSet.add("lol", "lt", 2700);
		Set<String> rangeAsc = opsForZSet.range("lol", 0, 2); // 通过排序取值 ll lz ln
		System.out.println(rangeAsc);
		Set<String> reverseRange = opsForZSet.reverseRange("lol", 0, 2);//lt ltd ln
		System.out.println(reverseRange);
		Set<TypedTuple<String>> tuples = new HashSet<ZSetOperations.TypedTuple<String>>();
		tuples.add(new DefaultTypedTuple<String>("ltd", 1000.00) );
		tuples.add(new DefaultTypedTuple<String>("lv", 1200.00) );
		tuples.add(new DefaultTypedTuple<String>("lz", 2900.00) );
		tuples.add(new DefaultTypedTuple<String>("lt", 100.00) );
		// 若redis 存在该key ,则需要数据类型相同,不然报错
		opsForZSet.add("dnf", tuples);
		
	}

}

8.2 RedisTemplate(扩展String类型)
在这里插入图片描述
若不设置序列化规则,它将使用JDK自动的序列化将对象转换为字节,存到Redis 里面
在这里插入图片描述
可以设置序列化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/**
 * spring 和junit 整合的测试框架
 * @author CodeLab
 *
 */
/**
 * 可以把是sping boot的项目启动起来
 * @author CodeLab
 *
 */
@RunWith(SpringRunner.class) 
@SpringBootTest
public class RedisTemplateTest {
	
	@Autowired
	private RedisTemplate<Object, Object> redisTemplate ; // 因为创建RedisTemplate 没有使用泛型信息来创建,泛型 本质还是Object,只不过泛型能自动推断并强转


   
	@Test
	public void testString() {
		redisTemplate.setKeySerializer(new  StringRedisSerializer()); // key的序列化使用String 类型来完成 因为key 很多时候都是一个字符串
		redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // 优先没有泛型的,因为有泛型后,它支持的数据类型就定了
		ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue();
//		valueOperations.set("boot-redis", "boot-value"); //对象->字符串 json
		User user = new User(1, "ltd", "124", "liangtiandong@live.com", "xx.jpg");
//		 KEY : com.sxt.domain.User:1 
//		com.fasterxml.jackson.databind.JsonSerializer 没有依赖jackson 之前大家可能使用spring-boot-web,这里面会自动依赖
		valueOperations.set(User.class.getName()+":"+user.getId(), user);
		
		// 若该对象的强转转换,则redis 内部会使用JackSon 的工具将字符串-> 转换为java 对象 ,那jackson 转换为对象时,需要一个对象的类型 ,其实它已经自动对象的类型了"@class": "com.sxt.domain.User",
		User object = (User)valueOperations.get(User.class.getName()+":"+user.getId());
	    System.out.println(object.getUsername()+":"+object.getIcon());
	}
	
	/**
	 * hash
	 */
	@Test
	public void testHash() {
		redisTemplate.setKeySerializer(new  StringRedisSerializer()); // key的序列化使用String 类型来完成 因为key 很多时候都是一个字符串
		redisTemplate.setHashKeySerializer(new  StringRedisSerializer());
		redisTemplate.setHashValueSerializer(new  StringRedisSerializer()); // 若都是string 则和StringRedisTempalte一样了
		HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();
		opsForHash.put("redis-hash", "prop1", "value");
	}

}

8.3 RedisKeyValueTemplate(很少用)
在这里插入图片描述

数据库的存储引擎
在这里插入图片描述
InnoDb
不保存数据的id,我们的做法,只是将数据存入redis ,并没有set 集合

MyISAM,不仅仅保存数据,还把当前表里面存在id 也放在一个文件里面(set集合)
使用一个文件专门保存已经存入表里面的数据id

我想查询一个没有的数据
Id = 47 没有?MyISAM
我想统计当前表里面有多少个数据?
Innodb需要扫描全表
MyISAM:得到一个总条数

* spring 和junit 整合的测试框架
 * @author CodeLab
 *
 */
/**
 * 可以把是sping boot的项目启动起来
 * @author CodeLab
 *
 */
@RunWith(SpringRunner.class) 
@SpringBootTest
public class KeyValueRedisTemplateTest {

	@Autowired
	private RedisKeyValueTemplate redisKeyValue;

	@Autowired
	private StringRedisTemplate strRedis;
	
	/**
	 * 别人写的有set 集合,有的好处就是查询一个不存在的对象,和统计数据的个数,已经获取前几条比较高效
	 */
	@Test // 和数据库的操作很像 可以根据对象的属性来查找数据
	public void testCurd() {
		User user = new User(1, "mjm", "1231", "4e12421", "r23r.jpg");
		redisKeyValue.insert(user); //redis 如何存储一个对象?map - > hash 最合适 属性 属性值
		//        对象->Map集合
		//		redisKeyValue.delete(objectToDelete);
		//		redisKeyValue.findById(id, type);
	}
	
	@Test
	public void testFind() {
		User findById = redisKeyValue.findById(3, User.class); // 1 先查询set(com.sxt.domain.User)集合里面是否有3 ,有:拼接key(com.sxt.domain.User:3) ,获取数据,没有,直接结束
		System.out.println(findById);
	}
	
	@Test
	public void testDelete() {
		User delete = redisKeyValue.delete(3, User.class);
		System.out.println(delete.getId());
	}
	
	@Test
	public void testFindAll() {
		Iterable<User> findInRange = redisKeyValue.findInRange(0, 2, User.class); // 从set 集合获取keys ,使用keys 查询
		Iterator<User> iterator = findInRange.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next().getId());
		}
	}

	/**
	 *自己写的存储对象。没有那个set 集合
	 */
//	@Test
	public void testInsertObject() {
		User user = new User(3, "mjm", "1231", "4e12421", "r23r.jpg");
		HashOperations<String, Object, Object> opsForHash = strRedis.opsForHash();
		String key = user.getClass().getName()+":"+user.getId();
		Map<String, Object> map = new HashMap<String,Object>();
		map = object2Map(user);
		opsForHash.putAll(key, map);
	}

	private static Map<String, Object> object2MapXiaoWen(Object key) {
		HashMap<String,Object> map = new HashMap<String,Object>();
		String objectJson = JSON.toJSONString(key);
		Map parseObject = JSON.parseObject(objectJson, Map.class);
		return parseObject;
	}
	
	private static Map<String, Object> object2Map(Object object){
		Class<?> clazz = object.getClass();
		Map<String,Object> map = new HashMap<String,Object>();
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field field : declaredFields) {
			field.setAccessible(true);
			String key = field.getName();
			Object value = null ;
			try {
				value = String.valueOf(field.get(object));
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
			map.put(key, value);
		}
		return map ;
	}
	
	public static void main(String[] args) {
		User user = new User(2, "mjm", "1231", "4e12421", "r23r.jpg");
		Map<String, Object> object2Map = object2Map(user);
		object2Map.forEach((k,v)->{
			System.out.println("k:"+k+"-----"+"v:"+v);
		});
	}
}


8.4 集群的额外操作

@Test
	public void testCluster() {
		ClusterOperations<String, String> opsForCluster = redisTemplate.opsForCluster();
		opsForCluster.shutdown(new RedisClusterNode("192.168.231.143", 7000));
	}

九、使用Redis 做缓存

9.1 在boot 里面使用Redis 做缓存
在这里插入图片描述
9.2 在你需要添加缓存的方法上面,添加注解
在这里插入图片描述
9.2.1 cacheable(使用最多)
就是一个完整的切面,先从缓存里面查询,若没有,查询数据库,把结果放入缓存里面
在这里插入图片描述

9.2.2 cacheput(解决脏读)
每次调用该方法,都会执行数据库查询,并且把缓存的值覆盖
在这里插入图片描述
在这里插入图片描述
9.2.3 cachEvict(解决脏读)
直接把缓存里面值删除
在这里插入图片描述
9.2.4 cacheconfig(全局的配置缓存)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张子又

感觉有用就打赏点吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值