第三篇:一条 Redis SET 命令到底经历了什么?

发布时间:2026/7/27 1:35:42
第三篇:一条 Redis SET 命令到底经历了什么? 引言前两篇已经建立了 Redis 的基本运行模型。我们知道Spring Boot 是一个独立进程 Redis Server 也是一个独立进程两者拥有各自独立的内存空间。Java 后端不能直接修改 Redis 进程中的内存只能通过 Redis 客户端发送命令由 Redis Server 真正执行操作。例如在 Spring Boot 中写下stringRedisTemplate.opsForValue() .set(user:name, 张三);表面上看这只是一行普通的 Java 代码。但它背后实际上经过了多层调用业务代码 → StringRedisTemplate → Spring Data Redis → Redis 客户端 → TCP Socket → Redis Server → 修改 Redis 内存 → 返回执行结果这一篇就沿着这条链路完整分析一条SET命令从 Java 代码出发到 Redis 真正保存数据中间到底经历了什么一、先看这行代码做了什么假设 Spring Boot 项目中已经注入了StringRedisTemplateService public class UserService { private final StringRedisTemplate stringRedisTemplate; public UserService( StringRedisTemplate stringRedisTemplate ) { this.stringRedisTemplate stringRedisTemplate; } public void saveUserName() { stringRedisTemplate.opsForValue() .set(user:name, 张三); } }我们真正关心的是这一行stringRedisTemplate.opsForValue() .set(user:name, 张三);它对应的 Redis 原生命令是SET user:name 张三最终 Redis 中会形成一条数据user:name → 张三但 Java 代码并不是直接执行 Redis 命令。中间还经过了多层封装。二、先分清这条链路中的几个角色在分析调用过程之前先把相关角色分清楚。1. 业务代码也就是我们自己编写的代码stringRedisTemplate.opsForValue() .set(user:name, 张三);业务代码表达的是我要在 Redis 中保存一个 Key-Value。业务层不需要自己处理 Socket、网络协议和字节传输。2. StringRedisTemplateStringRedisTemplate是 Spring Data Redis 提供的操作工具。它主要用于操作字符串形式的 Key 和 Value。例如stringRedisTemplate.opsForValue() .set(user:name, 张三);读取String name stringRedisTemplate .opsForValue() .get(user:name);删除stringRedisTemplate.delete(user:name);它帮助我们把 Redis 原生命令包装成更符合 Java 使用习惯的 API。但是StringRedisTemplate不是 Redis Server也不负责真正保存数据。它只是当前 Spring Boot 进程中的一个 Java 对象。3. Spring Data RedisSpring Data Redis 是 Spring 提供的 Redis 访问抽象层。它负责处理操作接口封装 序列化配置 连接获取 异常转换 命令调用 结果转换例如opsForValue().set(key, value)最终会被转换为底层 Redis 客户端能够执行的命令操作。它类似于数据库访问中的MyBatis / JPA但它们操作的目标不同MyBatis / JPA → 访问关系型数据库 Spring Data Redis → 访问 Redis4. Redis 客户端真正负责与 Redis Server 建立网络连接的是底层 Redis 客户端。Java 中常见的 Redis 客户端包括Lettuce Jedis RedissonSpring Boot 项目中我们可能只直接使用StringRedisTemplate但在更底层仍然需要具体客户端负责建立连接 发送命令 接收响应 处理网络异常 管理连接状态整体关系可以理解为StringRedisTemplate → Spring Data Redis → Lettuce 或其他客户端 → Redis Server5. Redis ServerRedis Server 才是真正执行命令的服务端。它负责接收客户端命令 解析命令 查找 Key 修改数据结构 管理过期时间 返回执行结果真正的数据最终保存在 Redis 进程管理的内存中。三、整个调用链先看一遍当我们执行stringRedisTemplate.opsForValue() .set(user:name, 张三);整体过程可以概括为第一步业务代码调用 StringRedisTemplate 第二步StringRedisTemplate 选择 Value 操作接口 第三步Key 和 Value 被序列化成字节数据 第四步Spring Data Redis 获取 Redis 连接 第五步底层客户端生成 SET 命令 第六步命令通过 TCP Socket 发送给 Redis 第七步Redis Server 读取并解析命令 第八步Redis 在自己的内存中保存数据 第九步Redis 返回执行结果 第十步Java 客户端接收并转换结果下面逐步展开。四、第一步调用 opsForValue()代码stringRedisTemplate.opsForValue()这里的Value并不是 Java 中任意类型的 Value而是对应 Redis 的 String 数据类型操作。Redis 虽然经常被描述为 Key-Value 数据库但 Value 并不只有普通字符串。Redis 还支持String Hash List Set ZSetSpring Data Redis 为不同数据类型提供了不同的操作入口。例如stringRedisTemplate.opsForValue();用于 String。stringRedisTemplate.opsForHash();用于 Hash。stringRedisTemplate.opsForList();用于 List。stringRedisTemplate.opsForSet();用于 Set。stringRedisTemplate.opsForZSet();用于 ZSet。所以opsForValue()可以理解为我要获取 Redis String 类型的操作接口。它并不是立即向 Redis 发送命令。真正的操作发生在后面的.set(user:name, 张三);五、第二步StringRedisTemplate 处理 Key 和 Value我们传入的是两个 Java 字符串user:name和张三但网络传输不能直接发送 JavaString对象。Java 对象只存在于当前 JVM 进程的内存中。Redis Server 不认识 Java 对象也不知道什么是java.lang.String因此在发送命令之前需要把 Java 字符串转换成字节数据。整体过程是Java String → 序列化 → byte[] → 网络传输StringRedisTemplate通常会按照字符串方式处理 Key 和 Value。例如user:name → 字符串编码 → 字节数组张三 → 字符串编码 → 字节数组这些字节才能通过网络发送给 Redis。六、什么是序列化序列化可以先理解为将当前程序中的对象转换成适合存储或传输的数据格式。例如 Java 中有一个对象User user new User( 1001L, 张三 );这个User对象存在于 JVM 堆内存中。Redis Server 无法直接接收这个 Java 对象。需要先转换成某种形式例如 JSON{ id: 1001, name: 张三 }再把 JSON 转换成字节通过网络发送。完整过程是Java User 对象 → JSON 字符串 → 字节数组 → 网络传输 → Redis 保存读取时则反过来Redis 字节数据 → JSON 字符串 → Java User 对象这就是序列化 反序列化对于当前示例stringRedisTemplate.opsForValue() .set(user:name, 张三);Key 和 Value 本身就是字符串所以序列化过程比较简单。但当后面使用RedisTemplate保存对象时序列化会成为一个非常重要的问题。七、第三步获取 Redis 连接要向 Redis 发送命令Spring Boot 必须先与 Redis Server 建立连接。连接信息通常写在配置文件中spring: data: redis: host: 127.0.0.1 port: 6379如果 Redis 配置了用户名和密码还可能包括spring: data: redis: host: 127.0.0.1 port: 6379 username: app_user password: redis_password这些配置告诉客户端Redis 在哪台服务器 监听哪个端口 使用什么身份连接整体目标地址可以理解为127.0.0.1:6379其中127.0.0.1 → Redis 所在主机 6379 → Redis 监听端口八、每次 SET 都会重新建立 TCP 连接吗一般不会把正常使用方式理解成执行一次 SET → 建立一次连接 → 发送命令 → 关闭连接如果每执行一条命令都重新完成 TCP 连接建立和关闭额外开销会比较大。正常情况下Spring 容器会管理 Redis 连接相关组件客户端也会复用已经建立的连接或者通过连接管理机制获取可用连接。可以先简单理解为Spring Boot 启动 → 创建 Redis 客户端相关组件 → 建立或准备 Redis 连接 执行 Redis 命令 → 获取可用连接 → 发送命令 → 接收结果 → 连接继续复用具体的连接管理方式与使用的客户端和配置有关。现阶段需要掌握的是StringRedisTemplate不负责保存数据但它会通过底层连接把命令发送给 Redis Server。九、第四步底层客户端生成 Redis 命令业务代码是stringRedisTemplate.opsForValue() .set(user:name, 张三);底层要表达成 Redis 能理解的命令SET user:name 张三但通过 TCP 发送时并不是简单地把控制台中看到的文本原样复制过去。客户端需要按照 Redis 通信协议组织命令数据。可以先把它抽象理解为命令名称SET 参数1user:name 参数2张三底层客户端会将它编码为 Redis Server 能够解析的字节格式。整体过程是Java API 调用 → Redis 命令对象 → 协议编码 → 字节数据最终这些字节才会进入 TCP 连接。十、Redis 协议解决了什么问题Java、Python、Node.js、Go 等语言都可以操作 Redis。例如Java 客户端 Python 客户端 Node.js 客户端 Go 客户端这些语言的对象模型完全不同。Redis Server 不可能分别理解Java String Python str JavaScript string Go string所以客户端和 Redis Server 之间必须约定一种统一通信格式。这个统一格式解决了命令如何表示 参数如何分隔 字符串长度如何表达 数组如何表达 返回值如何表示 错误信息如何表示只要客户端按照协议发送数据Redis Server 就能够解析。因此Java 客户端 → 把 Java 调用转换成 Redis 协议 Redis Server → 按照 Redis 协议解析命令客户端语言可以不同但发送给 Redis Server 的命令语义是一致的。十一、可以把 Redis 客户端和 Retrofit 再做一次类比Android 调用后端接口时我们可能写apiService.getUserInfo(1001)表面上是 Kotlin 方法调用。底层过程可能是Kotlin 方法调用 → Retrofit 解析注解 → OkHttp 构造 HTTP 请求 → 通过 TCP/TLS 发送 → Spring Boot 接收请求Redis 操作也是类似的Java 方法调用 → StringRedisTemplate → Redis 客户端构造命令 → 通过 TCP 发送 → Redis Server 接收命令对应关系可以理解为Android 请求后端Spring Boot 请求 RedisAPI 方法RedisTemplate 方法RetrofitSpring Data RedisOkHttpLettuce、Jedis 等客户端HTTP 请求格式Redis 协议格式Spring Boot ServerRedis ServerController 执行业务Redis 执行命令两者的共同点是本地的方法调用只是入口真正的操作发生在另一个进程或另一台服务器中。十二、第五步通过 TCP Socket 发送命令客户端把命令编码成字节后会通过 TCP Socket 发送给 Redis。链路可能是Spring Boot 服务器 127.0.0.1:随机端口 ↓ TCP Redis Server 127.0.0.1:6379如果 Redis 在另一台服务器上则可能是应用服务器 10.0.0.10 ↓ 内网 TCP Redis 服务器 10.0.0.20:6379TCP 负责把字节从客户端可靠地传输到 Redis Server。这里需要分清Redis 协议 → 规定发送的数据表示什么意思 TCP → 负责把这些字节传送到对方它们不是同一个概念。可以类比 HTTPHTTP → 规定请求行、请求头、请求体的含义 TCP → 负责传输 HTTP 数据Redis 也是Redis 协议 → 规定命令和返回值如何表达 TCP → 负责传输协议数据十三、第六步Redis Server 接收命令Redis Server 启动后会监听指定端口。常见默认端口是6379当客户端连接到 Redis 后Redis 会接收客户端发来的字节数据。可以把这个过程简单理解为Redis 监听端口 ↓ 收到客户端数据 ↓ 按照 Redis 协议解析 ↓ 识别出 SET 命令Redis 解析后会得到命令SET Keyuser:name Value张三此时 Redis 才真正知道客户端希望执行什么操作。十四、第七步Redis 查找对应的命令处理逻辑Redis 支持大量命令例如SET GET DEL EXPIRE INCR HSET LPUSH SADD ZADD收到命令后Redis 需要找到对应的处理逻辑。例如SET user:name 张三Redis 会识别出这是一个 SET 命令然后执行 SET 对应的数据写入流程。如果命令不存在例如SE user:name 张三Redis 无法识别就会返回错误。如果参数数量不对例如SET user:nameRedis 也会返回参数错误。因此 Redis Server 并不是接收到任意文本后直接保存而是会先完成命令识别 参数检查 权限判断 数据操作 结果返回十五、第八步Redis 修改自己的内存数据命令解析成功后Redis 才开始真正修改数据。原本 Redis 中可能没有user:name执行SET user:name 张三之后Redis 自己管理的内存中会出现user:name → 张三注意这份数据不在 Spring Boot 的 JVM 中。整体内存关系是Spring Boot 进程 ├── StringRedisTemplate 对象 ├── Redis 客户端对象 └── 业务对象 Redis 进程 └── user:name → 张三Spring Boot 只是发出了请求。真正的数据属于 Redis 进程。十六、如果 Key 已经存在会发生什么假设 Redis 中已经有user:name → 李四再次执行SET user:name 张三默认情况下原来的值会被覆盖执行前 user:name → 李四 执行后 user:name → 张三这和给变量重新赋值很像String userName 李四; userName 张三;但二者的运行位置不同。Java 变量赋值修改当前 JVM 内存RedisSET通过网络通知 Redis 修改 Redis 进程内存十七、SET 还可以带哪些条件最简单的 SET 是SET user:name 张三还可以设置过期时间SET verify:code:13800000000 9527 EX 300表示保存验证码 9527 300 秒后过期还可以要求 Key 不存在时才能写入SET submit:user:1001 1 NX这里的NX可以理解为Not Exists 只有 Key 不存在时才设置成功也可以要求 Key 已存在时才更新SET user:name 张三 XX这里的XX表示只有 Key 已存在时才更新因此SET 并不只是最简单的赋值还可以附带条件和生命周期。这些能力都由 Redis Server 在执行命令时处理。十八、第九步Redis 返回执行结果Redis 成功执行SET user:name 张三通常会返回成功结果。在redis-cli中可能看到OK底层返回的不是 JavaString对象而是按照 Redis 协议编码的响应数据。客户端收到响应后会完成网络字节 → 协议解码 → Java 可识别的结果然后 Spring Data Redis 再把结果交还给上层调用代码。因此返回链路是Redis 执行完成 ↓ 生成响应 ↓ 通过 TCP 返回 ↓ Redis 客户端接收 ↓ 协议解码 ↓ Spring Data Redis 转换结果 ↓ 业务代码继续执行十九、为什么 set() 方法通常没有返回值我们使用stringRedisTemplate.opsForValue() .set(user:name, 张三);时set()方法通常被当成一个写入操作使用业务层并不一定需要直接处理 Redis 返回的OK。Spring Data Redis 已经对底层返回进行了封装。如果执行失败例如Redis 无法连接 连接超时 认证失败 命令执行异常客户端通常会将异常传递到 Java 程序。因此业务层看到的可能不是返回 false而是抛出 Redis 访问相关异常。这意味着调用set()并不等于数据一定已经成功写入网络和服务异常仍然需要考虑。二十、如果 Redis 没启动会发生什么假设配置为spring: data: redis: host: 127.0.0.1 port: 6379但本机没有启动 Redis。此时执行stringRedisTemplate.opsForValue() .set(user:name, 张三);底层客户端尝试连接127.0.0.1:6379但没有 Redis Server 监听该端口。整体过程变成Java 发起 Redis 操作 ↓ 客户端尝试获取连接 ↓ 连接 Redis 失败 ↓ 抛出连接异常这再次说明引入 Redis 依赖 ≠ 已经拥有 Redis ServerMaven 或 Gradle 引入的是Java 客户端和 Spring 封装真正的 Redis 服务仍然需要单独安装并启动。二十一、如果 Redis 在远程服务器上会怎样假设 Spring Boot 配置spring: data: redis: host: 10.0.0.20 port: 6379整体调用链不变StringRedisTemplate → Redis 客户端 → TCP → Redis Server只不过数据需要经过网络从应用服务器发送到 Redis 服务器。这时可能增加一些新的问题网络延迟 网络中断 防火墙拦截 密码错误 连接数量限制 Redis 服务不可用所以 Redis 虽然速度很快但它仍然是一次远程服务调用。不能把它完全等同于读取 Java 本地变量。二十二、Redis 为什么快但仍然比本地变量多一步读取 Java 本地变量String name userName;链路是当前代码 → 当前 JVM 内存读取 RedisString name stringRedisTemplate .opsForValue() .get(user:name);链路是当前代码 → 序列化 → 客户端 → TCP → Redis → 查询内存 → TCP 返回 → 反序列化显然Redis 比读取本地变量多了很多步骤。所以从调用距离来看JVM 本地变量 通常比访问 Redis 更快但 Redis 的价值不只是单次访问速度。它还解决了多个实例共享 自动过期 原子操作 数据结构 集中管理 跨服务器访问因此可以这样理解本地变量 → 距离最近但只能当前进程使用 Redis → 多了一次网络调用但多个进程可以共享二十三、为什么多个后端实例可以看到同一个值假设系统部署了三个 Spring Boot 实例Spring Boot A Spring Boot B Spring Boot C它们都连接10.0.0.20:6379A 执行stringRedisTemplate.opsForValue() .set(user:name, 张三);实际修改的是Redis Server 内存 user:name → 张三B 执行String name stringRedisTemplate .opsForValue() .get(user:name);B 访问的也是同一个 Redis Server。所以可以读取到张三整体结构是Spring Boot A ─┐ Spring Boot B ─┼── Redis Server Spring Boot C ─┘并不是 A 把自己的 Java 变量同步给了 B。而是 A 和 B 都在访问第三方 Redis 服务。二十四、保存 Java 对象时又多了什么步骤保存字符串stringRedisTemplate.opsForValue() .set(user:name, 张三);相对简单因为 Key 和 Value 都是字符串。但如果保存用户对象User user new User( 1001L, 张三 );不能直接把对象发给 Redis。常见做法之一是先转换成 JSONString userJson objectMapper .writeValueAsString(user);然后写入stringRedisTemplate.opsForValue() .set( user:info:1001, userJson );完整过程是Java User 对象 ↓ JSON 序列化 ↓ 字符串字节 ↓ Redis 客户端 ↓ TCP Redis Server ↓ 保存 JSON 数据读取时String userJson stringRedisTemplate .opsForValue() .get(user:info:1001); User user objectMapper.readValue( userJson, User.class );过程是Redis 返回 JSON ↓ Java 获取字符串 ↓ JSON 反序列化 ↓ 恢复成 User 对象所以 Redis 保存 Java 对象时必须考虑序列化格式。二十五、为什么序列化格式必须统一假设写入时使用 JSON{ id: 1001, name: 张三 }读取时也应该按照 JSON 解析。如果写入端使用一种格式读取端却按照另一种格式解析就可能失败。例如写入端 → JDK 二进制序列化 读取端 → 按照 JSON 解析两边格式不一致无法正确还原对象。这也是为什么多个服务共用 Redis 时需要提前约定Key 命名规则 Value 数据格式 字符编码 对象字段结构 过期时间否则即使它们连接的是同一个 Redis也可能无法正确理解彼此写入的数据。二十六、一次 GET 命令的链路理解完 SET再看 GET 就很容易了。Java 代码String name stringRedisTemplate .opsForValue() .get(user:name);完整链路1. Java 调用 get() 2. StringRedisTemplate 序列化 Key 3. Spring Data Redis 获取连接 4. 客户端生成 GET 命令 5. 通过 TCP 发送 GET user:name 6. Redis Server 解析 GET 命令 7. Redis 在自己的内存中查找 Key 8. 找到 Value张三 9. Redis 编码返回结果 10. 客户端接收并解码 11. StringRedisTemplate 转换为 Java String 12. 业务代码得到“张三”如果 Key 不存在Redis 找不到 user:name → 返回空结果 → Java 最终得到 null所以代码通常需要处理String name stringRedisTemplate .opsForValue() .get(user:name); if (name null) { // Key 不存在或者已经过期 }二十七、一次 DEL 命令的链路Java 代码Boolean deleted stringRedisTemplate.delete(user:name);底层对应DEL user:name整体过程Java 调用 delete() ↓ Key 被序列化 ↓ 客户端生成 DEL 命令 ↓ TCP 发送 ↓ Redis 找到 Key ↓ 从 Redis 数据中删除 ↓ 返回删除数量如果 Key 存在并被成功删除删除数量1如果 Key 原本不存在删除数量0Spring Data Redis 会把底层结果转换成适合 Java 使用的返回值。二十八、一条命令的完整时序图以stringRedisTemplate.opsForValue() .set(user:name, 张三);为例完整时序可以整理为业务代码 | | set(user:name, 张三) ↓ StringRedisTemplate | | 序列化 Key 和 Value ↓ Spring Data Redis | | 获取 Redis 连接 ↓ Lettuce / Jedis 等客户端 | | 编码 SET 命令 | 通过 TCP 发送 ↓ Redis Server | | 解析命令 | 校验参数 | 修改 Redis 内存 | 生成响应 ↓ Redis 客户端 | | 接收响应 | 协议解码 ↓ Spring Data Redis | | 转换结果 ↓ 业务代码继续执行这就是一条 Redis 命令的完整生命周期。二十九、这条链路中哪里可能失败一条 SET 命令并不一定永远成功。可能出现问题的位置包括1. 配置错误Redis 地址错误 端口错误 密码错误 数据库编号错误2. Redis 没有启动客户端连接 6379 → 没有服务监听 → 连接失败3. 网络问题网络中断 防火墙拦截 连接超时 服务器不可达4. 序列化失败例如对象中存在无法正常序列化的字段。Java 对象 → JSON 转换失败 → 命令尚未发送5. Redis 内存问题Redis 可能达到内存限制或者根据配置拒绝某些写入。6. 命令类型错误例如一个 Key 原本保存的是 Hash却使用了不匹配的数据结构命令。因此真实项目中还需要考虑异常处理 超时配置 重试策略 降级方案 日志记录 监控告警这些属于后面的工程化内容。三十、不要把 Redis API 当成本地方法调用下面这段代码看起来很普通stringRedisTemplate.opsForValue() .set(user:name, 张三);但它本质上不是简单的本地赋值。本地赋值userName 张三;只发生在当前 JVM 中。Redis 操作则包含序列化 连接获取 协议编码 网络传输 服务端执行 结果返回所以在设计代码时应当意识到每一次 Redis 操作本质上都是一次对外部数据服务的访问。这意味着不能毫无节制地在循环中频繁调用 Redis。例如for (Long userId : userIds) { String value stringRedisTemplate .opsForValue() .get(user: userId); }如果userIds有 1000 个就可能产生大量 Redis 请求。后面需要学习批量操作 Pipeline 合理的数据结构设计 减少网络往返三十一、Redis 快不等于调用没有成本Redis 主要操作内存因此处理单条简单命令通常很快。但一次完整调用仍然可能包含Java 方法调用 序列化 客户端排队 网络发送 Redis 执行 网络返回 反序列化如果每个业务接口都随意发送几十次甚至上百次 Redis 请求性能仍然可能受到影响。所以 Redis 使用的核心不是反正 Redis 快随便调用而是选择合适的数据结构 设计清晰的 Key 减少无意义的请求 控制数据大小 合理设置过期时间三十二、重新理解 RedisTemplate现在可以重新理解RedisTemplate。它不是保存 Redis 数据的容器而是Spring Boot 进程中用于操作 Redis 的客户端模板和高级封装。它主要帮助我们完成选择 Redis 数据结构 序列化 Key 和 Value 获取连接 执行底层命令 转换返回结果 统一异常处理而真正的数据仍然保存在Redis Server 进程所以RedisTemplate 重建 → Redis 数据不会因此消失 Spring Boot 重启 → Redis 数据通常仍由 Redis Server 保管 Redis Server 被清空 → Java 中的 RedisTemplate 无法恢复这些数据三十三、本篇总结这一篇沿着一条SET命令分析了 Java 操作 Redis 的完整过程。我们写下stringRedisTemplate.opsForValue() .set(user:name, 张三);之后主要经历了以下步骤1. 业务代码调用 StringRedisTemplate 2. Spring Data Redis 处理操作请求 3. Key 和 Value 被序列化成字节 4. 底层 Redis 客户端获取连接 5. 客户端生成并编码 SET 命令 6. 命令通过 TCP Socket 发送 7. Redis Server 接收并解析命令 8. Redis 修改自己管理的内存数据 9. Redis 返回执行结果 10. Java 客户端接收并转换结果这里最需要记住的是Java 业务代码 → 表达操作意图 StringRedisTemplate → 提供 Java 操作接口 Spring Data Redis → 提供统一封装 Redis 客户端 → 负责连接与通信 Redis Server → 真正执行命令并保存数据最终可以用一句话概括Java 调用 Redis API并不是直接修改 Redis 内存而是通过 Redis 客户端将命令编码后经 TCP 发送给 Redis Server由 Redis Server 执行命令、修改自己的数据再将结果返回给 Java。下一篇预告下一篇继续进入 Redis 最基础的数据模型《Redis 的 Key-Value 模型到底是什么Key 应该怎么设计》下一篇会重点讲清楚Redis 的 Key 和 Value 分别是什么 为什么 Redis Key 通常使用冒号分隔 项目名、业务名和唯一标识如何组合 多个项目共用 Redis 时如何避免冲突 Key 太长或太短分别有什么问题 为什么不能把 Redis 当成 MySQL 模糊查询 如何为用户、Token、验证码和缓存设计 Key下一篇开始我们将正式进入 Redis 命令和数据设计阶段。