以下部分内容由AI生成,再添加自己的理解,仅供参考与了解记录
Redis是一个开源的高性能键值对数据库,支持多种数据结构,如字符串(String
)、哈希(Hash
)、列表(List
)、集合(Set
)和有序集合(Sorted Set
)等。
核心原理
1. 单线程模型
redis使用单线程处理命令(核心逻辑),避免了多线程竞争问题。通过非阻塞I/O多路复用监听多个客户端连接,高效处理请求。所有操作原子性执行,无需加锁。
2. 持久化机制
RDB(Redis DataBase):
原理:RDB是redis的默认持久化方式,会将redis在内存中的数据生成快照文件保存到硬盘上。快照文件是一个二进制文件,包含了某个时间点内redis的所有数据。
优点: RDB文件是一个紧凑的二进制文件,体积小,便于备份和恢复;生成RDB文件时,redis主进程可以继续处理命令请求,不会长时间阻塞主进程。
缺点:由于是定期生成快照,如果redis意外宕机,可能会丢失最近一次快照生成后的数据;对于大型数据集,fork子进程可能会比较耗时,影响性能。
AOF(Append Only File):
原理:AOF日志以追加的方式记录redis执行的每条写命令,当redis重启时会顺序执行一遍AOF文件中的命令来恢复原始的数据。
开启方式:默认是关闭的,需要通过修改redis.config
配置文件中的appendonly
参数为yes来开启。
配置项:
appendonly:控制AOF日志同步的频率,有三个合法值always、everysec、no。always表示每个写命令执行完,立马同步磁盘,性能较低但数据安全性高;everysec表示每秒同步一次,性能和安全都比较中庸,是推荐的方式;no表示让操作系统来决定何时同步磁盘,性能较好但不安全。
auto-aof-rewrite-min-size:设置AOF重写的最小文件尺寸,只有大于此尺寸时才会触发重写。
auto-aof-rewrite-percentage:设置当前AOF文件增长到上一次rewrite之后大小的百分比,达到该比例时触发重写。
优点:
可以提供更高的数据安全性,即使Redis意外宕机,最多只会丢失一秒内的数据。
AOF日志文件是可读的文本文件,方便进行数据恢复和分析。
缺点:
AOF日志文件会持续增大,需要定期进行AOF重写来对日志进行瘦身。
对于变更操作比较密集的场景,可能会导致磁盘IO的负荷加重。
混合持久化
原理:结合了RDB和AOF的优点,在重启时优先加载AOF文件来恢复原始的数据,当AOF文件过大时,会采用类似RDB快照的方式基于Copy On Write全量遍历内存中数据,然后逐个序列到AOF文件中。
优点:
既具有RDB的快速加载特性,又能保证数据的持久性和安全性。
在AOF重写期间,可以利用RDB文件快速恢复数据。
缺点:实现相对复杂,需要同时维护RDB和AOF两种持久化机制。
3. 数据结构与内存管理
所有数据存储在内存中,通过哈希表(全局字典)组织。支持多种数据结构,针对不同厂家优化内存分配(如ziplist、intset等紧凑结构)。
4. 过期策略与内存淘汰
惰性删除:访问时检查过期时间,过期则删除。
定期删除:随机抽查部分key,清理过期数据。
内存不足时触发淘汰策略(如LRU、LFU、随机删除等)。
5. 高可用与扩展
主从复制:主节点异步同步数据到从节点,实现读写分离和故障恢复。
哨兵模式:监控节点状态,自动故障转移。
Cluster模式:分片存储,支持水平扩展,数据分散在多个节点。
说了一大堆,都是用来面试造飞机的。。。
关于redis的数据类型,来看些例子吧,基于Spring boot的示例。
前置配置
1. 依赖:pom.xml
添加依赖:我用的Spring Boot版本是2.3.4.RELEASE,JDK11
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
如果你是Spring boot 2.2以后,@Test
需要使用org.junit.jupiter.api.Test
的,pom.xml
文件中引入
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-apiartifactId>
<version>5.7.1version>
dependency>
不要加
,加了之后还是会无法引入@Test
,具体原因还没搞明白。这样在测试类中使用@Autowired
注解引入的类就不会为null
完整pom.xml
内容
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-testartifactId>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-apiartifactId>
<version>5.7.1version>
dependency>
dependencies>
2. 配置:application.properties
配置 Redis 连接:记得启动redis服务哦
## spring boot 2.x及以上使用spring.data.redis;1.x版本使用spring.redis
spring.data.redis.host=localhost
spring.data.redis.port=6379
## 默认没有密码,有密码的话自己填上即可
spring.data.redis.password=
3. RedisTemplate
配置(解决key/value序列化问题):
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Key 使用字符串序列化
template.setKeySerializer(new StringRedisSerializer());
// Value 使用 JSON 序列化
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
代码示例
1. 字符串(String)
场景:缓存数据、计数器
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;
@SpringBootTest
public class StringExampleTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void testStringOperations() {
ValueOperations<String, Object> ops = redisTemplate.opsForValue();
// 设置值
ops.set("user:1:name", "Alice");
// 设置值并指定过期时间
ops.set("temp:session", "token123", 60, TimeUnit.SECONDS);
// 获取值
String name = (String) ops.get("user:1:name");
System.out.println("获取用户名: " + name); // 输出: Alice
// 自增操作
Long count = ops.increment("article:1001:views");
System.out.println("文章浏览量: " + count); // 输出: 1 (首次调用返回1)
}
}
2. 哈希(Hash)
场景:存储对象(如用户信息)
@SpringBootTest
public class HashExampleTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void testHashOperations() {
HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
// 存储用户对象
Map<String, Object> user = new HashMap<>();
user.put("name", "Bob");
user.put("age", 25);
ops.putAll("user:2", user);
// 获取单个字段
String name = (String) ops.get("user:2", "name");
System.out.println("用户姓名: " + name); // 输出: Bob
// 修改字段值
ops.put("user:2", "age", 26);
// 获取所有字段
Map<String, Object> userData = ops.entries("user:2");
System.out.println("完整用户信息: " + userData);
// 输出: {name=Bob, age=26}
}
}
3. 列表(List)
场景:消息队列、最新动态
@SpringBootTest
public class ListExampleTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void testListOperations() {
ListOperations<String, Object> ops = redisTemplate.opsForList();
// 左推元素(插入队列头部)
ops.leftPushAll("queue:tasks", "task1", "task2");
// 右推元素(插入队列尾部)
ops.rightPush("queue:tasks", "task3");
// 获取列表长度
Long size = ops.size("queue:tasks");
System.out.println("队列长度: " + size); // 输出: 3
// 右弹元素(从尾部取出)
Object task = ops.rightPop("queue:tasks");
System.out.println("处理任务: " + task); // 输出: task3
// 获取范围元素
List<Object> tasks = ops.range("queue:tasks", 0, -1);
System.out.println("剩余任务: " + tasks); // 输出: [task2, task1]
}
}
4. 集合(Set)
场景:标签系统、共同好友
@SpringBootTest
public class SetExampleTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void testSetOperations() {
SetOperations<String, Object> ops = redisTemplate.opsForSet();
// 添加元素
ops.add("user:1:tags", "java", "redis", "database");
ops.add("user:2:tags", "python", "redis", "AI");
// 获取所有元素
Set<Object> tags = ops.members("user:1:tags");
System.out.println("用户1的标签: " + tags);
// 输出: [java, redis, database]
// 求交集(共同标签)
Set<Object> commonTags = ops.intersect("user:1:tags", "user:2:tags");
System.out.println("共同标签: " + commonTags); // 输出: [redis]
// 删除元素
ops.remove("user:1:tags", "database");
}
}
5. 有序集合(Sorted Set)
场景:排行榜、优先级队列
@SpringBootTest
public class SortedSetExampleTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void testSortedSetOperations() {
ZSetOperations<String, Object> ops = redisTemplate.opsForZSet();
// 添加元素(分数用于排序)
ops.add("leaderboard", "player1", 100);
ops.add("leaderboard", "player2", 90);
ops.add("leaderboard", "player3", 95);
// 获取前两名(按分数降序)reverseRange
Set<Object> topPlayers = ops.reverseRange("leaderboard", 0, 1);
System.out.println("排行榜前两名: " + topPlayers);
// 输出: [player1, player3]
// 获取后两名(按分数升序)range
Set<Object> lastPlayers = ops.range("leaderboard", 0, 1);
System.out.println("排行榜后两名: " + lastPlayers);
// 输出: [player2, player3]
// 获取某个玩家的分数
Double score = ops.score("leaderboard", "player3");
System.out.println("player3的分数: " + score); // 输出: 95.0
// 根据分数范围查询
Set<ZSetOperations.TypedTuple<Object>> players = ops.rangeByScoreWithScores(
"leaderboard", 90, 95
);
players.forEach(tuple ->
System.out.println(tuple.getValue() + ": " + tuple.getScore())
);
// 输出: player2: 90.0 和 player3: 95.0
}
}
大致就是这样,多加练习哦,实践是检验真理的唯一标准。多动手才能有收获。