引言
很多人第一次接触 Redis 时,得到的解释通常只有一句话:
Redis 是一个高性能缓存数据库。
这句话不能说错,但对于刚开始学习 Redis 的人来说,帮助并不大。
什么叫缓存数据库?Redis 运行在哪里?Java 后端操作 Redis 时,修改的是 Java 进程里的变量,还是另一个程序里的数据?为什么多个后端服务可以共同使用 Redis?
如果这些问题没有弄清楚,后面直接学习缓存穿透、分布式锁、主从复制,很容易变成背概念。
所以这一篇先不讨论高级用法,只解决一个最基础的问题:
Redis 到底是一个什么东西?
一、先从 Android 中的全局变量说起
假设在 Android 项目中,登录成功后需要保存当前用户信息。
我们可能会写一个全局管理类:
object UserManager {
var currentUser: User? = null
}
登录成功后给它赋值:
UserManager.currentUser = user
后面的页面需要用户信息时,直接读取:
val user = UserManager.currentUser
这样做的特点是读取速度非常快,因为 currentUser 保存在当前 App 进程的内存中。
但是它也有一个明显的问题:
App 进程存在
→ currentUser 存在
App 进程被杀死
→ currentUser 消失
所以我们通常还会把用户信息保存到 MMKV、DataStore 或数据库中。
整体可能是:
服务器返回用户信息
↓
保存到 MMKV
↓
同时赋值给全局变量
↓
页面优先读取全局变量
这里其实已经包含了一种缓存思想:
将经常使用的数据放进内存,后续直接读取,避免每次都访问较慢的持久化存储。
Redis 的基础思想和它有相似之处,但 Redis 并不是 Java 中的一个全局变量。
二、Redis 是一个独立运行的进程
Redis 本质上是一个独立运行的软件。
启动 Redis 后,操作系统中会出现一个 Redis 服务进程:
redis-server
与此同时,Java 后端本身也是一个独立进程:
java -jar ark-backend.jar
如果服务器上还安装了 MySQL 和 Nginx,整体可能是:
Linux 服务器
├── Spring Boot 进程
├── Redis 进程
├── MySQL 进程
└── Nginx 进程
它们都是独立运行的程序。
因此,Redis 不是 Spring Boot 项目中的一个工具类,也不是 Maven 引入的某个对象。
真正保存 Redis 数据的是:
Redis Server 进程
Java 后端只是通过客户端去访问它。
三、不同进程之间的内存是隔离的
假设 Spring Boot 进程中有一个变量:
private static String userName = "张三";
这个变量位于 Spring Boot 进程自己的内存空间中。
Redis 进程也有自己独立的内存空间。
整体可以理解为:
Spring Boot 进程
└── 自己的 JVM 内存
Redis 进程
└── 自己管理的内存
Spring Boot 不能直接读取 Redis 进程中的内存,Redis 也不能直接访问 Java 对象。
不同进程之间的内存天然隔离。
这和 Android 中不同 App 进程类似。
假设:
App 进程 A
App 进程 B
A 进程中的全局变量,B 进程不能直接读取。
因此,当 Java 后端想要操作 Redis 时,不能像修改普通变量一样,直接拿到 Redis 内部的内存地址。
它必须通过进程间通信或网络通信发送请求。
四、Java 后端如何访问 Redis
Java 后端操作 Redis 时,一般会使用 Redis 客户端。
常见的 Java Redis 客户端包括:
Lettuce
Jedis
Redisson
在 Spring Boot 项目中,我们通常直接使用:
StringRedisTemplate
RedisTemplate
但 RedisTemplate 并不是 Redis 本身。
它只是 Spring 为我们提供的一层高级封装。
整体调用关系大致是:
业务代码
↓
StringRedisTemplate / RedisTemplate
↓
Spring Data Redis
↓
Lettuce 等 Redis 客户端
↓ TCP
Redis Server
例如我们写:
stringRedisTemplate.opsForValue()
.set("user:name", "张三");
表面上看,我们只是调用了一个 Java API。
但底层真正发生的是:
1. Java 调用 StringRedisTemplate
2. Redis 客户端生成 SET 命令
3. 客户端通过 TCP 将命令发送给 Redis
4. Redis 进程接收命令
5. Redis 修改自己管理的内存数据
6. Redis 将执行结果返回给 Java
对应的 Redis 原生命令是:
SET user:name 张三
所以最准确的理解是:
Java 代码负责发起操作,Redis 客户端负责通信,Redis Server 负责真正执行命令和保存数据。
五、一条 SET 命令到底修改了谁的数据
假设 Java 中执行:
stringRedisTemplate.opsForValue()
.set("user:1001:name", "张三");
这里并不是给 Java 中的某个全局变量赋值。
真正的数据变化发生在 Redis 进程中:
Redis 内存
user:1001:name → 张三
Spring Boot 进程只是收到了 Redis 返回的执行结果。
读取数据时也是同样的过程:
String name = stringRedisTemplate
.opsForValue()
.get("user:1001:name");
对应的底层过程是:
Spring Boot 发送 GET user:1001:name
↓
Redis 收到 GET 命令
↓
Redis 查询自己的内存
↓
Redis 返回"张三"
↓
Java 客户端将结果转换为 String
因此:
RedisTemplate 中没有保存真正的 Redis 数据
真正的数据属于 Redis Server。
六、Redis 和 OkHttp 很像
对于 Android 开发者,可以把 Redis 客户端和 OkHttp 做一个类比。
Android 请求后端时:
业务代码
→ Retrofit
→ OkHttp
→ HTTP
→ Spring Boot
Java 操作 Redis 时:
业务代码
→ RedisTemplate
→ Lettuce
→ Redis 协议
→ Redis Server
两条链路的思想非常相似。
Retrofit 和 OkHttp 并不是后端服务器,它们只是 HTTP 客户端。
同样:
RedisTemplate 和 Lettuce 也不是 Redis
它们只是 Redis 客户端。
可以简单对应为:
| Android 网络请求 | Java 操作 Redis |
|---|---|
| Retrofit | RedisTemplate |
| OkHttp | Lettuce |
| HTTP 协议 | Redis 通信协议 |
| Spring Boot 服务 | Redis Server |
| 请求接口 | 发送 Redis 命令 |
这个类比能够帮助 Android 开发者快速理解 Redis 的客户端与服务端关系。
七、Redis 通信算不算 IPC
如果 Spring Boot 和 Redis 运行在同一台服务器上:
Spring Boot 进程
↓ TCP
Redis 进程
从广义上看,这确实属于进程之间的通信。
但 Redis 并不依赖它们必须运行在同一台机器上。
例如:
应用服务器 Redis 服务器
Spring Boot ─── TCP 网络 ───> Redis
所以更准确的说法是:
Redis 是一个通过网络协议对外提供服务的数据进程。
当 Spring Boot 和 Redis 在同一台服务器时,是本机进程之间通过 Socket 通信。
当它们位于不同服务器时,就是标准的网络通信。
Redis 默认常见的监听端口是:
6379
Java 后端连接 Redis 时,一般需要配置:
spring:
data:
redis:
host: 127.0.0.1
port: 6379
如果 Redis 位于另一台服务器,则配置对应的内网地址:
spring:
data:
redis:
host: 10.0.0.20
port: 6379
对于业务代码来说,Redis 在本机还是远程机器上,通常只是连接地址不同。
八、Redis 不是真正的共享内存
Redis 可以被多个后端进程共同访问,所以很容易被理解成共享内存。
例如:
Spring Boot A ─┐
Spring Boot B ─┼── Redis
Spring Boot C ─┘
A 写入:
SET login:token:abc 1001
B 和 C 都可以读取:
GET login:token:abc
从使用效果看,它确实像多个后端服务共享了一份数据。
但是从操作系统角度看,Redis 并不是真正的共享内存。
真正的共享内存是:
多个进程共同映射同一块内存区域
Redis 的方式是:
Redis 独占自己的内存
其他进程通过命令访问 Redis
所以更准确的描述是:
Redis 是一个由独立进程管理、可以通过网络被多个程序访问的内存数据服务。
九、Redis 为什么比普通 Java 全局变量更强
在 Java 中,我们也可以自己写一个 Map:
private static final Map<String, Object> CACHE =
new ConcurrentHashMap<>();
然后保存数据:
CACHE.put("user:1001", user);
这确实也能实现内存缓存。
但是它只能服务当前 Spring Boot 进程。
假设后端部署了三个实例:
Spring Boot A
Spring Boot B
Spring Boot C
每个实例都有自己的 Map:
A 的 Map
B 的 Map
C 的 Map
A 写入的数据,B 和 C 看不到。
而 Redis 是独立服务:
Spring Boot A ─┐
Spring Boot B ─┼── Redis
Spring Boot C ─┘
三个实例访问的是同一份 Redis 数据。
另外,Redis 还提供了普通 Map 没有直接具备的能力:
自动过期
原子自增
集合去重
排行榜
持久化
访问权限
主从复制
高可用
集群
例如保存一个五分钟有效的验证码:
SET verify:code:13800000000 9527 EX 300
Redis 会在五分钟后让这个 Key 失效。
如果使用普通 Map,则需要自己编写:
过期时间记录
定时清理任务
并发控制
异常处理
Redis 已经把这些能力封装成了服务。
十、Redis 和 MySQL 是什么关系
Redis 和 MySQL 一般不是二选一的关系。
在多数后端项目中,它们承担不同职责。
MySQL
MySQL 主要负责保存正式业务数据:
用户
订单
商品
地址
支付记录
这些数据需要长期保存,不能因为服务重启就随意丢失。
Redis
Redis 通常负责保存:
热点数据副本
验证码
登录状态
临时令牌
计数器
排行榜
限流数据
例如查询用户信息时:
请求用户信息
↓
先查询 Redis
↓
Redis 有数据
→ 直接返回
Redis 没数据
→ 查询 MySQL
→ 将结果写入 Redis
→ 返回结果
所以常见的职责划分是:
MySQL
→ 正式数据源
Redis
→ 高速辅助数据服务
用 Android 的经验类比:
Android 后端
SQLite / MMKV MySQL
当前进程全局变量 JVM 本地缓存
独立共享数据服务 Redis
这个类比并不是完全等价,但非常适合建立第一层认知。
十一、Redis 的数据真的只在内存里吗
Redis 主要依靠内存完成数据操作,所以速度很快。
最简单的模型是:
Redis 命令
↓
操作内存中的数据
但这并不代表 Redis 完全不能将数据保存到磁盘。
Redis 支持持久化机制,例如:
RDB
→ 定期生成数据快照
AOF
→ 记录执行过的写命令
Redis 重启后,可以通过持久化文件恢复部分或全部数据。
不过需要注意:
Redis 能持久化,不代表它就应该完全代替 MySQL。
Redis 的主要优势仍然是快速的内存数据访问,而 MySQL 更适合复杂查询、关系模型和长期业务数据存储。
RDB 和 AOF 后面再单独深入学习。
十二、Redis 是谁安装和运行的
一台干净的 Linux 服务器上,并不会天然存在 Redis。
Redis 需要有人安装、配置并启动。
不同团队的分工可能不同。
个人项目或小型项目
通常由后端开发自己完成:
安装 Redis
配置端口和密码
启动 Redis
配置开机自启
让 Spring Boot 连接
中大型公司
一般由运维或 DevOps 负责:
准备服务器
部署 Redis
配置网络
配置权限
配置持久化
配置监控
提供连接信息
后端开发拿到的可能只是:
Redis 地址
端口
用户名
密码
数据库编号
云环境
也可以直接购买云 Redis。
云厂商负责:
运行
备份
监控
故障切换
版本维护
后端只需要配置连接地址。
因此,Redis 和 MySQL、Nginx 一样,都属于服务器上的基础服务。
十三、一个 Redis 是不是所有项目共用
技术上,多个项目可以连接同一个 Redis。
例如:
项目 A ─┐
项目 B ─┼── Redis
项目 C ─┘
但这时必须做好 Key 隔离。
例如:
ark:user:1001
ark:login:token:abc
lift:user:1001
lift:login:token:def
如果两个项目都使用:
user:1001
就可能发生 Key 冲突和数据覆盖。
因此,Key 通常会包含项目和业务前缀:
项目名:业务名:数据类型:唯一标识
例如:
ark:user:info:1001
ark:verify:code:13800000000
ark:login:token:abc123
对于重要项目,通常会使用独立 Redis 实例,避免不同系统之间相互影响。
十四、重新理解 Redis
现在可以重新给 Redis 下一个定义:
Redis 是一个独立运行的内存数据服务。它由 Redis Server 进程管理数据,Java、Python、Node.js 等程序通过客户端和网络协议向 Redis 发送命令,由 Redis 进程真正完成数据的读取、修改、删除和过期管理。
整条链路是:
Java 业务代码
↓
RedisTemplate
↓
Lettuce 等客户端
↓ TCP
Redis Server 进程
↓
Redis 管理的内存数据
↓
必要时持久化到磁盘
这里需要特别分清三个角色:
Java 代码
→ 发起业务操作
Redis 客户端
→ 负责连接、命令发送和结果转换
Redis Server
→ 负责真正执行命令和保存数据
十五、本篇总结
这一篇需要建立的不是 Redis 命令,而是 Redis 的运行模型。
第一,Redis 是一个独立运行的服务进程,不是 Java 中的普通变量。
第二,Spring Boot 与 Redis 拥有各自独立的内存空间。
第三,Java 后端需要通过 Redis 客户端和 TCP 通信访问 Redis。
第四,RedisTemplate 只是客户端封装,真正保存数据的是 Redis Server。
第五,Redis 可以被多个进程、多个服务器共同访问,因此适合在分布式后端中共享数据。
第六,Redis 主要操作内存数据,但也支持将数据持久化到磁盘。
第七,Redis 和 MySQL 通常不是替代关系,而是分工合作。
最终可以用一句话概括:
Redis 可以理解为一个独立运行、支持网络访问、能够被多个程序共享使用的高性能内存数据服务。
下一篇预告
下一篇继续讲:
《Redis、MySQL、本地变量和 MMKV 到底有什么区别?》
下一篇会从 Android 开发者熟悉的场景出发,系统对比:
Android 全局变量
MMKV
JVM 本地缓存
MySQL
Redis
重点讲清楚它们分别将数据存在哪里、生命周期有什么区别,以及真实项目中应该怎样分工。