爆款教程:Redis 核心数据结构 + 实战操作,手把手教你!(Redis基础篇)
一、初识Redis
Redis(Remote Dictionary Server)是一款开源、基于内存、高性能的 key-value 数据库。
1.1 Redis特点
- 内存存储、极低延迟:数据主要驻留内存,读写延迟通常在微秒到毫秒级,适合高吞吐场景。
- 单线程 + I/O 多路复用:命令处理采用单线程模型(避免锁竞态),通过事件驱动实现高并发。
- 内存管理与淘汰策略:支持
noeviction、allkeys-lru、volatile-lru、allkeys-random等多种策略,方便控制内存上限后的行为。 - 复制与高可用:主从复制(replication)+ Sentinel 用于监控与故障转移,Cluster 支持分片水平扩展。
- 原子操作与事务:大多数命令是原子性的,提供
MULTI/EXEC批量事务语义(非隔离);通过 Lua 脚本还能实现复杂的原子逻辑。 - 适用场景:缓存、会话存储、排行榜、计数器、分布式锁、消息队列、实时统计等;注意:不是替代关系型数据库的事务一致性场景(除非做额外设计)。
1.2 安装Redis及其图形化界面
建议看上一篇基于Docker安装Redis,内容详实,还可以直接按照教程给出的DockerCompose文件轻松配置。
二、Redis数据结构
2.1 数据结构简介
大致包含以下数据结构:
| 数据结构 | 典型命令 | 特点 / 能力 | 常用场景 |
|---|---|---|---|
| String | SET / GET / INCR / APPEND |
最基础结构,可存文本、数字、二进制;支持原子自增 | 缓存、计数器、分布式锁标识、配置存储 |
| Hash | HSET / HGET / HGETALL / HINCRBY |
类似小型对象存储,适合存用户数据;单字段可独立读写 | 用户信息、对象属性、配置项集合 |
| List | LPUSH / RPUSH / LPOP / BRPOP |
有序、支持快速首尾操作;支持阻塞读 | 消息队列、任务队列、按顺序记录日志 |
| Set | SADD / SMEMBERS / SINTER / SUNION |
自动去重;支持交并差;适合关系运算 | 标签系统、共同好友、抽奖池、唯一集合 |
| Sorted Set (ZSet) | ZADD / ZRANGE / ZREVRANGE / ZINCRBY |
带分数的有序集合,可按分值排序 | 排行榜、优先级队列、按权重排序的数据 |
| Bitmap | SETBIT / GETBIT / BITCOUNT |
位级操作,非常省内存;适合百万级布尔记录 | 用户签到、活跃统计、特征标记 |
| HyperLogLog | PFADD / PFCOUNT |
基数(去重人数)估算,内存占用极低(12KB) | UV 统计、百万级去重计数 |
| Stream | XADD / XREAD / XGROUP |
日志流结构,支持消费组、持久化、消息队列能力 | 消息系统、事件流、日志处理、高并发 MQ 替代 |
如果你太着急想知道他们的用法,可以先在配置好的Redis客户端命令行执行help来查看他们的对应的命令。就像下面这样

2.2Redis通用命令
KEYS:根据通配符匹配查询所有符合模式的键,可使用 *(任意字符)、?(单个字符)、[](字符集合)、^(取反集合)。但由于该命令会全库扫描、时间复杂度高,在生产环境不推荐使用,尤其键数量较大时会阻塞整个 Redis 线程。
DEL:删除指定的 key,可一次删除多个。操作是立即生效的,如果删除的是大型数据结构,会有一定的阻塞风险。
EXISTS:判断某个 key 是否存在,返回存在的数量(从 Redis 3.0 开始支持同时检查多个 key)。
EXPIRE:为 key 设置过期时间(秒),过期后 Redis 会自动将其删除。适用于缓存、临时标记、会话等需要自动失效的场景。
TTL:查看 key 的剩余有效时间。
-
返回
-1:该 key 存在,但没有设置过期时间 -
返回
-2:key 已过期或不存在 -
返回
>=0:表示剩余秒数
TYPE:查询指定 key 当前的数据类型,例如 string、hash、list 等。用于调试或确认结构。
RENAME:将某个 key 修改成新的名称,如果新 key 已存在则会被覆盖,因此使用需谨慎。
SCAN:比 KEYS 更安全的遍历方式,通过游标分段扫描,不阻塞主线程,适合大数据量下分页式遍历 key。
MOVE:将 key 从当前数据库移动到另一个数据库(如从 DB0 移到 DB1),适用于逻辑分库场景。
PERSIST:移除 key 的过期时间,使其变为永久有效。
EXISTS:检查 key 是否存在,通常用于业务逻辑判断,比如防重复提交、分布式锁占位。
PING:测试与 Redis 的连接是否正常,正常会返回 PONG。
搭配help,查看命令的参数信息,就像下面这样,

2.2 String 类型与 Key 层级结构
2.2.1 String 类型简介
String 类型是 Redis 中最基础、最常用的数据结构。它的 value 本质上是字节数组,根据内容格式不同可表现为三类:
-
string:普通字符串
-
int:整数,可执行原子自增/自减
-
float:浮点数,可执行浮点增减
底层统一使用字节数组存储,只是编码方式不同。
2.2.2 常用命令
| 命令 | 作用 |
|---|---|
| SET key value | 新增或修改一个 String 类型的键值对 |
| GET key | 获取 String 类型的 value |
| MSET key1 v1 key2 v2 ... | 批量新增多个 String 键值对 |
| MGET key1 key2 ... | 批量获取多个 String 的值 |
| INCR key | 整数类型自增 1(若 key 不存在,先初始化为 0) |
| INCRBY key n | 整数类型按指定步长自增 n(若 key 不存在,先初始化为 0) |
| INCRBYFLOAT key f | 浮点类型按步长自增(若 key 不存在,先初始化为 0) |
| SETNX key value | 仅当 key 不存在时写入(新增语义) |
| SETEX key seconds value | 写入并设置过期时间(秒) |
示例
SET user:name "Tom"
GET user:name
INCR counter
INCRBY counter 5
INCRBYFLOAT score 1.5
SETEX login:token:1 3600 "abc-123"
SETNX config:mode "dev"
2.2.3 Key 的层级结构规范
Redis 没有类似 MySQL Table 的概念,因此约定式的 key 命名规范非常重要。
推荐使用多段结构,并用 : 分隔,构成分层:
项目名:业务模块:数据主键
示例(以 chatroom为项目名):
chatroom:user:1
chatroom:product:1
若 value 是 Java 对象,通常序列化为 JSON 存储:
chatroom:user:1 {"id":1, "name":"Jack", "age":21} chatroom:product:1 {"id":1, "name":"小米11", "price":4999}
样例

在可视化客户端可以看到

2.3 Hash类型
-
Hash类型,也叫散列,其中value是一个无序字典,类似于Java中的HashMap结构
-
String结构是将对象序列化为JSON字符串后存储,当我们要修改对象的某个属性值的时候很不方便
| KEY | VALUE |
|---|---|
| reggie:user:1 | {“id”:1, “name”: “Jack”, “age”: 21} |
| reggie:dish:1 | {“id”:1, “name”: “鲟鱼火锅”, “price”: 4999} |
-
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD
2.3.1 Hash类型的常见命令
| 命令 | 作用 |
|---|---|
| HSET key field value | 添加或修改 hash 类型 key 的 field 的值 |
| HGET key field | 获取一个 hash 类型 key 的 field 的值 |
| HMSET key field1 value1 [field2 value2 ...] | 批量添加多个 hash 类型 key 的 field 的值 |
| HMGET key field1 [field2 ...] | 批量获取多个 hash 类型 key 的 field 的值 |
| HGETALL key | 获取一个 hash 类型 key 中的所有 field 和 value |
| HKEYS key | 获取一个 hash 类型 key 中的所有 field |
| HVALS key | 获取一个 hash 类型 key 中的所有 value |
| HEXISTS key field | 判断指定 key 中是否存在 field,存在返回 1,不存在返回 0 |
| HDEL key field [field ...] | 删除指定 key 中的 field,如果所有字段被删除,则 key 也被删除;key 不存在返回 0 |
| HLEN key | 返回该 key 所含有的 field 字段数量 |
2.3.2 Hash类型使用场景
-
存储对象(用户信息、商品信息、配置)
-
部分字段频繁修改的对象(Hash 不需要整个 JSON 重写,更新性能更高)
-
需要读取某一个字段时更高效、占用更少内存
-
避免大量 key 造成的内存碎片化(相比 string-json)
2.3.3 Hash 与 String(JSON) 的对比总结
很多人搞不清什么时候用啥,下面是一个简单的对比
| 对比项 | String(JSON) | Hash |
|---|---|---|
| 修改单字段 | ❌ 需要整体重写 JSON | ✔ 可单独改字段 |
| 内存占用 | 大 | 小(结构压缩优化) |
| 序列化/反序列化 | 需要 | 不需要 |
| 字段数量大时 | 不适合 | 非常适合 |
| 存储结构 | 一个 key 一个 JSON |
一个 key 多个 field |
2.4 List类型
-
List 类型是 链表结构(双向链表),其元素保持 有序、可重复。
-
可在两端高效插入和删除,非常适合实现 消息队列、日志流、时间线、排行榜的局部更新 等场景。
-
List 的操作大多是 O(1),适合频繁 push/pop。
典型结构如下:
Key: user:msg Value: ["Hi", "OK", "See you"]
2.4.1 List的常见命令
| 命令 | 作用 |
|---|---|
| LPUSH key value [value ...] | 从左侧插入元素 |
| RPUSH key value [value ...] | 从右侧插入元素 |
| LPOP key | 从左侧弹出一个元素 |
| RPOP key | 从右侧弹出一个元素 |
| LRANGE key start stop | 获取指定区间的元素列表 |
| LINDEX key index | 按下标获取元素 |
| LSET key index value | 修改指定位置的值 |
| LREM key count value | 删除指定元素 |
| BLPOP key timeout | 阻塞式弹出(左侧) |
| BRPOP key timeout | 阻塞式弹出(右侧) |
使用BLPOP和BRPO时,若不存在该元素,会阻塞进程等待指定时间。若指定时间内等待元素出现,返回该元素值,否则返回nil。

使用示范
# 在消息列表左侧插入
LPUSH user:msg "Hello" "OK" "Bye"
# 从右侧插入
RPUSH user:msg "newMsg"
# 按区间查询消息
LRANGE user:msg 0 -1
# 弹出最早的消息(队列语义)
LPOP user:msg
# 弹出最新的消息(栈语义)
RPOP user:msg
# 只保留最近 100 条消息
LTRIM user:msg 0 99
2.4.2 List 的典型场景
-
消息队列(简单队列):LPUSH + BRPOP
-
时间线 / 评论流:按插入顺序记录
-
最新 N 条记录:配 LTRIM 高效维护
-
任务列表:worker 消费任务
2.5 Set类型
Redis 的 Set 结构类似 Java 中的 HashSet,可以看作 value 为 null 的 HashMap。
主要特性:
-
无序:元素在集合中没有顺序
-
唯一性:元素不可重复
-
查找快:基于哈希表实现
-
支持集合运算:交集、并集、差集等
2.5.1 Redis Set 常用命令
| 命令 | 功能说明 |
|---|---|
| SADD key member ... | 向 set 中添加一个或多个元素 |
| SREM key member ... | 移除 set 中的一个或多个元素 |
| SCARD key | 返回 set 中元素个数 |
| SISMEMBER key member | 判断元素是否存在于 set,存在返回 1,不存在返回 0 |
| SMEMBERS key | 获取 set 中所有元素 |
| SINTER key1 key2 ... | 求多个 set 的交集 |
| SINTERSTORE dest key1 key2 ... | 求交集并将结果存入新 set(dest) |
| SDIFF key1 key2 ... | 求多个 set 的差集(key1 - key2 - ...) |
| SDIFFSTORE dest key1 key2 ... | 求差集并存入新 set(dest) |
| SUNION key1 key2 ... | 求多个 set 的并集 |
| SUNIONSTORE dest key1 key2 ... | 求并集并存入新 set(dest) |
2.5.2 Set类型使用场景
-
存储对象,可以将对象的各个属性作为 field 存储在同一个 key 下,逻辑清晰
-
部分字段频繁修改的对象,Hash 可以单独修改某个 field,无需重写整个对象,性能更高
-
需要读取某个字段时更高效,只读取特定 field,占用内存更少,避免序列化/反序列化整个对象
-
避免大量 key 导致内存碎片化,相比 String 存储 JSON,每个对象一个 key,Hash 将多个字段集中存储,减少内存开销
2.6 Sorted Set 类型简介
Redis 的 Sorted Set(有序集合) 是在 Set 基础上加了 分数(score) 的集合,每个元素都是唯一的,但可以根据分数自动排序。
主要特性:
-
元素唯一:元素本身不能重复
-
有序:每个元素都有一个 score,用于排序
-
快速查询:可以按分数范围或排名快速查找
-
支持集合操作:交集、并集(结果可根据 score 聚合)
2.6.1 Sorted Set 常用命令
| 命令 | 功能说明 |
|---|---|
| ZADD key score member [score member ...] | 向有序集合添加一个或多个元素,已存在则更新 score |
| ZREM key member [member ...] | 删除一个或多个元素 |
| ZCARD key | 返回有序集合元素个数 |
| ZSCORE key member | 获取元素的 score |
| ZRANGE key start stop [WITHSCORES] | 按索引范围获取元素,0 为第一个元素 |
| ZREVRANGE key start stop [WITHSCORES] | 按索引倒序获取元素 |
| ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] | 按分数范围获取元素 |
| ZREMRANGEBYSCORE key min max | 删除指定分数范围的元素 |
| ZRANK key member | 获取元素排名(从 0 开始,按 score 升序) |
| ZREVRANK key member | 获取元素倒序排名 |
| ZINCRBY key increment member | 对元素的 score 增加指定值 |
三、Reidisjava客户端
-
目前主流的Redis的Java客户端有三种
-
Jedis和Lettuce:这两个主要是提供了Redis命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以SpringDataRedis来学习。
-
Redisson:是在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。
-
3.1 Jedis客户端
导入依赖
<!-- jedis依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>6.2.0</version>
</dependency>
<!-- 单元测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.13.3</version>
<scope>test</scope>
</dependency>
<!-- 日志依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
普通连接
@BeforeEach
void setUp() {
//建立连接
jedis = new Jedis("192.168.84.40",6379);
//验证密码
jedis.auth("123456");
//选择数据库
jedis.select(0);
}
连接池连接
@BeforeEach
void setUp() {
//改为使用连接池
jedis = JedisCollectionFactory.getJedis();
//验证密码
jedis.auth("123456");
//选择数据库
jedis.select(0);
}
操作数据
@Test
public void testString() {
String result = jedis.set("name", "ling");
System.out.println("result = " + result);
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
public void testHash() {
jedis.hset("user:1", "name", "ling");
jedis.hset("user:1", "age", "18");
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println("map = " + map);
}
关闭连接
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}
总结
对于复杂的对象或者键值对集合,需要自己定义键值对,不方便。
3.2 SpringDataRedis客户端
-
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis
-
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
| API | 返回值类型 | 说明 |
|---|---|---|
| redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
| redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
| redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
| redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
| redisTemplate.opsForzSet() | ZSetOperations | 操作SortedSet类型数据 |
| redisTemplate | 通用的命令 |
快速入门
-
导入依赖
<!--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> <!--lombok--> <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> -
配置Redis
spring: redis: host: 192.168.84.40 port: 6379 password: 123456 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: 100ms -
注入RedisTemplate 因为有了SpringBoot的自动装配,我们可以拿来就用
@Autowired private RedisTemplate redisTemplate; -
编写测试方法
@Test void stringTest(){ redisTemplate.opsForValue().set("username","David"); String username = (String) redisTemplate.opsForValue().get("username"); System.out.println(username); }
自定义序列化
-
RedisTemplate可以接收任意Object作为值写入Redis
-
只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的,被剁碎了
\xAC\xED\x00\x05t\x00\x06\xE5\xBC\xA0\xE4\xB8\x89
-
缺点:
-
可读性差
-
内存占用较大
-
自己定义序列化方式
在启动类中配置即可
@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;
}
定义实体类并测试
@Test
public void testUser() {
//创建对象
User user = new User("小王", 18);
redisTemplate.opsForValue().set("user:1", user);
//获取
System.out.println(redisTemplate.opsForValue().get("user:1"));
User client = (User) redisTemplate.opsForValue().get("user:1");
System.out.println(client);
}
客户端结果
{
"@class": "com.ling.pojo.User",
"name": "小王",
"age": 18
}
-
整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。
统一使用String序列化器
StringRedisTemplate
-
为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
-
因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了
-
这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。源码如下
public class StringRedisTemplate extends RedisTemplate<String, String> { public StringRedisTemplate() { this.setKeySerializer(RedisSerializer.string()); this.setValueSerializer(RedisSerializer.string()); this.setHashKeySerializer(RedisSerializer.string()); this.setHashValueSerializer(RedisSerializer.string()); } -
省去了我们自定义RedisTemplate的序列化方式的步骤(可以将之前配置的RedisConfig删除掉),而是直接使用:
@Test void stringTest() throws JsonProcessingException { //创建对象 User user = new User("张三", 18); //手动序列化 String json = mapper.writeValueAsString(user); //写入数据 stringRedisTemplate.opsForValue().set("userdata", json); //获取数据 String userdata = stringRedisTemplate.opsForValue().get("userdata"); //手动反序列化 User readValue = mapper.readValue(userdata, User.class); System.out.println(readValue); }
四、总结
Redis 是一个高性能的内存数据存储,提供多种数据结构(String、Hash、List、Set、Sorted Set),每种类型都有其适用场景和操作特点:
-
String:最简单的键值对,支持数值自增、批量操作,适合存储简单数据或计数器。
-
Hash:适合存储对象或部分字段频繁修改的数据,如用户信息、商品信息,支持单字段操作,节省内存。
-
Set:无序且唯一的集合,支持快速去重、集合运算(交集、并集、差集),常用于标签管理、好友关系或随机抽取。
-
Sorted Set:带分数的有序集合,元素唯一但可按 score 排序,适用于排行榜、延时队列、带权重的数据排序。
在 Java 中,常用客户端有两类:
-
Jedis:操作直观,适合简单场景或快速开发,但需要自行管理序列化和复杂对象存储。
-
Spring Data Redis:通过
RedisTemplate提供类型化 API(StringOperations、HashOperations、SetOperations 等),支持自定义序列化,适合在 Spring 项目中统一管理 Redis,尤其适合存储复杂对象。 -
StringRedisTemplate:统一使用 String 序列化,便于与 JSON 结合进行对象存储和反序列化。
核心实践经验:
-
根据数据特点选择合适的数据结构,避免误用导致性能或内存浪费。
-
对复杂对象或集合数据,优先考虑 Hash 或 JSON 序列化存储,减少 key 数量,避免内存碎片。
-
集合操作(Set、Sorted Set)可以充分利用 Redis 的交并差运算和排名查询能力,减少客户端计算。
-
在 Java 项目中,
RedisTemplate配合自定义序列化器可以让 Redis 存储和对象操作无缝对接,提高开发效率。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)