Redis 开发&运维笔记(理论知识与常用操作综合向收录)
在开发、测试又或者生产系统中,时常需要对 Redis 环境运维、操作数据或者验证数据等等,基于此产生本篇综合向笔记。本篇旨在打通“Redis 命令 – Java 项目开发 – 运维巡检”的全链路,总结收录高频率收录高频率操作数据对象命令、运维命令、部署参数和日常排查问题关键注意点等方面,为在实际项目使用与运维操作中提供参考。
一、核心数据对象基础梳理
在项目中和运维时,对数据的增删改查是最基础的操作。理解不同数据结构的特性是高效正确使用的前提。
1.1 基础五大类型
[String] 字符串: 最常用的类型。
- 特点: String 是 Redis 最基本的类型,它是二进制安全的,可以存储从文本到图片、序列化对象(JSON/Protobuf)在内的任何数据(最大 512MB)。
SET key value [EX seconds] [NX|XX]:设置值并带过期时间,NX 常用于分布式锁,XX 常用于状态更新。
| 无参数 | 写入成功 | 覆盖成功 | 强制更新/覆盖写入 |
| NX (Not eXists) | 写入成功 (返回 OK) | 不执行 (返回 nil) | 加锁/占位:只有第一个人能成功 |
| XX (eXists) | 不执行 (返回 nil) | 覆盖成功 (返回 OK) | 更新:只对已存在的数据进行覆盖 |
在 Redis 2.6.12 版本之前,如果你想实现“不存在则设置并加过期时间”,需要执行两条命令:
1)SETNX lock_key value
2)EXPIRE lock_key 30
隐患:如果第 1 条执行成功,但程序在执行第 2 条之前崩溃,该锁将永远不会过期,导致程序产生死锁。
GETSET key value:设置新值并返回旧值(原子性操作,常用于重置计数器)。
INCRBY key increment:原子自增,常用于分布式计数或限流。
MSET / MGET:一次请求处理多个 Key,大幅降低网络往返时延(RTT)。
-
分布式锁 (Distributed Locking):
-
核心逻辑:利用 NX 的排他性与 EX 的原子失效。
-
实战解析:执行 SET lock_key request_id NX EX 30。在多节点并发竞争时,Redis 保证“判断是否存在”与“设置过期时间”是原子操作。仅首个请求会因 NX 判定成功返回 OK(即成功抢锁),后续请求均返回 nil。EX 引入了租约机制,防止因持锁进程意外宕机而产生永久死锁。
-
有态续期与更新 (Stateful Update & Lease):
-
核心逻辑:利用 XX 的存在性检查,防止“脏数据插入”。
-
实战解析:执行 SET user:session:101 data XX EX 1800。该操作仅在用户 Session 尚未过期(Key 存在)时执行覆盖更新并重置 30 分钟有效期。若用户已下线(Key 已失效),操作将直接跳过。这确保了业务逻辑的幂等性,有效规避了在数据已清理后又意外写入“孤儿数据”导致的内存泄漏。
-
防重入/防重放 (Idempotency Control):
-
核心逻辑:利用 NX 作为唯一性标识。
-
实战解析:在处理支付回调或幂等接口时,执行 SET serial_no:12345 1 NX EX 3600。若返回 OK 则说明该单据是首次处理,若返回 nil 则直接拦截重复请求,起到瞬时高并发下的流量防重作用。
-
动态配置热更新 (Hot Config):
-
核心逻辑:利用 XX 确保配置的“修改”语义而非“新增”。
-
实战解析:对于必须预先存在的全局开关,使用 SET global:switch on XX 进行操作。若配置项因误删而不存在,此操作将失败,从而触发告警,防止因误写导致配置逻辑在系统内产生歧义。
生产环境典型应用
- 分布式锁 (Distributed Locking):
利用 NX 的排他性与 EX 的原子失效。执行 SET lock_key request_id NX EX 30,在多节点并发竞争时,Redis 保证“判断是否存在”与“设置过期时间”是原子操作。仅首个请求会因 NX 判定成功返回 OK(即成功抢锁),后续请求均返回 nil。EX 的引入防止因持锁进程意外宕机而产生永久死锁。 - 有态续期与更新 (Stateful Update & Lease):
利用 XX 的存在性检查,防止“脏数据插入”。执行 SET user:session:101 data XX EX 1800。该操作仅在用户 Session 尚未过期(Key 存在)时执行覆盖更新并重置 30 分钟有效期。若用户已下线(Key 已失效),操作将直接跳过。这确保了业务逻辑的幂等性,有效规避了在数据已清理后又意外写入“孤儿数据”导致的内存泄漏。 - 高性能缓存 (Global Cache):
存储热点 JSON 对象或 Session 数据。结合 EX 过期时间实现缓存自动剔除。 - 防重入/防重放 (Idempotency Control):
在处理支付回调或幂等接口时,执行 SET serial_no:12345 1 NX EX 3600。若返回 OK 则说明该单据是首次处理,若返回 nil 则直接拦截重复请求,起到瞬时高并发下的流量防重作用。 - 频率限制 (Rate Limiting):
如验证码发送限制(1分钟1次)。执行 SET user:limit 1 NX EX 60,若返回 nil 则说明操作频繁,予以拦截。 - 原子计数器 (Atomic Counter):
如视频播放量、文章点赞数。在高并发下利用 INCR 保证计数的绝对准确,避免数据库锁竞争。
Jedis 代码操作示例:
// 分布式锁原子操作:设置成功返回 "OK",失败返回 null
jedis.set("lock_key", "request_id", new SetParams().nx().ex(30));
// 获取并设置:常用于重置计数器并获取重置前的值
String oldVal = jedis.getSet("counter", "0");
// 原子累加:常用于分布式计数或限流
jedis.incrBy("user_score:1001", 10);
// 批量读取:提升性能
List<String> configs = jedis.mget("conf:timeout", "conf:max_retry", "conf:switch");
RedisTemplate 代码操作示例:
// 分布式锁原子操作 (SET NX EX)
Boolean isLocked = redisTemplate.opsForValue()
.setIfAbsent("lock_key", "request_id", Duration.ofSeconds(30));
// 获取并设置 (GETSET)
Object oldVal = redisTemplate.opsForValue().getAndSet("counter", "0");
// 原子累加 (INCRBY)
Long currentScore = redisTemplate.opsForValue().increment("user_score:1001", 10);
// 批量读取 (MGET)
List<Object> configs = redisTemplate.opsForValue()
.multiGet(Arrays.asList("conf:timeout", "conf:max_retry"));
[Hash] 哈希: 适合存储对象,节省 Key 的空间。
- 特点: 适合存储对象。相比 String,它将对象的多个字段聚合在一个 Key 下,减少了 Key 本身的元数据开销(约节省 20% 内存)。
- 核心注意事项: 字段数过多(如超过 1000 个)时,HGETALL 会导致单线程阻塞。
HMSET key field1 value1 [field2 value2 …]:批量设置哈希字段值,Redis 4.0 后推荐用 HSET 替代(支持批量)。
HMGET key field1 [field2 …]:批量获取哈希指定字段值。
HGETALL key:获取哈希所有字段和值。
HINCRBY key field increment:对哈希指定字段进行原子自增/自减。
HDEL key field1 [field2 …]:删除哈希指定字段。
HEXISTS key field:判断哈希中指定字段是否存在。
| HGETALL | O(N)O(N)O(N) | N 为字段总数,字段过多会阻塞 Redis 主线程 |
| HINCRBY | O(1)O(1)O(1) | 原子操作,适合做对象属性的计数(如用户积分) |
| HEXISTS | O(1)O(1)O(1) | 比 HGET 更轻量的存在性判断(无需返回值) |
生产环境典型应用
- 对象存储 (Object Storage):
替代多个独立 String Key 存储对象属性。例如存储用户信息:user:1001 作为 Hash Key,name/age/phone 作为 Field,大幅减少 Key 数量和内存碎片。 - 计数器聚合 (Counter Aggregation):
对同一对象的多个维度计数。例如统计商品 goods:8888 的 click/collect/order 次数,通过 HINCRBY 原子更新。
反模式:将超大规模对象(如百万字段)存入单个 Hash,HGETALL 会直接引发 Redis 阻塞甚至 OOM,应按业务维度拆分 Hash(如按用户 ID 分段)。
Jedis 代码操作示例:
// 批量设置用户信息
Map<String, String> userInfo = new HashMap<>();
userInfo.put("name", "张三");
userInfo.put("age", "25");
userInfo.put("phone", "13800138000");
jedis.hmset("user:1001", userInfo);
// 获取单个字段
String userName = jedis.hget("user:1001", "name");
// 原子增加用户积分
jedis.hincrBy("user:1001", "score", 50);
// 判断字段是否存在
boolean hasPhone = jedis.hexists("user:1001", "phone");
RedisTemplate 代码操作示例:
// 批量设置 (HMSET)
Map<String, String> userInfo = new HashMap<>();
userInfo.put("name", "张三");
userInfo.put("age", "25");
redisTemplate.opsForHash().putAll("user:1001", userInfo);
// 获取单个字段 (HGET)
String name = (String) redisTemplate.opsForHash().get("user:1001", "name");
// 原子增加 (HINCRBY)
redisTemplate.opsForHash().increment("user:1001", "score", 50);
// 判断是否存在 (HEXISTS)
Boolean hasPhone = redisTemplate.opsForHash().hasKey("user:1001", "phone");
[List] 列表: 常用于异步队列或消息时间轴。
- 特点: 双向链表,支持高效的头尾操作(O(1)O(1)O(1))。
- 阻塞特性: BRPOP 是实现简单消息队列的神器,它让消费者在没有数据时休眠,有数据时被 Redis 唤醒。
LPUSH key value1 [value2 …]:从列表左侧(头部)插入一个/多个值。
RPUSH key value1 [value2 …]:从列表右侧(尾部)插入一个/多个值。
LPOP key:从列表左侧弹出一个值(非阻塞)。
RPOP key:从列表右侧弹出一个值(非阻塞)。
BRPOP key [key …] timeout:阻塞式从列表右侧弹出值,超时返回 nil。
LTRIM key start stop:修剪列表,只保留指定区间内的元素。
LLEN key:获取列表长度。
| LPUSH/RPOP | 生产者-消费者队列 | 左进右出,典型 FIFO 模型 |
| BRPOP | 异步任务消费 | 避免空轮询,降低 CPU 消耗 |
| LTRIM | 时间轴/日志保留 | 控制列表长度,防止内存膨胀 |
生产环境典型应用
- 异步任务队列 (Async Queue):
生产者通过 LPUSH 将任务写入队列 task:queue,消费者通过 BRPOP 阻塞获取任务,避免无效轮询。例如订单异步通知、日志异步处理。 - 消息时间轴 (Timeline):
如用户动态、聊天记录,通过 LPUSH 写入最新消息,LTRIM 只保留最近 100 条,既满足业务需求又控制内存占用。 - 限流兜底 (Fallback):
高并发场景下,将处理不过来的请求写入 List 队列,后端消费兜底,防止服务雪崩。
注意:List 是有序可重复的结构,若需去重请使用 Set;BRPOP 支持多 Key 监听,可实现队列优先级(先监听高优先级 Key)。
Jedis 代码操作示例:
// 生产者:写入任务到队列
jedis.lpush("task:order:queue", "order_10001", "order_10002");
// 消费者:阻塞获取任务(超时时间 30 秒)
List<String> task = jedis.brpop(30, "task:order:queue");
if (task != null && !task.isEmpty()) {
String taskId = task.get(1); // 索引 0 是 Key,索引 1 是值
System.out.println("处理任务:" + taskId);
}
// 修剪列表,只保留最近 100 条记录
jedis.ltrim("user:1001:timeline", 0, 99);
// 获取列表长度
long len = jedis.llen("task:order:queue");
RedisTemplate 代码操作示例:
// 生产者:入队 (LPUSH)
redisTemplate.opsForList().leftPushAll("task:order:queue", "order_1001", "order_1002");
// 消费者:阻塞获取 (BRPOP) – 注意:RedisTemplate 阻塞操作通常在监听容器中或用专用连接
Object task = redisTemplate.opsForList().rightPop("task:order:queue", 30, TimeUnit.SECONDS);
// 修剪列表 (LTRIM)
redisTemplate.opsForList().trim("user:1001:timeline", 0, 99);
// 获取长度 (LLEN)
Long size = redisTemplate.opsForList().size("task:order:queue");
[Set] 集合: 自动去重,支持并交差集运算。
- 特点: 无序唯一。
- 集合运算: SINTER(交集)、SUNION(并集)在社交场景(如共同好友)中极其强大,计算逻辑直接在内存中完成,远快于 SQL。
SADD key member1 [member2 …]:向集合添加一个/多个成员(自动去重)。
SREM key member1 [member2 …]:从集合删除一个/多个成员。
SISMEMBER key member:判断成员是否在集合中。
SMEMBERS key:获取集合所有成员。
SPOP key [count]:随机弹出一个/多个成员。
SUNION key1 [key2 …]:求多个集合的并集。
SINTER key1 [key2 …]:求多个集合的交集。
SDIFF key1 [key2 …]:求多个集合的差集。
| SISMEMBER | O(1)O(1)O(1) | 快速判重(如用户是否已点赞、是否已参与活动) |
| SPOP | O(1)O(1)O(1) | 随机抽奖、随机推荐 |
| SINTER | O(N∗M)O(N*M)O(N∗M) | 共同好友、相似兴趣匹配 |
生产环境典型应用
- 去重场景 (Deduplication):
如用户点赞、签到、参与活动,通过 SADD 自动去重,SISMEMBER 快速判断是否已操作(比数据库查询更高效)。 - 随机抽取 (Random Selection):
抽奖活动中,将所有参与者存入 Set,通过 SPOP 随机抽取中奖者,保证公平性且自动去重。 - 关系计算 (Relation Calculation):
社交场景中,通过 SINTER 计算两个用户的共同好友,SUNION 计算所有好友,SDIFF 计算单向关注但未互关的用户。
注意:SMEMBERS 会返回集合所有成员,集合过大时会阻塞主线程,建议使用 SSCAN 迭代获取;Set 是无序结构,若需有序请使用 ZSet。
Jedis 代码操作示例:
// 添加用户到抽奖集合(自动去重)
jedis.sadd("lottery:202402", "user_1001", "user_1002", "user_1003");
// 判断用户是否已参与抽奖
boolean isJoined = jedis.sismember("lottery:202402", "user_1001");
// 随机抽取 2 名中奖者
Set<String> winners = jedis.spop("lottery:202402", 2);
// 计算两个用户的共同好友
jedis.sadd("user:1001:friends", "user_2001", "user_2002", "user_2003");
jedis.sadd("user:1002:friends", "user_2002", "user_2003", "user_2004");
Set<String> commonFriends = jedis.sinter("user:1001:friends", "user_2002:friends");
RedisTemplate 代码操作示例:
// 添加成员并去重 (SADD)
redisTemplate.opsForSet().add("lottery:202402", "user_1001", "user_1002", "user_1003");
// 判断是否存在 (SISMEMBER)
Boolean isJoined = redisTemplate.opsForSet().isMember("lottery:202402", "user_1001");
// 随机抽取 (SPOP)
List<Object> winners = redisTemplate.opsForSet().pop("lottery:202402", 2);
// 求交集 (SINTER)
Set<Object> commonFriends = redisTemplate.opsForSet().intersect("user:1001:friends", "user:1002:friends");
[ZSet] 有序集合: 带有权重(Score)的集合。
- 核心特性: 每个元素关联一个 Double 类型的 Score,集合按 Score 排序。
- 底层: 由 SkipList (跳表) 实现,查找与插入效率均为 O(logN)O(\\log N)O(logN)。
ZADD key [NX|XX] [CH] [INCR] score1 member1 [score2 member2 …]:添加成员到有序集合,Score 为排序权重。
ZRANGE key start stop [WITHSCORES]:按 Score 升序获取指定区间成员(可带 Score)。
ZREVRANGE key start stop [WITHSCORES]:按 Score 降序获取指定区间成员。
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]:按 Score 范围获取成员。
ZREVRANK key member:获取成员的降序排名(从 0 开始)。
ZINCRBY key increment member:对成员的 Score 进行原子增减。
ZREM key member1 [member2 …]:删除有序集合中的指定成员。
| ZREVRANK | 降序排名,时间复杂度 O(1)O(1)O(1) | 排行榜实时排名展示 |
| ZRANGEBYSCORE | 范围筛选,支持 LIMIT | 按分数段筛选、延时任务 |
| ZINCRBY | 原子更新 Score | 动态更新排行榜分数 |
生产环境典型应用
- 排行榜 (Ranking List):
如游戏积分榜、商品销量榜,通过 ZADD/ZINCRBY 更新分数,ZREVRANGE/ZREVRANK 实时获取排名。例如 rank:game:score 作为 Key,用户 ID 为 Member,积分为 Score。 - 延时任务 (Delayed Task):
将任务执行时间戳作为 Score,任务 ID 作为 Member,通过 ZRANGEBYSCORE 轮询获取当前时间已到的任务,实现轻量级延时队列(替代 RabbitMQ 延时队列)。 - 范围筛选 (Range Filter):
如按用户等级筛选、按订单金额区间筛选,通过 ZRANGEBYSCORE 结合 LIMIT 实现分页查询。
注意:ZSet 的 Score 是 64 位浮点数,可存储时间戳(毫秒级);若需实现“过期自动删除”,需结合定时任务清理已处理的延时任务;ZSet 去重特性保证同一 Member 只会有一个 Score。
Jedis 代码操作示例:
// 添加用户游戏分数到排行榜
jedis.zadd("rank:game:score", 950, "user_1001");
jedis.zadd("rank:game:score", 880, "user_1002");
// 原子增加用户分数
jedis.zincrby("rank:game:score", 50, "user_1002"); // user_1002 分数变为 930
// 获取 user_1001 的降序排名(第 0 名是第一名)
Long rank = jedis.zrevrank("rank:game:score", "user_1001");
System.out.println("user_1001 排名:" + (rank + 1)); // 转换为从 1 开始的排名
// 获取排行榜前 10 名(带分数)
Set<Tuple> top10 = jedis.zrevrangeWithScores("rank:game:score", 0, 9);
for (Tuple tuple : top10) {
System.out.println("用户:" + tuple.getElement() + " 分数:" + tuple.getScore());
}
// 延时任务:添加 30 秒后执行的任务(Score 为未来时间戳)
long delayTime = System.currentTimeMillis() + 30 * 1000;
jedis.zadd("task:delayed", delayTime, "task_8888");
// 轮询获取已到执行时间的任务
long now = System.currentTimeMillis();
Set<String> tasks = jedis.zrangeByScore("task:delayed", 0, now, 0, 10);
RedisTemplate 代码操作示例:
// 添加成员与分数 (ZADD)
redisTemplate.opsForZSet().add("rank:game:score", "user_1001", 950);
// 原子增加分数 (ZINCRBY)
redisTemplate.opsForZSet().incrementScore("rank:game:score", "user_1002", 50);
// 获取降序排名 (ZREVRANK)
Long rank = redisTemplate.opsForZSet().reverseRank("rank:game:score", "user_1001");
// 获取前 10 名带分数 (ZREVRANGE WITHSCORES)
Set<ZSetOperations.TypedTuple<Object>> top10 = redisTemplate.opsForZSet()
.reverseRangeWithScores("rank:game:score", 0, 9);
// 延时任务:获取到期任务 (ZRANGEBYSCORE)
Set<Object> tasks = redisTemplate.opsForZSet()
.rangeByScore("task:delayed", 0, System.currentTimeMillis());
1.2 进阶数据类型简述
- Bitmaps: 位图。适用于签到、布尔状态统计,空间利用率极高。
- HyperLogLog: 基数估算(如 UV)。误差约 0.81%,占用内存固定(约 12KB)。
- GEO: 地理位置。支持经纬度存储、距离计算及半径搜索。
1.3 操作习惯与注意事项
- 过期时间必设: 除非是静态配置数据,否则原则上所有 Key 都应带上 TTL,防止内存溢出。
- Hash 操作避坑: 禁止对超大 Hash 执行 HGETALL,优先使用 HGET/HMGET 按需获取字段,或使用 HSCAN 迭代遍历。
- List 长度控制: 写入 List 时务必配合 LTRIM 控制长度,避免列表无限膨胀导致内存耗尽。
- ZSet 分数设计: 延时任务场景下,Score 建议使用毫秒级时间戳,保证时间精度;排行榜场景下,可结合时间戳+分数避免同分排名不稳定。
- 禁止使用 KEYS *: 生产环境下该指令会全量遍历,阻塞单线程的 Redis。请务必使用 SCAN 命令替代(无锁采样迭代)。
- BigKey 治理: 单个 Hash 或 List 若达到万级甚至十万级,删除时会产生抖动。建议使用 UNLINK 异步删除,它会在后台线程释放内存而不阻塞主进程。
- 批量操作优化: 尽可能使用 MSET/MGET 或 Pipeline(流水线)。Pipeline 能将多个命令打包发送,大幅减少网络 RTT(往返时延)带来的开销。
- Pipeline 性能翻倍: 大批量写入时,利用 Pipeline 减少网络往返。Pipeline p = jedis.pipelined();
for(int i=0; i<1000; i++) p.set("key:"+i, "val:"+i);
p.sync(); // 一次性刷新缓存并执行
Redis 高效稳定的核心在于“监控”与“规范”。保持良好的使用方式(如 Key 命名、强制 TTL、禁用 KEYS *)能规避 80% 的生产事故。
二、运维监控与管理
了解 Redis 运行状态是保障系统稳定的关键。
2.1 状态深度巡检
- INFO 指令:运维核心,可重点关注以下模块:
- INFO memory:关注 used_memory_human(实际内存)和 mem_fragmentation_ratio(内存碎片率,建议在 1.0~1.5 之间)。
- INFO clients:关注 connected_clients 是否异常飙升,排查连接泄露。
- INFO stats:关注 total_commands_processed 和 ops_per_sec(瞬时 QPS)。
- INFO replication:主从架构下检查 master_link_status 是否为 up。
- MONITOR: 实时打印所有指令。生产环境应严禁长时间开启(高并发下会导致 Redis 性能下降甚至挂掉),仅用于短时间(1-2秒)抓包确认业务逻辑。
2.2 客户端管理
- CLIENT LIST:查看所有连接详情(源 IP、当前命令、idle 空闲时间)。
- CLIENT KILL IP:PORT:手动断开异常连接或占用资源过高的僵尸连接。
- CLIENT SETNAME / GETNAME:为连接命名,方便在 CLIENT LIST 中快速定位业务方。
三、持久化与部署参数优化
合理的持久化方案决定了灾难恢复的速度。
3.1 持久化方案对比 (RDB vs AOF)
| 数据安全性 | 较低,丢失两次快照间的数据 | 较高,通常配置为每秒同步 |
| 恢复速度 | 快(直接加载二进制文件) | 慢(需要逐条回放指令) |
| 文件体积 | 紧凑,体积小 | 较大,需定期 Rewrite |
| 推荐场景 | 备份、冷启动数据恢复 | 对数据一致性要求极高的业务 |
运维建议:生产环境建议开启 混合持久化 (aof-use-rdb-preamble yes),兼顾 RDB 的快和 AOF 的优点。
3.2 内存驱逐策略 (maxmemory-policy)
当内存达到 maxmemory 限制时,Redis 的表现取决于此配置:
- allkeys-lru:移除最近最少使用的 Key(最推荐)。
- volatile-ttl:优先移除即将过期的 Key。
- noeviction:不删除,直接返回错误(默认配置,极易导致业务瘫痪)。
四、集群运维与高可用架构
4.1 Redis Sentinel (哨兵模式)
- 核心任务:监控、提醒、自动故障转移。
- 运维点:quorum (法定人数) 通常设为节点数的 N/2 + 1,确保选举有效。
4.2 Redis Cluster (集群模式)
- 槽位分配:Redis Cluster 将空间分为 16384 个 Slots。
- 常用命令:
- CLUSTER NODES:查看集群拓扑及主从关系。
- CLUSTER INFO:检查集群状态(ok 或 fail)。
- CLUSTER MEET:手动拉取新节点入群。
- ASK/MOVED 错误:这是集群模式下的重定向机制,客户端需支持集群协议才能自动处理。
五、日常排查关键点
5.1 大 Key (BigKey) 治理
- 现象:Redis 响应突然变慢,带宽被打满。
- 排查:先使用自带工具执行 redis-cli –bigkeys 扫描,初步筛查是否有单个集合类型元素超过 5000(严格)、1 万(宽松)或总大小超过 10MB、单个 String 超过 10KB(严格)或 512KB(宽松),可视为 BigKey。
场景 A:单 Key 存储了巨大的 JSON 对象(String 类型)
典型案例:将整个用户信息、甚至一个小型数据库表序列化后塞进一个 String。
隐患:每次 GET 都会产生巨大的网络 IO 开销,且在高并发下序列化/反序列化会耗尽 CPU。
治理方案:
场景 B:海量成员的集合(Hash/Set/ZSet 类型)
典型案例:一个 Hash 存储了百万级用户的关注列表,或者一个 ZSet 存储了全服百万玩家的积分榜。
治理方案:
- 逻辑:new_key = original_key + ":" + (field_id % 1000)。
- 效果:将 100 万个 Field 分散到 1000 个 Hash 中,每个 Hash 仅 1000 个成员,触发 Redis 底层的 ziplist 编码,极致节省内存。
场景 C:无限增长的列表(List 类型)
典型案例:异步任务队列或日志记录,只进不出,导致 List 长度达到数十万。
治理方案:
BigKey 的发现工具
- 自带扫描:redis-cli –bigkeys
- 优点:无损扫描,实时给出每种类型最大的 Key。
- 缺点:只能看到最大的一个,信息不够全面。
- 采样分析:使用 MEMORY USAGE key 查看具体某个 Key 占用的内存字节数。
- RDB 离线分析(推荐):使用 redis-rdb-tools 等工具解析 RDB 快照文件。
- 优点:不影响线上性能,统计最详尽(支持按大小排序输出 CSV)。
BigKey 的安全删除(关键)
禁止直接使用 DEL 删除 BigKey,这很可能会导致 Redis 瞬间“卡死”。
- 方案一:异步删除 (UNLINK)
- Redis 4.0+ 引入。执行 UNLINK key 后,Redis 会立刻将 Key 从全局空间移出,真正的内存释放由后台线程异步完成。
- 方案二:分批渐进式删除(老版本/兼容性)
- Hash:使用 HSCAN 配合 HDEL,每次删除 100 个字段。
- List:循环执行 LTRIM 逐步缩小范围。
- Set/ZSet:使用 SSCAN/ZSCAN 配合 SREM/ZREM 分批清除。
5.2 慢查询排查
- CONFIG GET slowlog-log-slower-than:默认 10000 微秒 (10ms)。
- SLOWLOG GET 10:获取最近 10 条慢查询记录。
- SLOWLOG RESET:清理历史记录。
5.3 内存碎片率过高
- 如果 mem_fragmentation_ratio > 1.5,说明物理内存浪费严重。
- 在线整理:在不重启的情况下执行 CONFIG SET activedefrag yes。(注意,这会消耗一定的 CPU 资源)
六、基础的安全防护
在 redis.conf 中配置:rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG "super_secret_config"





