2. Redis 核心数据结构回顾:String、List、Hash、Set、Sorted Set 在权限与削峰中的应用基础
在深入企业级权限模型与热点削峰方案之前,必须重新审视 Redis 的五种核心数据结构。本章不讨论基础 API 语法,而是聚焦于每种结构在“权限校验”与“流量削峰”两大场景下的设计模式、内存特征与选型依据。
2.1 数据结构全景对比
| 数据结构 | 底层编码 | 时间复杂度(关键操作) | 权限场景典型用途 | 削峰场景典型用途 |
|---|---|---|---|---|
| String | int / embstr / raw | O(1) 读写 | Token 存储、单用户权限缓存 | 计数器(令牌桶)、分布式锁 |
| List | quicklist | O(1) 头尾操作 | 操作审计日志队列 | 异步削峰缓冲队列 |
| Hash | ziplist / hashtable | O(1) 单字段操作 | 用户权限属性集(角色+资源+有效期) | 热点参数聚合、限流维度存储 |
| Set | intset / hashtable | O(1) 增删查 | 角色集合、用户权限交集/并集运算 | 去重拦截、黑/白名单 |
| Sorted Set | ziplist / skiplist | O(log N) 范围查询 | 权限优先级排序、资源分级 | 滑动窗口限流、热点 Key 排序 |
2.2 String:原子计数器与 Token 基石
String 是 Redis 最基础的结构,但在企业级架构中承担着两个关键角色:分布式计数器和Token 存储。
2.2.1 权限场景:Token 与 Session
用户登录后生成的 Token 直接以 String 类型存储,Key 为 Token 值,Value 为序列化的用户身份 JSON。配合 EXPIRE 命令实现自动过期,避免 Session 堆积。
// 用户登录成功后
SET token:abc123 "{\"uid\":1001,\"role\":\"admin\",\"expire\":1710000000}" EX 7200
// 每次请求时校验
GET token:abc123
// 若返回 nil,则 Token 已过期,需重新登录
2.2.2 削峰场景:令牌桶计数器
利用 INCR 与 EXPIRE 的原子组合,实现秒级计数器。这是最轻量的限流手段,适合单机或简单分布式场景。
// 伪代码:每秒最多允许 100 次请求
String key = "ratelimit:api:user_1001:" + currentSecond;
Long count = redis.incr(key);
if (count == 1) {
redis.expire(key, 1); // 设置 1 秒过期
}
if (count > 100) {
return "请求过于频繁";
}
INCR 与 EXPIRE 之间若发生崩溃,会导致 Key 永不过期。企业级方案应使用 Lua 脚本或 Redis 6.2+ 的 INCR 带 EX 选项保证原子性。
2.3 List:异步削峰缓冲队列
List 的 LPUSH + BRPOP 模式是企业级削峰的标准组件。请求先入队,后端 Worker 按能力消费,天然实现“生产者-消费者”解耦。
2.3.1 权限场景:操作审计日志
每次权限变更操作(角色分配、权限撤销)都作为一条 JSON 记录 RPUSH 到审计队列。异步消费者批量写入数据库,避免同步写库对权限校验路径的延迟影响。
// 生产者
LPUSH audit:log '{"uid":1001,"action":"assign_role","target":"editor","time":1710000000}'
// 消费者(后台守护进程)
BRPOP audit:log 0
2.3.2 削峰场景:请求缓冲
当瞬时流量超过后端处理能力时,List 作为缓冲区吸收峰值。配合 LLEN 监控队列深度,触发背压或降级策略。
LTRIM 定期裁剪队列。
2.4 Hash:权限属性集与限流维度
Hash 是权限模型中最常用的结构。一个用户的所有权限属性可以存储在一个 Hash 中,字段级操作避免全量序列化开销。
2.4.1 权限场景:用户权限属性集
将用户 ID 作为 Key,字段为资源标识,值为权限掩码或角色名。查询单资源权限时只需 HGET,无需反序列化整个对象。
// 用户 1001 的权限 Hash
HSET user:1001:perms "report:view" "1"
HSET user:1001:perms "report:edit" "1"
HSET user:1001:perms "admin:panel" "0"
// 校验用户是否有查看报表权限
HGET user:1001:perms "report:view" // 返回 "1"
2.4.2 削峰场景:多维度限流计数器
Hash 可以存储同一用户在不同接口上的限流计数,避免 Key 爆炸。例如:Key 为 ratelimit:user:1001,字段为接口路径,值为当前秒的请求计数。
// 对用户 1001 的 /api/order 接口限流
HINCRBY ratelimit:user:1001 "/api/order" 1
// 同时设置整个 Hash 的过期时间
EXPIRE ratelimit:user:1001 1
2.5 Set:角色集合与去重拦截
Set 的无序、唯一特性在权限系统中用于角色归属判断和权限交集运算;在削峰中用于幂等去重。
2.5.1 权限场景:角色与权限集合运算
使用 Set 存储用户拥有的角色集合,以及每个角色对应的权限集合。通过 SINTER 求交集判断用户是否同时拥有多个角色;通过 SUNION 合并角色权限。
// 用户 1001 的角色集合
SADD user:1001:roles "admin" "editor"
// 角色 admin 的权限集合
SADD role:admin:perms "report:view" "report:edit" "user:manage"
// 判断用户是否有 "user:manage" 权限
SISMEMBER role:admin:perms "user:manage" // 返回 1
2.5.2 削峰场景:请求幂等去重
对于重复提交的请求(如支付回调),使用 Set 的 SADD 原子性判断是否已处理。返回 1 表示首次处理,返回 0 表示重复请求。
// 使用请求唯一 ID 去重
SADD processed:orders "order_20240301_1001"
// 若返回 0,说明已处理过,直接返回成功
2.6 Sorted Set:滑动窗口与优先级排序
Sorted Set 的 score 排序特性使其成为滑动窗口限流和权限优先级排序的唯一选择。
2.6.1 权限场景:资源分级与优先级队列
将权限资源按敏感度或优先级排序,score 代表等级。高优先级权限(如超级管理员)score 值高,低优先级(如只读)score 值低。通过 ZRANGEBYSCORE 快速获取某个等级范围内的资源列表。
// 资源分级:score 越高,权限等级越高
ZADD resource:levels 1 "report:view"
ZADD resource:levels 5 "report:edit"
ZADD resource:levels 10 "admin:panel"
// 获取等级 >= 5 的资源
ZRANGEBYSCORE resource:levels 5 10
2.6.2 削峰场景:滑动窗口限流
固定窗口计数器存在“临界突变”问题。滑动窗口使用 Sorted Set 记录每个请求的时间戳,通过 ZREMRANGEBYSCORE 移除窗口外的记录,ZCARD 统计窗口内请求数。
// 滑动窗口限流:窗口 1 秒,最大 10 次
long now = System.currentTimeMillis();
String key = "sliding:user:1001";
// 移除 1 秒前的记录
redis.zremrangeByScore(key, 0, now - 1000);
// 添加当前请求
redis.zadd(key, now, String.valueOf(now));
// 设置 Key 过期(防止内存泄漏)
redis.expire(key, 2);
// 统计窗口内请求数
long count = redis.zcard(key);
if (count > 10) {
return "限流";
}
ZREMRANGEBYSCORE + ZADD 是 O(log N) 操作,高并发下建议使用 Lua 脚本合并为一次原子执行,减少网络往返。
2.7 数据结构选型决策树
2.8 内存与性能基准参考
以下基准数据基于 Redis 7.0 单实例(Xeon 2.5GHz, 4GB 内存),使用 redis-benchmark 测试,供选型时参考:
| 数据结构 | 写入 QPS(万/秒) | 读取 QPS(万/秒) | 内存开销(100 万 Key) | 适用并发等级 |
|---|---|---|---|---|
| String(10 字节 Value) | 12.5 | 14.2 | ~85 MB | 极高 |
| List(队列模式) | 10.8 | 11.5 | ~110 MB | 高 |
| Hash(5 字段) | 9.2 | 10.6 | ~95 MB | 高 |
| Set(10 元素) | 8.5 | 9.8 | ~120 MB | 中高 |
| Sorted Set(10 元素) | 7.1 | 8.3 | ~150 MB | 中 |
2.9 本章小结
- String:原子计数器与 Token 存储,削峰场景的基石。
- List:异步缓冲队列,削峰与审计日志的标准模式。
- Hash:权限属性集的最佳载体,字段级操作避免全量序列化。
- Set:角色集合与去重拦截,集合运算能力不可替代。
- Sorted Set:滑动窗口限流与权限优先级排序,唯一支持范围排序的结构。