第四篇:Redis 的 Key-Value 模型到底是什么?Key 应该怎么设计?
前面三篇,我们已经把 Redis 的运行模型串了起来:
Spring Boot 业务代码 ↓ StringRedisTemplate ↓ Lettuce 等 Redis 客户端 ↓ TCP Redis Server ↓ Redis 管理的内存数据我们也知道,执行:
stringRedisTemplate.opsForValue() .set("user:name", "张三");本质上是 Java 客户端向 Redis Server 发送了一条命令:
SET user:name 张三Redis 收到命令后,会在自己管理的内存中保存一条数据:
user:name → 张三但理解到这里之后,还会出现一系列问题:
Redis 的 Key 到底是什么? Value 是不是只能保存字符串? 为什么 Redis Key 经常使用冒号分隔? 多个项目共用一个 Redis 时,怎么避免 Key 冲突? Key 是越详细越好,还是越短越好? Redis 能不能像 MySQL 一样,通过条件查询 Key?这些问题看起来只是命名细节,实际上会直接影响:
数据是否容易管理 不同业务是否会冲突 缓存是否容易清理 线上问题是否容易排查 Redis 内存是否被浪费 后续系统是否容易扩展这一篇正式进入 Redis 的数据设计阶段,重点讲清楚:
Redis 的 Key-Value 模型是什么,以及真实项目中应该怎样设计 Key。
一、Redis 最基础的数据模型
Redis 最基础的数据模型可以表示为:
Key → Value例如:
user:name → 张三其中:
user:name → Key 张三 → Value可以暂时把它类比成 Java 中的 Map:
Map<String, String> map = new HashMap<>(); map.put("user:name", "张三");读取:
String name = map.get("user:name");Redis 中对应:
SET user:name 张三 GET user:name两者在使用形式上比较接近:
Java Map Key → Value Redis Key → Value但 Redis 不是当前 Java 进程中的普通 Map。
区别在于:
Java Map → 数据位于当前 JVM 进程 Redis → 数据位于独立的 Redis Server 进程Java 访问 Redis 时,需要通过 Redis 客户端和网络通信。
二、Redis 中的 Key 可以理解成什么
可以把 Redis Key 理解成一条数据的唯一名称。
例如:
user:1001:name表示:
用户 1001 的姓名再例如:
article:2001:view表示:
文章 2001 的阅读量Redis 不会主动理解这些 Key 的业务含义。
对 Redis 来说:
user:1001:name article:2001:view verify:code:13800000000都只是不同的 Key。
冒号、单词和数字的业务含义,都是开发者自己设计的。
Redis 只负责:
根据完整 Key 查找 Value例如:
GET user:1001:nameRedis 会查找完整名称为:
user:1001:name的 Key。
它不会主动分析:
user 是用户业务 1001 是用户 ID name 是字段名称这些分层只是我们为了方便管理而建立的命名规范。
三、Redis Key 必须唯一吗
在同一个 Redis 逻辑数据库中,Key 是唯一的。
例如第一次执行:
SET user:name 张三Redis 中的数据是:
user:name → 张三再次执行:
SET user:name 李四原来的值会被覆盖:
执行前: user:name → 张三执行后: user:name → 李四因为 Redis 不会同时保存两个完全相同的 Key。
可以类比 Java Map:
map.put("user:name", "张三"); map.put("user:name", "李四");最终:
map.get("user:name");得到的是:
李四所以:
同一个 Redis 数据库中,一个完整 Key 只能对应一条 Redis 数据。
如果业务 Key 设计不合理,就可能出现数据覆盖。
四、Redis 的 Value 只能是字符串吗
不是。
Redis 经常被称为 Key-Value 数据库,但这里的 Value 并不只是一段普通字符串。
Redis 常见的核心数据类型包括:
String Hash List Set ZSet例如,Key:
user:1001对应的 Value 可以是 String 类型:
user:1001 → {"id":1001,"name":"张三"}也可以是 Hash 类型:
user:1001 ├── id → 1001 ├── name → 张三 └── phone → 13800000000再例如:
article:rank对应的 Value 可以是 ZSet,用于保存排行榜:
文章 A → 100 分 文章 B → 80 分 文章 C → 120 分所以,更准确的模型是:
一个 Key → 对应一种 Redis 数据类型的 Value例如:
user:1001:name → String user:1001 → Hash task:queue → List article:liked:1001 → Set article:rank → ZSet同一个 Key 在同一时间只能对应一种数据类型。
五、一个 Key 不能同时是两种数据类型
假设执行:
SET user:1001 张三此时:
user:1001 → String 类型如果随后把它当成 Hash 操作:
HSET user:1001 age 30Redis 会返回类型错误。
因为:
user:1001已经是 String,不能同时又作为 Hash 使用。
这类错误通常会看到类似提示:
WRONGTYPE Operation against a key holding the wrong kind of value这说明 Redis 的 Key 不只是对应一个值,还对应一个确定的数据类型。
可以使用:
TYPE user:1001查看 Key 的数据类型。
如果返回:
string表示它是 String。
如果返回:
hash表示它是 Hash。
因此,Key 命名时最好能够让人看出它大致保存什么数据。
六、Redis 为什么常用冒号分隔 Key
实际项目中,经常会看到这样的 Key:
user:info:1001 login:token:abc123 verify:code:13800000000 article:view:2001冒号在 Redis 中并没有特殊的层级语法。
下面两个 Key 对 Redis 来说没有本质区别:
user:info:1001user_info_1001它们都只是一段完整字符串。
之所以通常使用冒号,是因为冒号能够让 Key 形成清晰的业务层次。
例如:
ark:user:info:1001可以拆解为:
ark → 项目名称 user → 业务模块 info → 数据类型或业务用途 1001 → 唯一标识这种命名方式便于开发者阅读、排查和统一管理。
因此冒号是一种约定俗成的命名分隔符,而不是 Redis 的特殊语法。
七、推荐的 Key 基本结构
真实项目中,可以采用下面的基本结构:
项目名:业务模块:数据用途:唯一标识例如:
ark:user:info:1001其中:
ark → ark-backend 项目 user → 用户模块 info → 用户信息 1001 → 用户 ID再例如验证码:
ark:verify:code:13800000000可以拆成:
ark → 项目名称 verify → 验证业务 code → 验证码 13800000000 → 手机号登录 Token:
ark:login:token:abc123文章访问量:
ark:article:view:2001接口限流:
ark:limit:user:1001防重复提交:
ark:submit:order:user:1001这种结构的核心目的不是追求格式漂亮,而是保证:
看见 Key 就知道属于哪个项目 看见 Key 就知道属于哪个业务 看见 Key 就知道保存什么数据 看见 Key 就知道对应哪个对象八、为什么建议加项目名前缀
假设一台 Redis 被三个项目共同使用:
档案项目 电梯项目 商城项目如果三个项目都使用:
user:1001就可能发生冲突。
例如档案项目写入:
SET user:1001 张三商城项目又写入:
SET user:1001 李四最终档案项目原来的数据会被覆盖。
如果增加项目前缀:
archive:user:1001 lift:user:1001 mall:user:1001它们就是三个不同的 Key。
因此,共用 Redis 时,通常应该使用:
项目简称:业务名称:唯一标识例如你的项目可以使用:
ark:user:info:1001 ark:login:token:abc123 ark:verify:code:13800000000这样可以减少不同项目之间的数据污染。
九、Redis 有多个数据库,为什么还要加项目前缀
Redis 默认可以提供多个逻辑数据库。
有些环境中可能会看到:
db0 db1 db2 ...可以使用:
SELECT 1切换到数据库 1。
看起来似乎可以这样隔离:
项目 A 使用 db0 项目 B 使用 db1 项目 C 使用 db2但生产环境中,通常不建议完全依赖逻辑数据库实现项目隔离。
原因是它们仍然属于:
同一个 Redis 实例 同一个 Redis 进程 同一份服务器资源例如某个项目大量写入数据,占满 Redis 内存,其他逻辑数据库也会受到影响。
另外,Redis Cluster 模式下通常只使用数据库 0,不支持像单机 Redis 一样自由切换多个逻辑数据库。
因此,更通用的做法是:
重要项目使用独立 Redis 实例 或者至少使用清晰的 Key 前缀项目名前缀仍然非常重要。
十、用户相关 Key 应该怎么设计
假设需要缓存用户 1001 的基本信息。
可以设计为:
ark:user:info:1001Value 可以保存用户 JSON:
{ "id": 1001, "name": "张三", "phone": "13800000000" }Redis 命令:
SET ark:user:info:1001 "{\"id\":1001,\"name\":\"张三\"}"Spring Boot 中可以写:
String key = "ark:user:info:" + userId; stringRedisTemplate.opsForValue() .set(key, userJson);如果缓存用户地址:
ark:user:address:1001如果缓存用户权限:
ark:user:permission:1001如果记录用户登录失败次数:
ark:user:login-fail:1001同一个用户可以有多个不同用途的 Key:
ark:user:info:1001 ark:user:address:1001 ark:user:permission:1001 ark:user:login-fail:1001关键是每个 Key 的用途必须清晰。
十一、Token 相关 Key 应该怎么设计
假设用户登录后生成 Token:
abc123一种设计方式是:
ark:login:token:abc123 → userId 1001Redis 命令:
SET ark:login:token:abc123 1001 EX 7200表示:
Token:abc123 对应用户:1001 有效期:7200 秒Spring Boot 中:
String key = "ark:login:token:" + token; stringRedisTemplate.opsForValue() .set( key, String.valueOf(userId), 2, TimeUnit.HOURS );验证 Token 时:
String userId = stringRedisTemplate .opsForValue() .get("ark:login:token:" + token);如果返回null,可能表示:
Token 不存在 Token 已经过期 Token 已经被服务端删除也可以反向按照用户设计:
ark:login:user:1001 → abc123这种设计更方便实现:
一个用户只允许一个 Token 新设备登录时覆盖旧 Token 按用户 ID 强制下线Key 的设计应该根据查询方向决定。
十二、验证码 Key 应该怎么设计
短信验证码通常以手机号作为唯一标识:
ark:verify:code:13800000000写入:
SET ark:verify:code:13800000000 9527 EX 300表示:
手机号:13800000000 验证码:9527 有效期:300 秒Spring Boot 中:
String key = "ark:verify:code:" + phone; stringRedisTemplate.opsForValue() .set( key, code, 5, TimeUnit.MINUTES );校验:
String redisCode = stringRedisTemplate .opsForValue() .get(key);验证码 Key 设计需要能够回答:
这是什么业务的验证码? 这是哪个用户或手机号的验证码? 验证码有效期是多少?如果同一个系统有多种验证码,还可以继续细分:
ark:verify:login:13800000000 ark:verify:register:13800000000 ark:verify:reset-password:13800000000否则不同业务可能相互覆盖。
例如:
登录验证码和:
修改密码验证码都使用:
ark:verify:code:13800000000后发送的验证码会覆盖前一个。
因此,应根据业务场景增加类型:
项目名:验证码业务:场景:手机号十三、文章访问量 Key 应该怎么设计
记录文章 2001 的访问量,可以设计为:
ark:article:view:2001初始化:
SET ark:article:view:2001 0每访问一次:
INCR ark:article:view:2001查看:
GET ark:article:view:2001这里的 Key 结构是:
ark → 项目 article → 文章模块 view → 阅读量 2001 → 文章 ID如果还要记录点赞量:
ark:article:like:2001评论数:
ark:article:comment:2001收藏量:
ark:article:favorite:2001这样的 Key 结构比较清晰。
十四、Key 中应该使用用户 ID 还是手机号
假设要保存用户缓存,可以选择:
ark:user:info:1001也可以选择:
ark:user:info:13800000000通常更推荐使用稳定、唯一且较短的内部 ID:
ark:user:info:1001原因包括:
1. 用户 ID 通常更加稳定
手机号可能会更换。
用户 ID 通常不会变化。
2. 避免敏感信息直接出现在 Key 中
手机号、身份证号、邮箱等信息直接出现在 Redis Key 中,可能增加日志和运维排查过程中的隐私暴露风险。
3. 用户 ID 通常更短
短 Key 可以减少一定内存占用。
因此,正式业务数据通常优先使用:
内部唯一 ID但验证码场景天然需要通过手机号查询,此时手机号作为 Key 的一部分是合理的。
Key 设计需要根据实际查询方式决定。
十五、Key 是越长越好吗
不是。
一个极其详细的 Key 可能是:
ark-backend-project:user-module:user-information-cache:user-id:1001它确实非常清晰,但也过于冗长。
Redis 中通常会存在大量 Key。
如果每个 Key 都很长,就会增加内存占用。
假设有一百万个 Key,每个 Key 多出几十个字符,整体内存差异就会非常明显。
因此,Key 设计需要在:
可读性 和 空间占用之间取得平衡。
例如:
ark:user:info:1001通常已经足够表达业务含义。
没有必要写成:
ark-backend-system:user-management-module:user-basic-information-cache:1001推荐原则是:
Key 应该清晰,但不要为了描述完整而无限增长。
十六、Key 是越短越好吗
也不是。
例如把用户缓存设计成:
u:1001确实很短。
但团队成员看到它时,可能无法确定:
u 是 user 还是 upload? 保存的是用户信息还是用户状态? 这是哪个项目的用户?再例如:
t:a1几乎无法通过 Key 判断业务含义。
这种 Key 虽然节省少量空间,但会增加维护成本。
线上排查时,运维或开发看到:
u:1001 t:a1 v:2001很难判断它们分别是什么。
因此:
Key 太长 → 浪费内存,书写复杂 Key 太短 → 缺少业务含义,难以维护更合理的是使用稳定、简洁、可读的缩写:
ark:user:info:1001 ark:login:token:abc123 ark:article:view:2001十七、Key 命名应该统一大小写
Redis Key 区分大小写。
下面是三个不同的 Key:
user:1001 User:1001 USER:1001Redis 不会把它们当成同一个 Key。
例如:
SET user:1001 张三 SET User:1001 李四此时 Redis 中会存在两条数据。
因此,团队必须统一大小写规范。
通常推荐全部使用小写:
ark:user:info:1001不要混用:
Ark:User:Info:1001 ARK:user:INFO:1001全小写的优势是:
风格统一 减少输入错误 避免大小写冲突 便于团队协作十八、Key 中应该使用空格吗
虽然 Redis 的 Key 可以包含很多字符,但不建议在业务 Key 中使用空格。
例如:
ark user info 1001在命令行中需要额外处理:
SET "ark user info 1001" 张三这会增加:
命令输入难度 日志阅读难度 脚本处理难度 排查问题的复杂度因此一般使用:
冒号 短横线 下划线其中最常见的是冒号:
ark:user:info:1001业务单词内部可以使用短横线:
ark:user:reset-password:1001核心是保持团队统一。
十九、Key 中能不能使用中文
Redis 的 Key 本质上可以保存字节数据,因此技术上可以使用中文。
例如:
SET 用户:1001:姓名 张三但生产项目中一般不建议大量使用中文 Key。
原因包括:
不同工具显示可能不一致 脚本处理不方便 跨语言协作不方便 编码问题更难排查 命令行输入效率较低更推荐:
ark:user:name:1001Value 中保存中文通常没有问题:
SET ark:user:name:1001 张三也就是:
Key → 建议使用规范英文和数字 Value → 根据业务正常保存中文内容二十、Key 能不能像数据库字段一样模糊查询
Redis 的主要使用方式是通过完整 Key 查询。
例如:
GET ark:user:info:1001Redis 可以快速找到对应数据。
但是有时我们可能想查询:
所有 ark:user 开头的 KeyRedis 提供了相关命令,例如:
KEYS ark:user:*它可能返回:
ark:user:info:1001 ark:user:info:1002 ark:user:address:1001 ark:user:permission:1001但生产环境中要非常谨慎使用KEYS。
二十一、为什么生产环境不建议随便使用 KEYS
KEYS会扫描符合模式的所有 Key。
例如:
KEYS *表示查找当前数据库中的所有 Key。
如果 Redis 中只有几十个 Key,问题可能不明显。
但如果 Redis 中有:
几十万 几百万 甚至更多 Key执行KEYS *可能占用 Redis 较长时间。
Redis 在处理这个命令期间,其他请求可能受到影响。
因此生产环境中通常不建议随意执行:
KEYS *或者:
KEYS ark:user:*尤其是在数据量较大时。
Redis 不是为了让我们像数据库一样频繁模糊搜索 Key 而设计的。
正确思路应该是:
在写入数据之前,就设计好明确、可直接计算出来的 Key。
例如通过用户 ID 得到:
String key = "ark:user:info:" + userId;然后直接查询:
stringRedisTemplate.opsForValue().get(key);二十二、需要遍历 Key 时使用什么
如果确实需要逐步扫描 Key,可以使用:
SCAN例如:
SCAN 0 MATCH ark:user:* COUNT 100SCAN会以游标方式分批返回结果,而不是一次性扫描完所有 Key。
可以简单理解为:
KEYS → 一次性找完 SCAN → 分批逐步查找SCAN对线上环境相对友好,但也不代表可以毫无成本地频繁使用。
应用业务代码中,最好仍然通过明确的 Key 直接访问。
如果某个业务经常需要:
查询所有用户缓存 查询某类全部 Key 按照多个条件筛选可能说明数据结构设计不合理,或者这部分需求更适合数据库、Set、ZSet 等结构。
二十三、如何检查一个 Key 是否存在
可以使用:
EXISTS ark:user:info:1001如果 Key 存在,返回:
1如果不存在,返回:
0Spring Boot 中可以写:
Boolean exists = stringRedisTemplate .hasKey("ark:user:info:1001");但实际业务中不要为了查询一个值,先执行:
EXISTS再执行:
GET例如下面这种写法可能会产生两次 Redis 请求:
if (Boolean.TRUE.equals( stringRedisTemplate.hasKey(key) )) { return stringRedisTemplate .opsForValue() .get(key); }很多情况下可以直接:
String value = stringRedisTemplate .opsForValue() .get(key); if (value == null) { // Key 不存在或已经过期 }这样只需要一次访问。
EXISTS适合确实只关心 Key 是否存在的场景。
二十四、如何查看 Key 的数据类型
可以使用:
TYPE ark:user:info:1001可能返回:
string或者:
hash list set zset none如果返回:
none表示 Key 不存在。
Spring Boot 中也可以获取类型,但在日常业务中一般不会频繁动态判断。
更合理的是:
在 Key 设计阶段就确定每类 Key 对应的数据类型。
例如统一约定:
ark:user:info:{userId} → String,保存用户 JSON ark:user:profile:{userId} → Hash,保存用户字段 ark:article:liked:{articleId} → Set,保存点赞用户 ID ark:article:rank → ZSet,保存文章排行榜不要让同一个业务 Key 在不同代码中被当成不同类型使用。
二十五、Key 是否应该设置过期时间
不是所有 Key 都必须过期,但大多数缓存数据都应该考虑过期时间。
例如用户信息缓存:
ark:user:info:1001可以设置 30 分钟过期:
SET ark:user:info:1001 "{...}" EX 1800验证码:
ark:verify:login:13800000000设置 5 分钟过期:
SET ark:verify:login:13800000000 9527 EX 300Token:
ark:login:token:abc123设置 2 小时过期:
SET ark:login:token:abc123 1001 EX 7200如果缓存数据永不过期,就可能出现:
旧数据长期存在 无用 Key 越来越多 Redis 内存持续增长 数据库数据已经更新,但缓存仍然陈旧因此设计 Key 时,不仅要考虑名称,还应该同时考虑:
Value 类型 过期时间 更新方式 删除方式 数据来源二十六、Key 删除后会发生什么
删除 Key:
DEL ark:user:info:1001之后:
GET ark:user:info:1001会返回空结果。
在缓存场景中,删除 Key 并不代表正式用户数据被删除。
例如:
MySQL → 仍然保存用户 1001 Redis → 用户缓存被删除下一次请求可能会:
先查询 Redis ↓ Redis 没有 ↓ 查询 MySQL ↓ 重新写入 Redis因此删除缓存 Key 的含义通常是:
让当前缓存副本失效,下一次重新从正式数据源加载。
这和删除 MySQL 中的正式数据完全不同。
二十七、Key 设计必须考虑如何删除
假设用户退出登录,需要删除 Token:
ark:login:token:abc123这个 Key 很容易计算:
String key = "ark:login:token:" + token;然后删除:
stringRedisTemplate.delete(key);但如果 Key 设计混乱,例如:
abc123-token-login-user-1001-ark虽然也能工作,但代码维护和问题排查会更加困难。
好的 Key 设计应该同时支持:
容易生成 容易查询 容易删除 容易判断用途 容易统一设置过期时间不能只考虑写入时是否方便。
二十八、不要把所有参数都塞进 Key
假设有一个商品查询接口:
分类:手机 品牌:华为 价格区间:3000~5000 排序:销量降序 页码:1 每页:20如果直接把所有参数拼进 Key:
mall:product:list:category-phone:brand-huawei: price-3000-5000:sort-sales-desc:page-1:size-20Key 会变得非常长,而且参数顺序、空值处理、字符编码都可能产生问题。
复杂查询缓存通常需要:
规范化请求参数 固定参数顺序 对参数生成摘要 控制缓存维度 防止产生海量不同 Key例如可以将规范化参数计算哈希:
mall:product:list:7f83a2...但这种做法会降低可读性,需要配合日志和文档。
目前学习阶段只需要知道:
简单实体缓存可以直接使用业务 ID;复杂查询缓存不能无脑拼接所有参数。
二十九、避免缓存 Key 数量无限增长
假设搜索接口按照搜索词缓存:
ark:search:keyword:redis ark:search:keyword:mysql ark:search:keyword:spring如果任何用户输入都生成一个新 Key,可能出现:
用户输入一次 → 生成一个新 Key 不同搜索词越来越多 → Redis Key 数量持续增长这种问题称为缓存空间失控的一种表现。
因此 Key 设计还要考虑:
业务可能产生多少个 Key Key 是否会自动过期 是否存在用户恶意输入 是否有最大数量限制 是否真的值得缓存Redis Key 设计不是简单的字符串命名,而是一种数据规模设计。
三十、推荐建立统一的 Key 常量
Spring Boot 项目中,不建议在业务代码里到处手写:
"ark:user:info:"例如:
String key = "ark:user:info:" + userId;另一个类又写:
String key = "ark:user:information:" + userId;这可能导致同一个业务产生两套 Key。
可以建立统一常量:
public final class RedisKeyConstants { private RedisKeyConstants() { } public static final String PROJECT_PREFIX = "ark"; public static final String USER_INFO = PROJECT_PREFIX + ":user:info:"; public static final String LOGIN_TOKEN = PROJECT_PREFIX + ":login:token:"; public static final String VERIFY_LOGIN = PROJECT_PREFIX + ":verify:login:"; public static final String ARTICLE_VIEW = PROJECT_PREFIX + ":article:view:"; }使用:
String key = RedisKeyConstants.USER_INFO + userId;这样可以统一维护。
三十一、进一步封装 Key 构建方法
如果 Key 结构较多,可以进一步封装:
public final class RedisKeys { private static final String PROJECT = "ark"; private RedisKeys() { } public static String userInfo(Long userId) { return PROJECT + ":user:info:" + userId; } public static String loginToken(String token) { return PROJECT + ":login:token:" + token; } public static String loginVerifyCode(String phone) { return PROJECT + ":verify:login:" + phone; } public static String articleView(Long articleId) { return PROJECT + ":article:view:" + articleId; } }使用:
String key = RedisKeys.userInfo(1001L);得到:
ark:user:info:1001这种方式的优势是:
统一命名 减少字符串拼写错误 方便批量调整前缀 业务含义更加清晰 更容易编写测试三十二、是否要把环境名称加进 Key
实际项目可能有多个环境:
开发环境 测试环境 预发布环境 生产环境如果它们错误地共用同一个 Redis,就可能互相覆盖数据。
可以在 Key 中增加环境前缀:
dev:ark:user:info:1001 test:ark:user:info:1001 prod:ark:user:info:1001但更合理的部署方式通常是:
不同环境使用不同 Redis 实例因为即使增加 Key 前缀,它们仍然共用:
同一份 Redis 内存 同一套资源 同一套故障范围环境前缀可以作为额外保护,但不能代替环境隔离。
三十三、一个实用的 Key 设计规范
对于当前阶段,可以先使用下面这套规范。
1. 全部小写
ark:user:info:1001避免:
ARK:User:Info:10012. 使用冒号分层
项目:模块:用途:唯一标识3. 使用稳定唯一标识
优先:
用户 ID 订单 ID 文章 ID 设备 ID4. 不使用无意义缩写
推荐:
ark:user:info:1001谨慎使用:
a:u:i:10015. 不包含不必要的敏感信息
尽量避免直接放入:
身份证号 完整手机号 密码 密钥 Token 明文日志Token 作为 Key 的组成部分有时难以避免,但日志中要注意脱敏。
6. 明确数据类型
例如提前约定:
ark:user:info:{id} → String JSON7. 明确过期时间
例如:
用户缓存:30分钟 验证码:5分钟 Token:2小时 防重复提交:10秒8. Key 必须可以直接计算
尽量避免业务代码依赖模糊扫描才能找到数据。
三十四、为 ark-backend 设计一组 Key
结合你的后端项目,可以先设计下面这组 Key。
用户信息缓存
ark:user:info:{userId}例如:
ark:user:info:1001用户地址缓存
ark:user:address:{userId}登录 Token
ark:login:token:{token}用户当前 Token
ark:login:user:{userId}登录验证码
ark:verify:login:{phone}注册验证码
ark:verify:register:{phone}登录失败次数
ark:login:fail:{account}防重复提交
ark:submit:{business}:{userId}例如:
ark:submit:create-order:1001接口限流
ark:limit:{api}:{userId}例如:
ark:limit:send-code:1001文章访问量
ark:article:view:{articleId}通过这一组 Key,可以看到统一结构:
ark → 项目名称 第二段 → 业务模块 第三段 → 数据用途 最后一段 → 唯一标识三十五、Redis Key 设计不是数据库表设计
MySQL 中可能有:
user 表字段包括:
id name phone status create_timeRedis 中不一定要为每个字段单独设计 Key:
ark:user:name:1001 ark:user:phone:1001 ark:user:status:1001 ark:user:create-time:1001这样会产生大量 Key,并增加多次网络请求。
更常见的是把用户作为一个整体缓存:
ark:user:info:1001 → 用户 JSON或者使用 Hash:
ark:user:info:1001 ├── name → 张三 ├── phone → 13800000000 └── status → 1因此 Redis Key 的粒度需要根据:
数据读取方式 字段更新频率 数据大小 网络请求次数 缓存失效方式综合判断。
不要机械地把 MySQL 每一行、每一列都转换成 Redis Key。
三十六、Redis 适合通过 Key 精准访问
MySQL 的典型访问方式是:
SELECT * FROM user WHERE status = 1 AND create_time > '2026-01-01';Redis 更典型的访问方式是:
GET ark:user:info:1001也就是:
已经知道唯一 Key → 直接获取数据如果业务需求是:
按多个条件筛选 关联多张表 动态排序 复杂分页 聚合统计通常更适合 MySQL、Elasticsearch 等系统。
Redis 更擅长:
根据明确 Key 获取数据 根据明确成员操作集合 按照分数查询排行榜 进行计数和临时状态管理所以 Redis Key 的核心设计目标是:
让业务代码能够根据已知参数直接计算出目标 Key。
三十七、本篇动手实践
打开redis-cli,执行下面的命令。
1. 保存用户信息
SET ark:user:info:1001 "{\"id\":1001,\"name\":\"张三\"}"读取:
GET ark:user:info:1001检查是否存在:
EXISTS ark:user:info:1001查看类型:
TYPE ark:user:info:10012. 保存验证码
SET ark:verify:login:13800000000 9527 EX 300读取:
GET ark:verify:login:13800000000查看剩余时间:
TTL ark:verify:login:138000000003. 记录访问量
SET ark:article:view:2001 0增加:
INCR ark:article:view:2001读取:
GET ark:article:view:20014. 查看当前 Key
学习环境中可以执行:
KEYS ark:*但需要记住:
生产环境不要随意使用
KEYS *。
三十八、本篇总结
这一篇正式介绍了 Redis 的 Key-Value 数据模型。
Redis 中的每条数据都通过唯一 Key 标识:
Key → Value但 Value 不只可以是普通字符串,还可以是:
String Hash List Set ZSet同一个 Key 在同一时间只能对应一种 Redis 数据类型。
Redis Key 中常见的冒号并不是特殊语法,而是一种方便管理的命名约定。
推荐的基础结构是:
项目名:业务模块:数据用途:唯一标识例如:
ark:user:info:1001 ark:login:token:abc123 ark:verify:login:13800000000 ark:article:view:2001设计 Key 时需要同时考虑:
唯一性 可读性 长度 大小写 数据类型 过期时间 查询方式 删除方式 数据规模Key 不能过长,也不能为了节省几个字符而失去业务含义。
Redis 最擅长通过完整 Key 精准访问数据,而不是像 MySQL 一样频繁进行复杂条件查询。
最终可以用一句话总结:
Redis Key 是数据在 Redis 中的唯一业务地址。好的 Key 设计应该让程序能够直接计算、准确查询、方便删除,并让开发者看到 Key 就能判断它属于哪个项目、哪个业务以及哪一条数据。
下一篇预告
下一篇继续学习 Redis 最常用的数据类型:
《Redis String 类型和过期时间 TTL:验证码为什么会自动失效?》
下一篇会重点讲清楚:
Redis String 到底能保存什么 SET 和 GET 的常见用法 SETEX、EX、PX 分别是什么 EXPIRE 如何设置过期时间 TTL 的返回值为什么有 -1 和 -2 验证码到期后是谁删除的 Redis 如何判断一个 Key 已经过期 INCR 为什么可以安全计数 Token、验证码和缓存应该如何设置有效期下一篇开始,我们会使用 String 完成 Redis 中最常见的几类业务操作。
