Redis快速入门
- 数据结构特点:Redis采用键值对(key-value)存储结构,与传统关系型数据库的表结构有本质区别
- 存储方式:
- 基础存储:将对象字段拆分为多个键值对(如用户ID、name、age分别存储)
- 优化存储:可将多个字段组合成JSON字符串作为value,以ID为key存储
- 值类型多样性:value不仅支持字符串,还支持集合(有序/无序)、哈希表等复杂数据结构
- NoSQL特性:属于非关系型数据库,没有表结构和约束的概念
关系型与非关系型的差异
数据结构
- 关系型数据库:
- 结构化特征: 数据必须遵循预定义的表结构和约束条件
- 约束示例: 主键约束(ID)、唯一约束(name)、无符号约束(age)、数据类型约束(bigint(20), varchar(32), int(3))
- 修改限制: 数据量大时修改表结构可能导致锁表,影响业务逻辑
- NoSQL数据库:
- 非结构化特征: 数据结构约束松散,常见四种类型:
- 键值型(如Redis):key可自定义,value类型灵活
- 文档型(如MongoDB):JSON格式存储,字段可任意增减
- 图型(如Neo4j):数据作为节点,维护节点间关系
- 列型(如HBase):宽列存储
- 修改灵活性: 数据结构可随时调整,影响较小
- 非结构化特征: 数据结构约束松散,常见四种类型:
数据关联
- 关系型数据库:
- 关联机制: 通过外键建立表间关系(如订单表关联用户表和商品表)
- 优势: 节省存储空间,数据库自动维护引用完整性
- 限制: 删除被引用数据时会受到约束
- NoSQL数据库:
- 无关联特性: 数据间无直接关联维护
- 替代方案: 使用JSON嵌套方式描述关系(如用户文档内嵌订单数组)
- 缺点: 可能导致数据冗余,关联需程序员手动维护
查询方式
- 关系型数据库:
- 标准语法: 统一SQL语法(如SELECT id, name, age FROM tb_user WHERE id = 1)
- 优势: 语法通用,适用于所有关系型数据库
- NoSQL数据库:
- 多样性:
- Redis: 命令式(get user:1)
- MongoDB: 函数式(db.users.find({_id: 1}))
- Elasticsearch: RESTful API(GET http://localhost:9200/users/1)
- 特点: 语法简单但不统一,不同数据库需学习不同语法
- 多样性:
事务
- 关系型数据库:
- ACID特性: 原子性、一致性、隔离性、持久性
- 实现: 所有关系型数据库都支持完整ACID
- NoSQL数据库:
- BASE理论: 基本可用、软状态、最终一致性
- 限制: 无法完全保证ACID,适合对一致性要求不高的场景
数据库选择建议
- 关系型适用场景:
- 数据结构固定
- 对数据安全性和一致性要求高(如订单系统)
- NoSQL适用场景:
- 数据结构灵活多变
- 高性能查询需求
- 对一致性要求不高
- 最佳实践: 两者结合使用,关键数据用关系型,高频查询数据用NoSQL缓存
Redis的特征
1)键值型
- 数据结构:类似词典的键值对存储方式,通过key查找value
- 数据类型:支持字符串、集合(有序/无序)、哈希等近十种数据结构
- 操作特点:简单易用,功能丰富能满足多样化业务需求
2)单线程
- 执行机制:命令串行执行(Redis 6.0+仅网络请求处理部分多线程)
- 线程安全:命令具备原子性,不会出现执行中途被其他命令打断的情况
- 性能表现:单线程架构下仍保持低延迟和高速度
3)低延迟和速度快
- 核心原因:
- 基于内存:数据存储在内存而非磁盘,读写速度比磁盘高多个数量级
- IO多路复用:采用高效网络通信模型提升吞吐能力
- 优秀编码:C语言编写,代码质量获业界公认(标杆级开源项目)
- 性能对比:内存存储是Redis性能优于MySQL的最关键因素
4)数据的持久化
- 必要性:内存数据断电易失
- 解决方案:定期将数据从内存持久化到磁盘
- 优势:兼顾内存的高性能和磁盘的数据安全性
5)支持主从集群和分片集群
- 主从集群:
- 数据备份:从节点复制主节点数据
- 高可用:节点宕机时可快速恢复
- 读写分离:提升查询效率
- 分片集群:
- 水平扩展:将数据拆分存储到不同节点
- 容量突破:突破单机存储限制(如1TB数据分n份存储)
多语言客户端
- 支持语言:Java、Python、C等主流编程语言
- 生态优势:降低使用门槛,方便不同技术栈团队接入
Redis客户端
命令行客户端
- 基本语法:
redis-cli [options] [commands] - 常用options:
- -h 127.0.0.1:指定连接IP(默认本机)
- -p 6379:指定端口(默认6379)
- -a 密码:指定访问密码
- 交互模式:不指定commands时进入交互控制台
- 连接测试:
- ping命令返回pong表示连接正常
- 密码验证两种方式:
- 连接时直接加-a参数
- 连接后使用auth 密码命令验证
图形化客户端
开源
Redis命令
笔记内容5中存在基本的命令介绍
SortedSet
- 数据结构特性:
- 可排序的set集合,类似Java中的TreeSet但底层实现不同
- 每个元素带有score属性用于排序
- 底层实现:跳表(SkipList)+哈希表
- 核心特点:
- 可排序性:基于score值自动排序
- 元素唯一性:保证元素不重复
- 高效查询:哈希表保证O(1)查询复杂度
- 典型应用:排行榜功能实现(如topN查询)
常用命令
- 基础操作:
- ZADD key score member:添加/更新元素(需指定分数)
- ZREM key member:删除指定元素
- ZCARD key:获取元素总数
- 分数相关:
- ZSCORE key member:查询元素分数
- ZINCRBY key increment member:分数自增/减
- 范围查询:
- ZCOUNT key min max:统计分数区间元素数量
- ZRANGE key min max:按排名范围查询(默认升序)
- ZRANGEBYSCORE key min max:按分数范围查询
- 集合运算:
- ZDIFF/ZINTER/ZUNION:差集/交集/并集运算
- 排序控制:
- 默认升序排列,添加REV后缀可改为降序(如ZREVRANGE)
Redis的Java客户端
- 官网资源: Redis官网提供多种语言客户端支持,地址为https://redis.io/clients
- 语言覆盖: 包含ActionScript、C#、Java、Python等40+编程语言支持
- 主流选择: Java领域推荐使用Jedis、Lettuce、Redisson三个客户端
1)Jedis
- 命名特点: 方法名称与Redis命令完全一致(如set/get/mset等)
- 学习成本: 命令即方法,学习成本极低
- 线程安全: 实例线程不安全,多线程环境需使用连接池
- 使用场景: 早期广泛使用,适合简单业务场景
2)Lettuce
- 底层实现: 基于Netty高性能网络框架
- 编程模式: 支持同步/异步及响应式编程
- 线程安全: 原生支持线程安全
- 集群支持: 完善支持哨兵模式和集群模式
- Spring整合: Spring官方默认兼容的Redis客户端
3)Redisson
- 核心功能: 基于Redis实现分布式Java工具
- 数据结构: 提供分布式Map、队列、锁、信号量等
- 应用场景: 专为解决分布式环境下的数据共享问题
- 典型应用: 分布式锁实现的首选方案
Spring Data Redis
在 Java 生态中,Spring Data Redis 是 Spring 官方提供的一个用于简化 Redis 操作的框架,而 Lettuce、Jedis、Redisson 等是具体的 Redis 客户端实现。它们之间的关系可以总结为:
1. Spring Data Redis 的作用
Spring Data Redis 是一个抽象层,它封装了底层 Redis 客户端的操作,提供统一的 API(如
RedisTemplate、ReactiveRedisTemplate),开发者无需直接调用客户端的具体方法。它的核心功能包括:
- 统一操作接口:通过
RedisTemplate简化数据序列化、连接管理和命令调用。- Repository 支持:类似 Spring Data JPA,支持通过接口定义 Redis 数据访问逻辑。
- 自动化配置:与 Spring Boot 深度集成,通过配置文件(
application.yml)即可管理 Redis 连接。- 异常处理:将 Redis 客户端的异常转换为 Spring 的统一异常体系(如
DataAccessException)。
2. 与 Redis 客户端的关系
依赖关系: Spring Data Redis 底层依赖具体的 Redis 客户端(如 Lettuce 或 Jedis)来与 Redis 服务器通信。
- 默认客户端:Spring Boot 2.x+ 默认集成 Lettuce(因其异步和非阻塞特性)。
- 切换客户端:可以通过排除 Lettuce 并引入 Jedis 依赖,切换为 Jedis(如旧项目需要同步阻塞模型)。
抽象与实现: Spring Data Redis 定义了一套通用操作(如
ValueOperations、HashOperations),而具体命令的执行由底层客户端(Lettuce/Jedis)实现。 例如:当你调用redisTemplate.opsForValue().set("key", "value"),底层实际是通过 Lettuce 或 Jedis 发送SET命令。
Jedis
快速入门
1)引入依赖:
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>2)建立连接
新建一个单元测试类,内容如下:
private Jedis jedis;
@BeforeEach
void setUp() {
// 1.建立连接
// jedis = new Jedis("192.168.150.101", 6379);
jedis = JedisConnectionFactory.getJedis();
// 2.设置密码
jedis.auth("123321");
// 3.选择库
jedis.select(0);
}3)测试:
@Test
void testString() {
// 存入数据
String result = jedis.set("name", "虎哥");
System.out.println("result = " + result);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
void testHash() {
// 插入hash数据
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");
// 获取
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}4)释放资源
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用Jedis连接池代替Jedis的直连方式。
package com.heima.jedis.util;
import redis.clients.jedis.*;
public class JedisConnectionFactory {
private static JedisPool jedisPool;
static {
// 配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
// 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
jedisPool = new JedisPool(poolConfig, "192.168.150.101", 6379, 1000, "123321");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}SpringDataRedis
- 模块定位: SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis。
- 官网地址: https://spring.io/projects/spring-data-redis
- 核心特性:
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)
- 提供了RedisTemplate统一API来操作Redis
- 支持多种序列化方式(JDK、JSON、字符串等)
- 支持发布订阅模型、哨兵、集群等高级特性
- 支持响应式编程(基于Lettuce实现)
- 实现了分布式版本的JDK集合
- 版本迭代: 目前最新版本为2.6.0(发布于2021年12月11日),支持到2022年11月12日
- 企业实践: 企业通常使用稍旧版本(如2.3.x或2.4.x),但各版本间差异不大
Jedis的不足
- 数据类型限制: Jedis仅支持字符串和字节类型的数据存储,无法直接存储Java对象
- 序列化问题: 需要手动对复杂对象进行序列化才能存储
- 示例代码中jedis.set(“name”,“虎哥”)和jedis.hset(“user:1”,“name”,“Jack”)都只能操作字符串
- 连接管理: 需要手动处理连接建立、认证和数据库选择
- API组织方式: 按照Redis命令分组封装,避免方法臃肿
- opsForValue(): 返回ValueOperations对象,封装字符串操作
- opsForHash(): 返回HashOperations对象,封装哈希操作
- opsForList(): 返回ListOperations对象,封装列表操作
- opsForSet(): 返回SetOperations对象,封装集合操作
- opsForZSet(): 返回ZSetOperations对象,封装有序集合操作
- 设计优势: 各数据类型操作分离,职责单一,降低学习成本
- 通用命令: RedisTemplate类本身封装了一些通用或特殊命令
快速入门
首先,新建一个maven项目,然后按照下面步骤执行:
1)引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.heima</groupId>
<artifactId>redis-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redis-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--common-pool-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>2)配置Redis
spring:
redis:
host: 192.168.150.101
port: 6379
password: 123321
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 100ms3)注入RedisTemplate
因为有了SpringBoot的自动装配,我们可以拿来就用:
@SpringBootTest
class RedisStringTests {
@Autowired
private RedisTemplate redisTemplate;
}4)编写测试
@SpringBootTest
class RedisStringTests {
@Autowired
private RedisTemplate edisTemplate;
@Test
void testString() {
// 写入一条String数据
redisTemplate.opsForValue().set("name", "虎哥");
// 获取string数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}自定义序列化
- 问题现象:通过RedisTemplate写入”name”:“虎哥”后,在控制台查询却显示”Jack”,修改为”rose”后查询结果变为”rose”,与预期不符。
- 原因分析:RedisTemplate默认使用JDK序列化,会将key和value都序列化为字节形式,导致实际存储的key是序列化后的形式,而非原始字符串。
- 缺点:
- 可读性差
- 内存占用较大
- 核心组件:RedisTemplate内部有四个序列化器:
- keySerializer:普通key的序列化
- valueSerializer:普通value的序列化
- hashKeySerializer:哈希key的序列化
- hashValueSerializer:哈希value的序列化
- 默认行为:当未显式设置时,会使用JDK的ObjectOutputStream进行序列化。
代码实现
- 最佳实践:
- key/hashKey使用StringRedisSerializer
- value/hashValue使用GenericJackson2JsonRedisSerializer
- 配置方法:通过RedisTemplate的setXXXSerializer()方法设置
我们可以自定义RedisTemplate的序列化方式,代码如下:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
// 设置Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 设置Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
}这里采用了JSON序列化来代替默认的JDK序列化方式。最终结果如图:

整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。
String RedisSerializer
- 序列化方式:统一使用String序列化器,而非JSON序列化器处理value
- 存储限制:只能存储String类型的key和value,不能直接存储Java对象
- 内存优化:相比JSON序列化器,节省了存储类字节码的内存空间
- 手动处理:存储Java对象时需要手动完成序列化和反序列化

示例代码
- 默认配置:Spring提供的StringRedisTemplate类默认使用String序列化方式
- 代码简化:省去了自定义RedisTemplate的复杂配置过程
- 核心工具:使用ObjectMapper进行JSON序列化和反序列化
- 操作流程:
- 准备Java对象
- 手动序列化为JSON字符串
- 使用set方法写入Redis
- 读取时获取JSON字符串
- 手动反序列化为Java对象
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON序列化工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User("虎哥", 21);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("user:200", json);
// 获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
总结
- 方案对比:
- 自定义RedisTemplate:
- 优点:自动处理序列化/反序列化
- 缺点:占用额外内存存储类字节码
- StringRedisTemplate:
- 优点:节省内存,无需复杂配置
- 缺点:需手动处理序列化/反序列化
- 自定义RedisTemplate:
- 推荐方案:StringRedisTemplate配合工具类封装JSON处理
- 内存优化:String序列化方案存储的数据更简洁,不包含类元信息
Hash操作
- 操作获取方式:在RedisTemplate中,所有操作都通过opsFor前缀方法获取,如opsForValue()获取字符串操作,opsForHash()获取哈希操作。
- 方法命名规则:Spring中不以Redis命令名作为方法名,而是采用更贴近Java习惯的命名,如哈希操作使用put而非hset,类似于Java的HashMap操作。
- 基本操作:
- 单个字段操作:使用put(key, field, value)方法存储单个字段值,如存储用户信息:put(“user:400”, “name”, “虎哥”)。
- 批量操作:使用putAll(key, map)方法存储多个字段值,类似于Redis的hmset命令。
- 取值操作:
- get(key, field)获取单个字段值
- entries(key)获取所有字段值对,返回Map
- keys(key)获取所有字段名
- values(key)获取所有字段值
- 其他API:
- delete(key, fields):删除指定字段
- hasKey(key, field):判断字段是否存在
- increment(key, field, delta):对数值字段进行增减操作
- size(key):获取字段数量
序列化
- 对象存储:
- 使用ObjectMapper手动序列化对象为JSON字符串
- 通过stringRedisTemplate.opsForValue().set()存储序列化后的字符串
- 对象读取:
- 从Redis获取JSON字符串
- 使用ObjectMapper反序列化为Java对象