本文是 Redis 键值设计的 14 个核心规范与最佳实践,按重要程度分层说明:
一、通用数据类型选择
这里我们先给出常规的选择路径图。
以下是对每个步骤的分析:
- 是否需要排序?:
zset
(有序集合)用于排序的唯一值,而list
用于排序的重复值。
- 数据是否唯一?:
set
用于存储唯一的值。
- 是否需要存储对象?:
Hash
适合存储对象或具有多个字段的结构。
- 考虑操作频率?:
- String
和
Hash`都是Redis中最常用的数据类型,适用于高频读写操作。
- String
- 数据大小和内存占用大?:
Bitmap
适合存储大量数据,同时占用较少的内存。
- 消息队列?:
stream
是Redis用于实现消息队列的数据类型。
- 原子操作和数据过期?:
lua脚本
可以用于实现原子操作,而Redis的过期机制可以用于数据过期。
二、键设计规范(Key Design)
-
命名规范
- 格式:
业务模块:数据维度:唯一标识
(例:user:profile:10001
) - 强制要求:禁止包含空格、换行符、不可见字符
- 建议:长度控制在 100 字节以内(内存敏感场景)
- 格式:
-
大Key规避
- 单Key值大小限制:
- String 类型 ≤ 10KB
- Hash/List/Set/Zset 元素数 ≤ 5000
- 超标处理方案:
- 数据分片(例:user:10001:cart_page1)
- 启用压缩(客户端压缩 + LZF Redis压缩)
- 单Key值大小限制:
-
过期策略
- 必须设置过期时间(包括持久化数据,建议 30 天兜底)
- 不同过期时间策略:
-- 使用随机过期时间避免批量过期导致的毛刺 local expire_time = 86400 + math.random(0, 3600) redis.call('EXPIRE', KEYS[1], expire_time)
三、值设计规范(Value Design)
-
数据结构选择原则
- 按使用频率选择:
高频读写 → String/Hash 范围查询 → ZSET 去重计算 → Set/HLL 关系查询 → RedisGraph(需 4.0+)
- 禁止将 Redis 当关系型数据库使用(避免复杂关联查询)
- 按使用频率选择:
-
JSON序列化陷阱
- 推荐方案:
- 高频字段拆解为 Hash 字段
- 保留完整 JSON 作为 fallback 方案
- 优化案例:
HMSET user:10001 name "John" age 30 SET user:10001:full '{...}' EX 3600
- 推荐方案:
-
计数器设计
- 必须使用
INCR/DECR
代替GET+SET
- 集群环境推荐使用
INCRBY float
代替整数运算
- 必须使用
三、高级优化策略
-
内存优化技巧
- Hash 使用 ziplist 编码:
redis.conf 配置: hash-max-ziplist-entries 512 hash-max-ziplist-value 64
- 使用
SSCAN/ZSCAN
替代SMEMBERS/ZRANGE
- Hash 使用 ziplist 编码:
-
热点Key治理
- 检测方法:
redis-cli --hotkeys
- 解决方案:
- 本地缓存 + 异步刷新
- Key 分片(例:hotkey_v1 → hotkey:{shard_id}:v1)
- 检测方法:
-
事务与管道
- 管道(pipeline)批量操作控制在 100 命令/批次
- Watch 事务中避免包含耗时操作
四、集群与持久化
-
集群规范
- 单个分片内存 ≤ 10GB(AWS 内存优化型实例)
- 跨槽操作使用 Hash Tag 需满足:
- 相关Key必须使用相同{}内容
- 示例:
{user10001}.orders
,{user10001}.profile
-
持久化策略
- AOF 配置:
appendfsync everysec auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
- RDB 快照周期 ≥ 15 分钟
- AOF 配置:
五、避坑指南
-
危险命令禁用
rename-command FLUSHALL "" rename-command KEYS "internal_KEYS"
-
慢查询防御
- 设置超时阈值:
slowlog-log-slower-than 5000 # 5ms
- 定期分析:
SLOWLOG GET 50
- 设置超时阈值:
-
连接池配置
// Jedis 最佳配置示例 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(500); // 最大连接数 config.setMaxIdle(100); // 最大空闲连接 config.setMinIdle(20); // 最小空闲连接 config.setMaxWaitMillis(2000); // 最大等待时间
六、案例
以下通过 6个高频场景的对比案例 说明 Redis 键值设计的核心规范,帮助直观理解:
案例1:用户信息存储设计
❌ 错误做法
# 大JSON直接存储String类型,无过期时间
SET user_10001 '{name:"John",age:30,address:"...20个字段...",lastLogin:...}'
问题:
- Key无业务含义,易冲突
- Value超10KB违反大Key规范
- 高频读取时需全量解析JSON
✅ 正确方案
# 模块化Key命名 + Hash分字段存储 + 过期时间
HMSET user:profile:10001 name "John" age 30 address "..." lastLogin 1717040000
EXPIRE user:profile:10001 2592000 # 30天过期
优化点:
- 键结构清晰:
业务模块:数据维度:ID
- 高频字段独立存取,减少网络传输
- 兜底过期避免数据堆积
案例2:电商购物车设计
❌ 错误做法
# 用List存储所有商品ID(可能产生大Key)
LPUSH cart:10001 "sku_123:5" "sku_456:3" ...(5000+商品)
问题:
- 超出5000元素的大Key阈值
- 分页查询困难
✅ 正确方案
# Hash分片存储 + 计数器
HMSET cart:10001:page1 sku_123 5 sku_456 3
HMSET cart:10001:page2 sku_789 2 ...
# 获取商品数量(原子操作)
HINCRBY cart:10001:page1 sku_123 1
优化点:
- 分片控制单个Key元素数量
- 利用Hash字段的原子计数特性
案例3:秒杀库存热点Key
❌ 错误做法
# 集中式库存计数器(产生热点Key)
SET stock:sku_8888 1000
DECR stock:sku_8888 # 所有请求集中访问此Key
问题:
- 单Key承受极高QPS
- 集群模式下无法分散压力
✅ 正确方案
# 库存分片设计
SET stock:sku_8888:shard1 200
SET stock:sku_8888:shard2 200
...
SET stock:sku_8888:shard5 200# 客户端随机选择分片扣减
DECR stock:sku_8888:shard{random(1-5)}
优化点:
- 通过分片分散热点
- 结合本地缓存减少Redis访问
案例4:页面访问计数器
❌ 错误做法
# 非原子操作导致计数不准
count = redis.GET('page_view:home')
redis.SET('page_view:home', count+1)
问题:
- 并发场景下数据不一致
- 频繁GET/SET产生大量请求
✅ 正确方案
# 使用INCR原子操作
INCR page_view:home# 按小时滚动存储(避免单Key过大)
INCR page_view:home:2024052715
优化点:
- 原子操作保证准确性
- 时间分片控制Key规模
案例5:用户消息通知列表
❌ 错误做法
# 用String存储JSON数组(频繁全量读写)
SET msg:10001 '[{id:1,content:"..."}, {...1000条数据}]'
问题:
- 大Value导致网络阻塞
- 修改任意消息需全量更新
✅ 正确方案
# 使用ZSET按时间排序存储
ZADD msg:10001 1717040000 '{"id":1,"content":"..."}'
ZADD msg:10001 1717040001 '{"id":2,"content":"..."}'# 分页查询最新消息
ZREVRANGE msg:10001 0 9 WITHSCORES
优化点:
- 天然支持按时间排序和分页
- 单个消息的增删不影响整体
案例6:社交关系存储
❌ 错误做法
# 用String存储用户粉丝列表(大JSON数组)
SET followers:10001 "[20001,20002,...50000个用户ID]"
问题:
- 50000个ID超过大Key限制
- 判断是否关注需全量扫描
✅ 正确方案
# 使用Set存储关系 + 分页控制
SADD following:10001 20001 20002 ... # 最多5000元素/Key
SADD following:10001:page2 20003 ... # 分片存储# 检查关注关系
SISMEMBER following:10001 20001
优化点:
- 分片规避大Key
- 使用原生集合操作提升效率
总结技巧
- Key设计三要素:业务线明确(
user
)、数据类型清晰(profile
)、标识唯一(10001
) - Value选择原则:
- 优先使用 Hash 替代 String 存储对象
- 需要
排序用 ZSET
,去重用 Set
,队列用 List
- 性能压测公式:
# 模拟高并发场景 redis-benchmark -h 127.0.0.1 -p 6379 -n 100000 -c 100 -t set,get