CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
监控与测试:让 Redis 可观测、可验证
这一课你将能做到
用 INFO memory/stats/server 三段输出回答「Redis 健康吗」,并动手算出缓存命中率
用 SLOWLOG 抓慢命令,说清为什么生产绝不能开 MONITOR
用 Testcontainers 在集成测试里起真 Redis(含 @DynamicPropertySource 接线本站开关),并用 Boot 3.5 的 @MockitoBean 单测 miss 分支
为什么要学
前面 10 课你已经在「会用」,但工程闭环还差两件事:
可观测 :缓存生效了吗?命中率高不高?内存涨没涨?谁在执行慢命令拖累大家?——上生产前必须会看
可验证 :缓存代码怎么测?本站 CI 环境没有 Redis 服务,Testcontainers 让测试自己拉起一个真 Redis,测完即弃
这一课把两件事一次补齐,也是第 12 课综合实战的「验收工具包」。
先建立图像
INFO 分段(记三段就够)
├─ server :版本、运行天数 —— 排查兼容问题
├─ memory :used_memory_human、maxmemory_human、mem_fragmentation_ratio
└─ stats :keyspace_hits / keyspace_misses → 命中率;evicted_keys → 淘汰发生
命中率 = hits / (hits + misses)
SLOWLOG:服务端记录的慢命令黑名单(默认 >10ms 记录,保留 128 条)
MONITOR:实时打印所有命令——调试神器,生产毒药
跟着做
步骤 1:读 INFO 三段并算命中率
docker exec -it redis-dev redis-cli -a devpass123
INFO server # redis_version、uptime_in_days
INFO memory # used_memory_human、maxmemory_human(0=不限)、mem_fragmentation_ratio(1.0~1.5 健康)
INFO stats # keyspace_hits、keyspace_misses、evicted_keys、total_commands_processed
SET hit:demo 1
GET hit:demo # 命中 +1
GET hit:not-exist # miss +1
INFO stats | grep -E "keyspace_(hits|misses)" # 两个数字各涨 1
命中率公式:hits / (hits + misses)。业务缓存的合理预期通常在 80%~95%。
步骤 2:制造并抓一条慢命令
# 用 redis-benchmark 快速生成几万个随机 key(比 shell 循环快得多)
docker exec redis-dev redis-benchmark -n 50000 -t set -r 100000 -c 10 -q
# 慢日志阈值调低到 1ms 便于复现(单位微秒)
CONFIG SET slowlog-log-slower-than 1000
KEYS "key:*" # 生产禁令!本步仅实验
SLOWLOG GET 5 # 能看到 KEYS 那一条:耗时、命令原文
SLOWLOG RESET
CONFIG SET slowlog-log-slower-than 10000 # 恢复默认 10ms
SLOWLOG 输出四元组:序号、时间戳、耗时(微秒)、命令原文。
步骤 3:认清 MONITOR 的危险
MONITOR # 实时打印每一条命令——本机看看可以,Ctrl+C 退出
它让 Redis 额外做全量命令的格式化和分发,生产开启 = 主动给自己加压,流量大时直接拖垮。线上排查用 CLIENT LIST 和 SLOWLOG,别开 MONITOR。
步骤 4:Testcontainers 集成测试(真 Redis)
backend 测试目录新建 RedisIntegrationTest(如缺依赖,先在 pom 加 ,test scope):
org.testcontainers:junit-jupiter
@SpringBootTest
@Testcontainers
class RedisIntegrationTest {
@Container
static GenericContainer<?> redis =
new GenericContainer<>("redis:7-alpine").withExposedPorts(6379);
@DynamicPropertySource
static void redisProps(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", redis::getHost);
registry.add("spring.data.redis.port", () -> redis.getMappedPort(6379));
registry.add("codeforge.redis.enabled", () -> "true"); // 打开本站开关
}
@Autowired StringRedisTemplate redis;
@Test
void setAndGetWithTtl() {
redis.opsForValue().set("test:key", "v", Duration.ofSeconds(60));
assertThat(redis.opsForValue().get("test:key")).isEqualTo("v");
assertThat(redis.getExpire("test:key")).isPositive();
}
}
要点:容器端口是随机映射的,必须用 getMappedPort 动态注入;codeforge.redis.enabled=true 让 RedisConfig 的 4 个 Bean 真正装配。CI 不需要预装 Redis。
步骤 5:@MockitoBean 单测业务分支 不起容器的场景,Mock 掉模板测逻辑分支(Boot 3.5.4 用 @MockitoBean,@MockBean 已废弃):
@ExtendWith(SpringExtension.class)
class TrackCacheServiceTest {
@MockitoBean
StringRedisTemplate redisTemplate;
@MockitoBean
ValueOperations<String, String> valueOps;
@Test
void missThenFallbackToDb() {
given(redisTemplate.opsForValue()).willReturn(valueOps);
given(valueOps.get("coderoadmap:tracks:list")).willReturn(null); // miss
// 调用你的缓存服务 → 断言走到了 MySQL 回源分支(Mockito verify)
}
}
分工原则:分支逻辑用 Mock 单测 (快),真实读写用 Testcontainers 集成测 (准)。
讲清楚要点
命中率是缓存的体检指标 :正常业务缓存应 >80%;偏低先查 TTL 设置和 key 设计,而不是急着加缓存。
evicted_keys > 0 说明内存上限触发过淘汰 :缓存实例正常现象,持续增长要扩容;会话实例出现就是事故信号(用户被踢)。
mem_fragmentation_ratio :>1.5 碎片偏多(大量增删后常见),activedefrag 或择机重启可缓解。
SLOWLOG 是服务端视角的慢命令史 :比客户端超时日志更准;抓到 KEYS、大集合的 HGETALL/SMEMBERS 就是优化目标。
测试金字塔落在 Redis 上 :单测 Mock(分支)→ 集成 Testcontainers(真实协议)→ 生产 INFO/SLOWLOG(真实流量)。
你可能会懵的地方
命中率多少算合格?
无绝对值。读多写少的内容站 90%+ 常见;刚上线冷启动期低是正常的,看趋势不看瞬时值。
Testcontainers 每次测试都拉镜像很慢?
首次拉取后镜像本地复用;容器随测试上下文启停,比「共享开发 Redis」干净且无状态污染。
@MockBean 不是还能用吗?
Spring Boot 3.4 起废弃,3.5.4 用 @MockitoBean(org.springframework.test.context.bean.override.mockito 包)。老教程代码要迁移。
INFO stats 的 hits 会重复计吗?
EXPIRE 过期导致的 miss 也计入 misses;命中率是业务视角的近似指标,关注趋势。
SLOWLOG 会不会把 Redis 拖慢?
记录本身开销极小(超时判断 + 环形缓冲),放心常开。
错法对照 错法 问题 正解 生产开 MONITOR 排查 自我拖垮 CLIENT LIST / SLOWLOG 只看接口快慢不看命中率 缓存形同虚设不知情 INFO stats 定期观测 测试直连开发机 Redis 数据污染、CI 不可复现 Testcontainers 隔离实例 用 @MockBean(Boot 3.5) 已废弃编译告警 @MockitoBean 集成测试写死端口 6379 容器是随机映射 getMappedPort + @DynamicPropertySource SLOWLOG 阈值从不调 抓不到也看不懂慢命令 按环境调 slowlog-log-slower-than
卡点急救
Testcontainers 起不来(Docker 没运行) :先启动 Docker Desktop;CI 里确认 Runner 有 Docker 环境。
@DynamicPropertySource 注入后 Bean 还是没装配 :检查 codeforge.redis.enabled 是否给了字符串 "true"。
SLOWLOG GET 空 :没有超过阈值的命令。调低 slowlog-log-slower-than 再造慢命令。
INFO 里 maxmemory 是 0 :未设上限。容器/生产必设 maxmemory + 淘汰策略(第 9 课)。
测试里 opsForValue() 返回 null NPE :Mock 没配 given(redisTemplate.opsForValue()).willReturn(valueOps)。
面试常问
怎么评估缓存效果?
INFO stats 算命中率 hits/(hits+misses),看 evicted_keys 判断内存压力;结合接口 P99 与 MySQL QPS 改造前后对比。
线上 Redis 突然变慢怎么排查?
SLOWLOG 找慢命令 → redis-cli --bigkeys 找大 key → INFO memory 看碎片率 → 排查 KEYS 类命令或持久化抖动。
你们的 Redis 相关代码怎么测?
两层:业务分支用 @MockitoBean(Boot 3.5)Mock 模板;集成用 Testcontainers 起真 Redis + @DynamicPropertySource 注入连接与本站开关,CI 可复现、无状态污染。
自测三问
命中率怎么算?evicted_keys 持续增长分别对缓存实例和会话实例意味着什么?
为什么生产禁开 MONITOR?替代手段是什么?
Testcontainers 方案里必须动态注入的三个属性是什么?
小结一句话 INFO 看健康、SLOWLOG 抓慢、MONITOR 生产禁;测试用 Testcontainers 起真 Redis、@MockitoBean 测分支。
延伸
SLOWLOG 命令
下一课:综合实战 ——给本站 TrackService 完整加上 Redis 缓存层,把 12 课所学串成一次真实交付。