第四篇:Redis 的 Key-Value 模型到底是什么?Key 应该怎么设计?

📅 2026/8/3 6:21:09 👁️ 阅读次数 📝 编程学习
第四篇: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: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 中最常见的几类业务操作。