Key 与 Value 设计规范

最近更新时间:2026-04-24 10:57:51

我的收藏
Key 的命名方式影响数据的可维护性,Value 的大小和结构影响查询效率与内存利用率。本文档从 Key 设计原则、大 Key 规避策略和数据类型选择三个维度,定义具体的设计准则,用于在项目初期建立合理的数据模型,减少慢查询和内存膨胀问题。

一、Key 设计原则

Key 的命名直接影响数据的可维护性、运维效率和内存利用率。以下六条原则从简洁性、字符集、语义结构、类型标识、长度控制和特殊字符六个维度,定义 Key 命名的标准化规范。

1. 简洁性

在保证语义清晰的前提下,尽量缩短 Key 长度。当 Key 总量达到百万级甚至千万级时,Key 本身占用的内存空间不可忽略。每个 Key 缩短10个字节,百万 Key 即可节省约10MB内存。
分类
示例
说明
正确
cx:orderdb:order:202401150001:hash
简洁明确,总长度40字节。
错误
cx:orderdb:orderdb_order_detail_info:202401150001
表名冗余重复库名"orderdb",增加18字节无效长度。

2. 命名字符

以英文字母开头,只能包含大小写字母、数字、竖线(|)、下划线(_)、英文点号(.)和英文半角冒号(:)。统一字符集有助于避免编码歧义,同时确保 Key 在日志、监控和运维工具中能被正确解析。
分类
示例
说明
正确
cx:userdb:session:U100238:string
仅使用字母、数字和冒号,符合字符集规范。
错误
user 100238
包含空格,SCAN 命令无法正确匹配。
错误
cx:orderdb:order:{202401150001}
使用花括号,与 Hashtag 语法冲突。

3. 语义分割

使用英文半角冒号(:)分隔不同业务含义层级(如 业务:模块:标识),同一层级内的单词之间使用英文半角点号(.)连接。清晰的分层结构可让运维人员在排查问题时快速定位 Key 所属的业务模块。
分类
示例
说明
正确
cx:userdb:profile:U100238:hash
用冒号分层:业务 → 库 → 表 → ID → 类型。
正确
cx:userdb:basic.info:U100238:string
同层词汇"basic"和"info"用点号连接。
错误
cx_userdb_profile_U100238
使用下划线分隔层级,与冒号分层规范不一致。

4. 类型后缀

在 Key 末尾附加 Value 类型标识。当团队成员看到 Key 名称时,无需执行 TYPE 命令即可判断对应的数据结构,降低沟通成本。
分类
示例
说明
正确
cx:orderdb:order:202401150001:hash
末尾 :hash 表明 Value 为 Hash 类型。
正确
cx:userdb:session:U100238:string
末尾 :string 表明 Value 为 String 类型。
错误
cx:orderdb:order:202401150001
缺少类型后缀,无法判断 Value 是 String 还是 Hash。

5. 控制 Key 大小

Key 名称建议控制在128字节以内。过长的 Key 占用额外内存,同时增加比较和哈希计算的开销,在高并发场景下对 CPU 产生可测量的影响。
分类
示例
说明
正确
cx:userdb:session:U100238:string
总长度36字节,远低于128字节上限。
错误
cx:orderdb:orderdb_order_detail_info_full_record:202401150001:hash
总长度65字节,包含冗余描述,应精简。

6. 禁止特殊字符

禁止包含 \\*?{}[]()、空格、单双引号和转义字符。这些字符与 glob 通配符和 Shell 转义规则冲突,会导致 SCANKEYS 等模式匹配命令无法正确检索,同时在日志分析和脚本处理中引发解析异常。
分类
示例
说明
正确
cx:orderdb:order:202401150001:hash
不含任何特殊字符。
错误
cx:orderdb:order:{202401150001}
花括号与 Hashtag 语法冲突。
错误
cx:orderdb:order:2024*:hash
星号为 glob 通配符,SCAN 无法精确匹配。
错误
user "test"
包含空格和双引号,Shell 和日志解析会出错。

二、Value 设计原则

Value 的体积和集合元素数量需严格控制,避免产生大 Key。当某个 Key 的 Value 体积过大或集合元素过多时,读写该 Key 会占用较长的 CPU 时间片,阻塞其他请求的处理,表现为慢查询增多;同时,大 Value 在网络传输时占用较多网卡带宽,少量大 Key 的并发读取即可造成网络拥塞,影响同节点上其他业务的响应时间。本节定义大 Key 的判定阈值、日常开发建议阈值及标准化拆分策略。

1. 大 Key 判定阈值

当某个 Key 达到下表条件时,应排查并进行拆分或重构:
数据类型
大 Key 判定条件
风险说明
String
Value 值超过1MB。
单次读写占用过长 CPU 时间片,阻塞同线程其他请求。
Set
成员数量超过10000个。
全量遍历耗时线性增长,SMEMBERS 等命令可能触发慢查询。
List
成员数量超过10000个。
同上,LRANGE 0 -1 全量读取将阻塞服务端线程。
Hash
成员数量超过1000个,且所有字段的总 Value 大小超过 1000MB。
HGETALL 返回的数据量过大,网络传输耗时显著增加。
Sorted Set
成员数量超过10000个。
ZRANGEBYSCORE 大范围查询耗时线性增长。

2. 日常开发建议阈值

为了在问题发生之前将风险控制在安全范围内,建议在日常开发中遵循更严格的阈值标准:
数据类型
建议阈值
规范建议
String
≤ 10KB
超过10KB时按业务维度拆分为多个子 Key。
Hash / List / Set / ZSet
元素个数 ≤ 5000
超过5000时按字段范围、时间区间或业务分类拆分。

3. 大 Key 拆分策略

当 Value 超过上述阈值时,应根据数据类型选择对应的拆分方式。以下按数据类型分别说明拆分策略和代码实现规范。

String 类型拆分

当单个 String 的 Value 超过10KB时,按业务维度将数据拆分为多个子 Key。
反面示例(单 Key 超限):将整个订单的 JSON 序列化后存入一个 String,体积达到50KB。
# 潜在风险:50KB 的 Value 在高并发读取时占用大量网卡带宽
SET cx:orderdb:order:202401150001:string '{"status":"paid","amount":12580,"items":[...大量商品明细...],"logistics":{...物流信息...}}'
正确示例(按业务维度拆分):将订单拆分为基础信息、商品明细、物流信息三个子 Key,每个 Key 的 Value 控制在10KB以内。
# 拆分后每个子 Key 的 Value 体积可控,支持按需读取
SET cx:orderdb:order:202401150001:base:string '{"status":"paid","amount":12580}'
SET cx:orderdb:order:202401150001:items:string '[...商品明细...]'
SET cx:orderdb:order:202401150001:logistics:string '{...物流信息...}'

Hash 类型拆分

当单个 Hash 的字段数超过5000个时,按字段范围或业务含义拆分为多个 Hash Key。
反面示例(单 Hash 超限):将10000个用户属性全部存入一个 Hash。
# 潜在风险:HGETALL 返回 10000 个字段,网络传输和客户端解析耗时显著
HSET cx:userdb:user.attrs:hash field_0001 value_0001 field_0002 value_0002 ... field_10000 value_10000
正确示例(按字段范围拆分):每个子 Hash 控制在5000个字段以内。
# 按字段范围拆分,HGETALL 单个子 Hash 的返回量可控
HSET cx:userdb:user.attrs:part1:hash field_0001 value_0001 ... field_5000 value_5000
HSET cx:userdb:user.attrs:part2:hash field_5001 value_5001 ... field_10000 value_10000

List / Set / Sorted Set 类型拆分

当集合元素超过5000个时,按时间范围、ID 区间或业务分类拆分为多个子 Key。
反面示例(单集合超限):将全年的用户操作日志存入一个 List,元素数达到数万。
# 潜在风险:LRANGE 0 -1 全量读取数万条记录,阻塞服务端线程数秒
LPUSH cx:userdb:user.oplog:U100238:list "2024-01-01 login" "2024-01-01 query" ... "2024-12-31 logout"
正确示例(按月份拆分):每个 List 仅存储当月日志,单个 List 元素数可控。
# 按月份拆分后,单个 List 的元素数控制在合理范围内
LPUSH cx:userdb:user.oplog:U100238:202401:list "2024-01-01 login" "2024-01-01 query" ...
LPUSH cx:userdb:user.oplog:U100238:202402:list "2024-02-01 login" ...

三、数据类型选择指南

KeeWiDB 兼容 Redis 协议,提供五种基础数据类型。不同类型在内部编码和操作复杂度上存在差异,选择合适的类型可以在相同硬件条件下提升读写效率、降低内存占用。以下按数据类型分别说明适用场景、典型用法及代码示例。

1. String(字符串)

存储单一值,如状态标记、序列化对象、二进制数据等。适用于分布式锁、计数器、会话缓存、配置信息等场景。
# 会话缓存:存储用户登录态,设置 30 分钟过期
SET cx:userdb:session:U100238:string '{"token":"abc123","role":"admin"}' EX 1800

# 计数器:文章阅读量自增
INCR cx:articledb:article:A20240001:views:string

2. Hash(哈希表)

存储具有多个字段的结构化数据,支持对单个字段的独立读写,无需整体序列化。适用于用户画像(姓名、年龄、地域)、商品属性、设备信息等场景。
# 用户画像:聚合存储多个属性
HMSET cx:userdb:profile:U100238:hash name "张三" age 28 region "广州"

# 按需读取单个字段
HGET cx:userdb:profile:U100238:hash name

# 批量读取部分字段
HMGET cx:userdb:profile:U100238:hash name age

3. List(列表)

维护有序的元素序列,支持从两端执行 O(1) 复杂度的插入和弹出操作。适用于消息时间线、最近浏览记录、任务队列等场景。
# 最近浏览记录:从左侧插入最新记录,保留最近 50 条
LPUSH cx:userdb:recent.views:U100238:list "商品A_2024-01-15"
LTRIM cx:userdb:recent.views:U100238:list 0 49

4. Set(集合)

存储无序且不重复的元素集合,支持交集、并集、差集运算。适用于标签系统、共同好友计算、去重集合等场景。
# 标签系统:为用户添加兴趣标签
SADD cx:userdb:tags:U100238:set "保险" "理财" "汽车"

# 共同好友:计算两个用户的共同标签
SINTER cx:userdb:tags:U100238:set cx:userdb:tags:U200456:set

5. Sorted Set(有序集合)

每个元素关联一个分值(score),按分值排序存储,支持范围查询和排名查询。适用于实时排行榜、延迟任务调度、带权重的投票系统等场景。
# 实时排行榜:以销售额作为分值
ZADD cx:salesdb:rank:202401:zset 125800 "华南区" 98500 "华东区" 76200 "华北区"

# 查询 Top 3
ZREVRANGE cx:salesdb:rank:202401:zset 0 2 WITHSCORES
说明:
KeeWiDB 极速版在底层使用了 ziplist(压缩列表)等紧凑编码方式来优化小型数据结构的内存占用。ziplist 将元素存储在一段连续的内存块中,省去了指针开销,但由于缺少索引,查找、插入和删除操作均需线性扫描。因此,当元素数量较少(通常不超过128个)且单个元素体积较小时,ziplist 可以减少内存占用;当元素规模增长后,应让系统自动转换为标准数据结构以维持查询效率。

6. 多属性存储:用 Hash 替代多个 String Key

当一个实体拥有多个属性时(如用户的姓名、年龄、爱好),建议使用一个 Hash Key 来聚合存储,而非为每个属性创建独立的 String Key。这样做有三个好处:一是减少 Key 的总数量,降低数据库的键空间占用;二是可以通过 HGETHMGET 按需读取部分字段,避免不必要的数据传输;三是在逻辑上保持数据的内聚性,便于后续维护。
反面示例(多 String Key 分散存储):为订单的每个属性创建单独的 String Key,5个属性即产生5个 Key,百万订单将占用500万个键空间。
# 潜在风险:每个属性一个 Key,键空间膨胀 5 倍,内存开销显著增加
SET cx:orderdb:order:202401150001:status paid
SET cx:orderdb:order:202401150001:amount 12580
SET cx:orderdb:order:202401150001:product car_insurance
SET cx:orderdb:order:202401150001:customer C100238
SET cx:orderdb:order:202401150001:create_time 2024-01-15T10:30:00
正确示例(Hash 聚合存储):使用一个 Hash Key 聚合全部属性,百万订单仅占100万个键空间,且支持按需读取单个字段。
# 一个 Hash Key 聚合全部属性,减少键空间占用
HMSET cx:orderdb:order:202401150001:hash status paid amount 12580 product car_insurance customer C100238 create_time 2024-01-15T10:30:00

# 按需读取单个字段,无需加载整个订单
HGET cx:orderdb:order:202401150001:hash status

# 批量读取部分字段
HMGET cx:orderdb:order:202401150001:hash status amount customer