引言
在如今的互联网应用里,几乎没有哪个系统离得开 Redis。首页的商品信息、用户的登录状态、接口的限流计数、实时的排行榜......背后往往都有 Redis 的身影。它是目前最流行的内存数据库,也是后端工程师面试必问、日常必用的中间件之一。
很多刚接触 Redis 的同学,第一反应是"它不就是个缓存吗"。其实 Redis 远不止缓存这么简单,它丰富的数据类型能支撑起缓存、计数器、排行榜、消息队列、分布式锁等大量场景。这篇文章我会先讲清楚 Redis 是什么、为什么快,然后逐个拆解它的五种基础数据类型,每种都配上命令示例和典型使用场景。
一、Redis 是什么
Redis(Remote Dictionary Server,远程字典服务) 是一个开源的、基于内存的高性能 键值对(Key-Value)存储系统。它既可以当数据库,也可以当缓存和消息中间件。
Redis 的几个核心特点:
- 基于内存:数据存在内存里,读写速度极快(单机轻松 10W+ QPS),这是它快的根本原因。
- 单线程:核心命令处理是单线程的,避免了多线程切换和锁竞争的开销(注:Redis 6.0 之后引入了多线程,但主要用于网络 IO,命令执行仍是单线程)。
- 持久化:虽然数据在内存,但可以通过 RDB 快照或 AOF 日志把数据落盘,防止宕机丢失。
- 丰富的数据类型:除了 String,还支持 List、Hash、Set、ZSet 等,这是它区别于普通 KV 缓存(如 Memcached)的关键。
- 支持多种高级特性:过期时间、发布订阅、事务、Lua 脚本、主从复制、哨兵、集群等。
它为什么这么快? 归纳起来就是:纯内存操作 + 单线程避免锁竞争 + IO 多路复用 + 高效的数据结构。
二、安装与启动
最简单的体验方式是用 Docker 跑一个 Redis:
bash
# 拉取镜像并启动
docker run -d --name redis -p 6379:6379 redis
# 进入 Redis 命令行客户端
docker exec -it redis redis-cli
连接成功后输入 PING,返回 PONG 就说明 Redis 正常运行了:
plaintext
127.0.0.1:6379> PING
PONG
本文后续的命令示例,都在这个 redis-cli 命令行里演示。
三、五种基础数据类型
Redis 的 Key 都是字符串,但 Value 可以是不同类型。最基础、最常用的有五种:String、Hash、List、Set、ZSet。下面逐个介绍。
3.1 String(字符串)
String 是最基础的类型,一个 Key 对应一个字符串值,这个字符串最大能存 512MB。它不仅能存普通文本,还能存整数、浮点数、JSON、二进制等。
常用命令:
| 命令 | 作用 | 示例 |
|---|---|---|
| SET / GET | 设置 / 获取 | SET name zhangsan → GET name |
| SETNX | 不存在才设置(分布式锁基础) | SETNX lock 1 |
| SETEX | 设置并指定过期时间(秒) | SETEX code 60 123456 |
| INCR / DECR | 原子自增 / 自减 | INCR count |
| MSET / MGET | 批量设置 / 获取 | MSET a 1 b 2 → MGET a b |
| APPEND | 追加内容 | APPEND name " hello" |
演示:
plaintext
127.0.0.1:6379> SET user:1:name zhangsan
OK
127.0.0.1:6379> GET user:1:name
"zhangsan"
127.0.0.1:6379> INCR view:count
(integer) 1
127.0.0.1:6379> INCR view:count
(integer) 2
典型场景: 缓存对象(JSON 字符串)、计数器(点赞数、浏览量)、分布式锁(SETNX + 过期时间)、验证码(SETEX)、Session 共享。
3.2 Hash(哈希)
Hash 是一个 Key 对应一个 field-value 映射表 ,特别适合用来存储对象,就像 Java 里的 Map<String, Map<String, String>>。
常用命令:
| 命令 | 作用 | 示例 |
|---|---|---|
| HSET / HGET | 设置 / 获取某个 field | HSET user:1 name zhangsan → HGET user:1 name |
| HMSET / HMGET | 批量设置 / 获取 | HMSET user:1 name lisi age 20 |
| HGETALL | 获取所有 field-value | HGETALL user:1 |
| HDEL | 删除某个 field | HDEL user:1 age |
| HLEN | field 数量 | HLEN user:1 |
| HINCRBY | 给 field 的整数加值 | HINCRBY user:1 age 1 |
演示:
plaintext
127.0.0.1:6379> HMSET user:1 name zhangsan age 18 city beijing
OK
127.0.0.1:6379> HGET user:1 name
"zhangsan"
127.0.0.1:6379> HGETALL user:1
1) "name"
2) "zhangsan"
3) "age"
4) "18"
5) "city"
6) "beijing"
典型场景: 存储对象(用户信息、商品信息)。相比把对象序列化成 JSON 存 String,Hash 可以单独修改某个字段而不必整个重新序列化,也更节省内存(字段少时用 ziplist 压缩存储)。
3.3 List(列表)
List 是一个有序、可重复 的字符串列表,底层是双向链表,所以它头尾操作极快,中间操作较慢。
常用命令:
| 命令 | 作用 | 示例 |
|---|---|---|
| LPUSH / RPUSH | 从左 / 右插入 | LPUSH mylist a b c |
| LPOP / RPOP | 从左 / 右弹出 | LPOP mylist |
| LRANGE | 按区间取元素 | LRANGE mylist 0 -1(取全部) |
| LLEN | 列表长度 | LLEN mylist |
| LREM | 删除指定元素 | LREM mylist 1 a |
| BLPOP / BRPOP | 阻塞式弹出(消息队列基础) | BRPOP mylist 0 |
演示:
plaintext
127.0.0.1:6379> LPUSH mylist a b c
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> RPOP mylist
"a"
注意 LPUSH 是从左边依次插入,所以最后 LRANGE 出来是倒序的(c b a)。
典型场景: 消息队列(LPUSH 生产 + BRPOP 消费)、最新消息列表、粉丝关注列表、简单的栈(LPUSH + LPOP)。
3.4 Set(集合)
Set 是一个无序、不可重复 的字符串集合,基于哈希表实现,查找、判重的复杂度都是 O(1)。它最大的亮点是支持集合运算(交集、并集、差集)。
常用命令:
| 命令 | 作用 | 示例 |
|---|---|---|
| SADD | 添加元素 | SADD tags java redis |
| SMEMBERS | 查看所有元素 | SMEMBERS tags |
| SISMEMBER | 判断是否存在 | SISMEMBER tags java |
| SREM | 删除元素 | SREM tags java |
| SCARD | 元素数量 | SCARD tags |
| SINTER | 交集 | SINTER set1 set2 |
| SUNION | 并集 | SUNION set1 set2 |
| SDIFF | 差集 | SDIFF set1 set2 |
演示:
plaintext
127.0.0.1:6379> SADD user:1:friends lisi wangwu zhaoliu
(integer) 3
127.0.0.1:6379> SADD user:2:friends wangwu zhaoliu sunqi
(integer) 3
# 共同好友(交集)
127.0.0.1:6379> SINTER user:1:friends user:2:friends
1) "wangwu"
2) "zhaoliu"
典型场景: 去重(防止重复点赞、重复抽奖)、共同好友/共同关注(交集)、标签系统、随机抽奖(SPOP)。
3.5 ZSet(有序集合)
ZSet 是 Set 的"升级版",每个元素都关联一个 score(分数) ,元素按 score 从小到大排序。它既保留了 Set 的"唯一性",又多了"排序"能力,是排行榜场景的首选。
常用命令:
| 命令 | 作用 | 示例 |
|---|---|---|
| ZADD | 添加元素(带分数) | ZADD rank 100 zhangsan 95 lisi |
| ZRANGE | 按分数从小到大取 | ZRANGE rank 0 -1 |
| ZREVRANGE | 按分数从大到小取 | ZREVRANGE rank 0 -1 |
| ZRANGEBYSCORE | 按分数区间取 | ZRANGEBYSCORE rank 90 100 |
| ZSCORE | 获取某元素分数 | ZSCORE rank zhangsan |
| ZRANK | 获取排名(从小到大) | ZRANK rank lisi |
| ZREM | 删除元素 | ZREM rank lisi |
| ZINCRBY | 给分数加值 | ZINCRBY rank 5 zhangsan |
演示:
plaintext
127.0.0.1:6379> ZADD rank 100 zhangsan 95 lisi 88 wangwu
(integer) 3
# 从高到低排名(排行榜)
127.0.0.1:6379> ZREVRANGE rank 0 -1 WITHSCORES
1) "zhangsan"
2) "100"
3) "lisi"
4) "95"
5) "wangwu"
6) "88"
# 给 zhangsan 加 5 分
127.0.0.1:6379> ZINCRBY rank 5 zhangsan
"105"
典型场景: 排行榜(游戏积分、商品销量、热搜榜)、带权重的队列、延时队列(用时间戳做 score)。
四、五种类型速查对比
| 类型 | 结构特点 | 是否有序 | 是否可重复 | 典型场景 |
|---|---|---|---|---|
| String | 单个值 | --- | --- | 缓存、计数器、分布式锁 |
| Hash | field-value 映射 | 否 | field 不重复 | 存储对象 |
| List | 双向链表 | 是 | 是 | 消息队列、最新列表 |
| Set | 哈希集合 | 否 | 否 | 去重、共同好友 |
| ZSet | 带 score 的集合 | 是 | 否 | 排行榜、延时队列 |
五、实战:SpringBoot 整合 Redis
光会命令还不够,后端开发最终要在 Java 代码里操作 Redis。下面给一个 SpringBoot 整合 Redis 的最小示例。
5.1 引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池(可选,生产建议加) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
5.2 配置连接
yaml
spring:
data:
redis:
host: localhost
port: 6379
# password: 123456
database: 0
5.3 使用 StringRedisTemplate
java
@Service
public class RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
// 缓存字符串
public void set(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}
// 计数器
public Long incr(String key) {
return redisTemplate.opsForValue().increment(key);
}
// Hash 存对象
public void saveUser(String key, Map<String, String> user) {
redisTemplate.opsForHash().putAll(key, user);
}
// List 队列
public Long push(String key, String value) {
return redisTemplate.opsForList().leftPush(key, value);
}
// Set 添加
public Long sadd(String key, String value) {
return redisTemplate.opsForSet().add(key, value);
}
// ZSet 排行榜加分数
public void zadd(String key, String member, double score) {
redisTemplate.opsForZSet().add(key, member, score);
}
}
StringRedisTemplate 对应的操作方法:opsForValue()(String)、opsForHash()(Hash)、opsForList()(List)、opsForSet()(Set)、opsForZSet()(ZSet),和上面讲的五种类型一一对应。
总结
这篇文章把 Redis 的基础脉络梳理了一遍:
- Redis 是什么:基于内存的高性能 Key-Value 存储,可当数据库、缓存、消息中间件;快的原因在于内存操作、单线程、IO 多路复用。
- 五种基础数据类型 :
- String:最基础,缓存、计数器、分布式锁;
- Hash:field-value 映射,适合存对象、局部更新;
- List:有序可重复的双向链表,适合队列、最新列表;
- Set:无序去重的集合,适合判重、共同好友;
- ZSet:带 score 的有序集合,排行榜首选。
- SpringBoot 整合 :通过
StringRedisTemplate的opsForXXX()方法即可方便地操作五种类型。
掌握这五种类型和它们的典型场景,是深入 Redis 的第一步。