Redis多租户场景下,默认maxmemory策略(如allkeys-lru)会全局淘汰无视租户边界,导致跨租户缓存污染;须结合TTL分级控制、ACL权限隔离、内存预估拦截三重机制实现租户级内存约束。

Redis maxmemory 策略对多租户淘汰的隐式穿透
默认的 maxmemory-policy(如 allkeys-lru 或 volatile-lru)在共享实例中会全局扫描所有 Key 进行淘汰,完全无视租户边界。这意味着租户 A 的大缓存 Key 可能挤掉租户 B 的高频热 Key,导致后者命中率骤降、接口抖动——这不是“隔离失效”,而是“淘汰失控”。
常见错误现象包括:
- 某租户批量写入日志类缓存后,其他租户的用户会话 Key 频繁被驱逐
- 监控显示
evicted_keys持续上涨,但各租户业务方均未主动设置过期时间 - 使用
redis-cli --bigkeys发现大量 Key 集中在少数几个租户前缀下
根本原因在于:Redis 本身不感知租户,LRU/LFU 算法只看内存占用和访问频次,不看 Key 前缀或 ACL 权限。
用 MEMORY USAGE + TTL 分层控制租户内存水位
不能只靠 maxmemory-policy 被动淘汰,必须主动按租户维度做内存约束。核心思路是:把“全局内存上限”拆成“租户级软上限”,再配合 Key 生命周期管理。
实操建议:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 为每个租户 Key 显式设置
TTL(哪怕只是逻辑过期),避免无过期 Key 占满内存;例如用SET tenant_a:cache:user:123 "val" EX 3600 - 定期执行
MEMORY USAGE扫描租户前缀(如tenant_a:*),当单租户内存 > 总maxmemory的 15% 时触发告警并清理冷 Key - 禁用
allkeys-random类策略——它无法保障热 Key 存活,对多租户场景风险极高 - 若用 Redis 7.0+,可结合
MEMORY DOCTOR输出租户前缀的内存分布热点,定位异常膨胀来源
ACL 用户 + CONFIG SET 动态限频是更稳妥的组合
单纯靠 Key 前缀无法阻止租户恶意刷写。比如租户 A 故意构造百万个 tenant_a:tmp:* Key,照样能把整个实例拖垮。这时必须叠加运行时权限控制。
关键配置项:
- 为每个租户创建独立 ACL 用户:
ACL SETUSER tenant_a on >pwd123 ~tenant_a:* &~tenant_a:* +get +set +expire +ttl - 限制该用户最大连接数:
ACL SETUSER tenant_a ~* &~* +@all -@dangerous -@admin -@slow -@keyspace -@connection(注意去掉flushdb和keys) - 启用内存硬限:
CONFIG SET maxmemory 4gb后,再对 ACL 用户做命令频率限制:ACL SETUSER tenant_a ~* &~* +@read +@write --qps 1000(需 Redis 7.2+ 支持) - 禁止租户使用
SCAN全量遍历,改用带游标的分页查询,避免阻塞主线程
代理层兜底:Key 前缀校验 + 内存预估拦截
ACL 和 TTL 是防御性措施,但仍有漏网之鱼。最可靠的兜底是在 Redis 客户端接入层加一道内存预估拦截——不是等写入后再淘汰,而是写入前就拒绝超限请求。
典型实现逻辑:
- 在
RedisTemplate.set()前,调用StringRedisTemplate.execute()执行 Lua 脚本,估算tenant_x:*当前内存占比 - 若估算值 > 配置阈值(如 20%),直接抛出
RedisTenantMemoryExceededException,不走网络 IO - Lua 脚本内用
MEMORY USAGE抽样 100 个 Key 取平均值,再乘以 Key 数量粗略估算,避免全量扫描 - 该脚本必须与 Key 前缀强绑定,且只对已知租户 ID 生效,防止伪造前缀绕过
这个环节最容易被忽略:很多团队只做了 Key 前缀和 ACL,却没在写入链路最前端加内存水位校验。一旦某个租户突发流量打穿内存,淘汰风暴就会瞬间波及全部租户。

















