Redis相关问题
-
对于Redis理解
Redis 简介https://redis.com.cn/redis-intro.html
一个开源的基于内存的非关系型数据库,也叫做键值对数据库。读写效率非常高,主要做缓存数据。将频繁访问的数据存入内存中,提高读写性能。
- 优点:
- 高性能:Redis将数据存储在内存中,具有非常快速的读写速度。它采用高效的数据结构和算法,在处理大量请求时表现出色,适合于需要快速响应和处理大量并发请求的场景。
- 丰富的数据结构:Redis支持多种数据结构,如字符串、哈希、列表、集合和有序集合等。这些数据结构可以满足不同场景和需求的存储和操作要求,提供了更灵活的数据模型。
- 持久化:Redis支持将数据持久化到硬盘上,以便在服务器重启后进行恢复。它提供了两种持久化方式:快照(RDB)和日志(AOF)。
- 高可用性:Redis支持主从复制和哨兵机制,可以实现数据的自动备份和故障转移,提供了高可用性和数据冗余。
- 发布/订阅:Redis实现了发布/订阅模型,允许多个客户端通过订阅主题来接收消息。这在实时消息推送和事件驱动的应用程序中非常有用。
- 分布式缓存:Redis经常被用作分布式缓存系统,可以减轻后端数据库的负载,提高应用程序的性能和响应速度。
- 缺点:
- 有限的存储容量:Redis的存储容量受限于服务器内存的大小,相比磁盘型数据库如MySQL,存储容量较小。因此,对于大规模数据的持久化和长期存储,Redis不如磁盘型数据库适合。
- 单线程限制:Redis采用单线程模型,虽然能够有效利用CPU和网络资源,但在处理大量并发写操作时可能会成为瓶颈。对于大规模并发写的场景,需要谨慎考虑Redis的性能瓶颈。
- 持久化配置复杂:Redis的持久化配置相对复杂,需要根据不同需求选择合适的持久化方式(RDB或AOF),并进行相应的设置和调优。
- 缺乏复杂查询支持:相对于关系型数据库如MySQL,Redis的查询功能较为有限。它主要支持简单的键值对存取和基本的数据结构操作,不适用于复杂的查询和数据关联。
-
Redis常用命令
官方手册 redis 命令手册https://redis.com.cn/commands.html
-
Redis数据结构及使用场景
- 字符串:存储单个值的最基本数据结构,常用于缓存、计数器、分布式锁等场景
- 列表:有序可重复的字符串集合,支持在两端进行插入、删除等操作,通常用于实现队列、栈等数据结构。
- 集合:无序且唯一的字符串集合,支持并集、交集、差集等操作,适用于存储唯一值,并进行快速的集合运算。
- 有序集合:在集合的基础上为每个成员关联一个分数,使得集合中的成员可以按照分数进行排序,适用于排行榜、范围查询等场景。
- 哈希:存储字段和值的映射关系,适用于存储对象、用户信息等具有多个属性的数据。
- 地理信息:主要是Geo(Geospatial)数据结构,它是一种特殊的有序集合(Sorted Set),用于存储地理位置信息。
-
Redis相关面试题
- Redis的数据结构有哪些?
Redis提供了五种基本数据结构:字符串、哈希、列表、集合和有序集合,以及其他数据类型如地理位置(Geo)和HyperLogLog等。 - Redis能否替代关系型数据库?
虽然Redis提供了很多有用的功能,但它并不能完全替代关系型数据库。Redis最适合存储非关键数据,例如会话数据、缓存数据、计数器、排名列表等数据。对于复杂的事务支持,关系型数据库仍然是一个更好的选择。 - Redis中如何实现分布式锁?
可以使用Redis的SETNX命令实现分布式锁。当多个客户端同时尝试获取锁时,只有一个客户端能成功获取并获得锁。获取锁后,客户端可以执行一段代码块,并在完成后释放锁。 - Redis如何处理数据持久化?
Redis提供两种方式来进行数据持久化:RDB和AOF。RDB是将某一时刻的数据集快照存储到磁盘上,而AOF则是将每个命令追加到一个文件中。RDB适用于备份和恢复整个数据集,而AOF适用于更高级的数据可靠性。 - 如何通过Redis实现缓存?
可以使用Redis作为缓存来提升应用程序的性能。将经常访问的数据存储在Redis中,可以减少对关系型数据库的访问次数,从而提高系统吞吐量。常见的缓存策略包括简单的键值对缓存、LRU缓存和时间到期缓存。
Redis代码实操
-
添加依赖
<!-- 在 Spring Boot 项目中集成 Redis 的相关依赖项 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Hutool 工具库的小工具包,用来转化类型 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.15</version> </dependency>
-
配置文件
在application.properties配置文件设置
spring.redis.host=127.0.0.1 spring.redis.port=6379
小问题:可能会出现无法自动装配。找不到 'RedisConnectionFactory' 类型的 Bean。的警告
需要添加 @SuppressWarnings("all") 告知编译器忽略特定类型的警告信息
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import java.io.Serializable; @Configuration public class RedisConfiguration { @Bean @SuppressWarnings("all") public RedisTemplate<String, Serializable> redisTemplate( RedisConnectionFactory redisConnectionFactory ) { RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); // RedisTemplate 的键(key)和值(value)的序列化器 redisTemplate.setKeySerializer(RedisSerializer.string()); redisTemplate.setValueSerializer(RedisSerializer.json()); // 哈希键(hash key)和哈希值(hash value)的序列化器 redisTemplate.setHashKeySerializer(RedisSerializer.string()); redisTemplate.setHashValueSerializer(RedisSerializer.json()); return redisTemplate; } }
-
缓存预热
在程序启动的时候将 SQL 的数据写入到 Redis 中
ApplicationRunner
是 Spring Boot 提供的一个接口,用于在应用程序启动后自动执行一些初始化操作。它定义了一个 run
方法,该方法在应用程序启动完成后被调用。
import com.example.adminarea.service.RoomService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class RoomCatchPreload implements ApplicationRunner { @Autowired RoomService service; @Override public void run(ApplicationArguments args) throws Exception { service.rebuildCatch(); } }
-
计划任务
必须在主启动类上加上 @EnableScheduling 开启
Spring Boot 默认不开启计划任务
fixedRate:执行频率,以【上一次开始执行的时间】来计算下一次的执行时间,以毫秒为单位
fixedDelay:执行间隔,以【上一次执行结束的时间】来计算下一次的执行时间,以毫秒为单位
cron:使用cron表达式来配置,cron表达式的值是一个字符串,由6~7个域组成,各域之间使用空 格分隔
import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component @Slf4j public class RoomCatchSchedule { // fixedRate执行频率,以上一次开始 // fixedDelay执行间隔,以下一次开始 // cron表达式 <秒> <分钟> <小时> <日期> <月份> <星期> @Scheduled(fixedDelay = 60 * 60 * 1000) // 1小时 public void rebuildCache() { log.debug("计划任务执行!"); } }
-
基本操作
-
自动装配
@Autowired RedisTemplate<String, Serializable> redisTemplate;
-
字符串类型存取
// 存字符串数据 @Test void setValue() { ValueOperations<String, Serializable> operations = redisTemplate.opsForValue(); operations.set("username","专家1"); System.out.println("存入成功!"); }// 取字符串数据 @Test void getValue() { ValueOperations<String,Serializable> operations = redisTemplate.opsForValue(); Serializable value = operations.get("username"); System.out.println("value = " + value); }
-
自定义对象存取
// 存入自定义对象 @Test void setObjectValue() { Room room = new Room(); room.setName("测试"); room.setFloor(1); ValueOperations<String,Serializable> operations = redisTemplate.opsForValue(); operations.set("Room",room); System.out.println("存入成功!"); } // 取出自定义对象 @Test void getObjectValue() { ValueOperations<String,Serializable> operations = redisTemplate.opsForValue(); Serializable value = operations.get("Room"); System.out.println("value = " + value); }
-
列表数据存取
// 存列表数据 @Test void SetRoomList() { // 搞假数据进行测试 List<Room> list = new ArrayList<>(); for (int i=1;i<=8;i++) { Room room = new Room(); room.setId(i+0L); room.setName("测试号"); list.add(room); } ListOperations<String, Serializable> opsForList = redisTemplate.opsForList(); // 将假数据存入 String key = "RoomListSS"; for (Room room : list) { opsForList.rightPush(key,room); } System.out.println("存入成功!"); }// 读列表数据 @Test void GetRoomList() { String key = "RoomListSS"; ListOperations<String, Serializable> opsForList = redisTemplate.opsForList(); // 得到长度 Long size = opsForList.size(key); System.out.println("size = " + size); // 0 到 -1 表示全部 long start = 0; long end = -1; List<Serializable> list = opsForList.range(key,start,end); for (Serializable serializable : list) { System.out.println(serializable); } }
-
hash类型存取
// 存 hash 类型的数据 @Test void hashPutAll() { String key = "test:room:hash2"; Room room = new Room(); room.setId(1L); room.setName("hash测试测试再次测试!!"); /*HashMap<String, Object> map = new HashMap<>(); map.put("id",room.getId()); map.put("name",room.getName());*/ Map<String, Object> map = BeanUtil.beanToMap(room); // 工具包作用直接转为 map 对象 HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); hash.putAll(key,map); }// 读取Redis中的hash数据 @Test void hashEntries() { String key = "test:room:hash2"; HashOperations<String, Object, Object> opsForHash = redisTemplate.opsForHash(); Map<Object, Object> entries = opsForHash.entries(key); System.out.println("entries = " + entries); // 小工具依赖包作用,将 map 转化为对象 Room room = BeanUtil.mapToBean(entries, Room.class, true); System.out.println("room = " + room); }// 修改Redis中的hash数据 @Test void hasPut() { String key = "test:room:hash2"; HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); hash.put(key,"name","修改"); Map<Object, Object> entries = hash.entries(key); System.out.println("entries = " + entries); }
-
获取全部
// 获取全部 @Test void keys() { String pattern = "*"; Set<String> keys = redisTemplate.keys(pattern); System.out.println("keys = " + keys); }
-
删除单个
// 删除单个 @Test void delete() { String key = "username"; Boolean delete = redisTemplate.delete(key); System.out.println("delete结果 = " + delete); }
-
删除多个
// 删除多个 @Test void deletes() { Set<String> keys = new HashSet<>(); keys.add("username"); keys.add("Room"); Long delete = redisTemplate.delete(keys); System.out.println("成功delete几条 = " + delete); }