前面三篇,我们已经把 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:name
Redis 会查找完整名称为:
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 30
Redis 会返回类型错误。
因为:
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:1001
user_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:1001
Value 可以保存用户 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 1001
Redis 命令:
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:1001
Redis 不会把它们当成同一个 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:1001
Value 中保存中文通常没有问题:
SET ark:user:name:1001 张三
也就是:
Key
→ 建议使用规范英文和数字
Value
→ 根据业务正常保存中文内容
二十、Key 能不能像数据库字段一样模糊查询
Redis 的主要使用方式是通过完整 Key 查询。
例如:
GET ark:user:info:1001
Redis 可以快速找到对应数据。
但是有时我们可能想查询:
所有 ark:user 开头的 Key
Redis 提供了相关命令,例如:
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 100
SCAN 会以游标方式分批返回结果,而不是一次性扫描完所有 Key。
可以简单理解为:
KEYS
→ 一次性找完
SCAN
→ 分批逐步查找
SCAN 对线上环境相对友好,但也不代表可以毫无成本地频繁使用。
应用业务代码中,最好仍然通过明确的 Key 直接访问。
如果某个业务经常需要:
查询所有用户缓存
查询某类全部 Key
按照多个条件筛选
可能说明数据结构设计不合理,或者这部分需求更适合数据库、Set、ZSet 等结构。
二十三、如何检查一个 Key 是否存在
可以使用:
EXISTS ark:user:info:1001
如果 Key 存在,返回:
1
如果不存在,返回:
0
Spring 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 300
Token:
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-20
Key 会变得非常长,而且参数顺序、空值处理、字符编码都可能产生问题。
复杂查询缓存通常需要:
规范化请求参数
固定参数顺序
对参数生成摘要
控制缓存维度
防止产生海量不同 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:1001
2. 使用冒号分层
项目:模块:用途:唯一标识
3. 使用稳定唯一标识
优先:
用户 ID
订单 ID
文章 ID
设备 ID
4. 不使用无意义缩写
推荐:
ark:user:info:1001
谨慎使用:
a:u:i:1001
5. 不包含不必要的敏感信息
尽量避免直接放入:
身份证号
完整手机号
密码
密钥
Token 明文日志
Token 作为 Key 的组成部分有时难以避免,但日志中要注意脱敏。
6. 明确数据类型
例如提前约定:
ark:user:info:{id}
→ String JSON
7. 明确过期时间
例如:
用户缓存: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_time
Redis 中不一定要为每个字段单独设计 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:1001
2. 保存验证码
SET ark:verify:login:13800000000 9527 EX 300
读取:
GET ark:verify:login:13800000000
查看剩余时间:
TTL ark:verify:login:13800000000
3. 记录访问量
SET ark:article:view:2001 0
增加:
INCR ark:article:view:2001
读取:
GET ark:article:view:2001
4. 查看当前 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 中最常见的几类业务操作。