缓存穿透是指客户端请求的数据在redis和数据库中均不存在,导致请求持续穿透缓存直达数据库,引发数据库压力剧增甚至宕机;其核心特征是“数据本身不存在”,故无法回填缓存,每次请求均重复穿透。

Redis 缓存穿透本身不会直接导致 Redis 日志暴增,但会间接引发数据库日志、应用层日志(如 MyBatis、Spring Boot access log)或监控埋点日志的异常增长。关键在于:穿透请求最终落到数据库和业务逻辑层,而这些环节的日志行为才是日志膨胀的源头。
为什么缓存穿透不写 Redis 日志,却让其他日志疯涨?
Redis 默认的 redis.log 主要记录服务启停、配置加载、AOF/RDB 操作、连接异常、慢查询等系统级事件;它**不记录每个 GET/SET 的 key 值或业务语义**。即使每秒 10 万次请求 GET user:999999999(根本不存在),Redis 日志里通常只看到几条连接警告或 INFO 统计波动,不会逐条记 key。
真正暴增的是:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 数据库慢查询日志(
slow_query_log):大量SELECT * FROM user WHERE id = ?返回空结果,且未走索引(如用负数、超长随机 ID),触发慢查阈值 - MyBatis 的
debug级 SQL 日志:每条空查都会输出完整 SQL 和参数 - Spring Boot 的
org.springframework.web.servlet.DispatcherServletDEBUG 日志:记录每个 HTTP 请求的入参、响应状态(如反复返回 200 + null) - 自定义监控埋点(如 Micrometer + Prometheus):若对「空查次数」打点但未聚合,指标采集日志可能刷屏
如何精准识别穿透流量并控制日志量?
不能靠“关日志”来治标,得在日志产生前做拦截和分级。重点不是少记,而是记对地方、记有效信息:
- 在 DAO 层加判断:查询数据库前,先检查参数合法性(如
userId 10000000),非法参数直接抛IllegalArgumentException,不进 SQL 日志 - 用
@ConditionalOnProperty控制日志开关:例如仅在logging.level.com.example.dao.UserDao=warn时记录空查,避免 debug 级全量输出 - 在网关或 Filter 层统计高频空 key:比如 1 分钟内
user:-1出现 500+ 次,自动触发告警并临时限流,而非记 500 条日志 - 把「空查命中」指标单独上报:用
MeterRegistry.counter("cache.miss.empty", "key", "user")替代文本日志,既可监控又不占磁盘
布隆过滤器 + 空值缓存组合下,日志该怎么记?
当已部署 BloomFilter 拦第一道(不存在的 key 直接拒绝),再用 redis.set("user:999", "{}", "EX", 300) 挡第二道时,日志策略要分层:
-
BloomFilter误判(false positive)导致的无效放行:这类请求仍会走到 DB,但频率极低(可控在 0.05% 内),建议只记录误判率指标,不记单条日志 - 空值缓存命中的请求:完全不查 DB,此时 连 DAO 层都不进,自然无 SQL 日志;只需在缓存读取层加 trace 日志(如
log.debug("empty cache hit: {}", key)),且建议设为trace级别,生产默认关闭 - 空值缓存过期后首次重建:这时才需要记一条
log.info("rebuild empty cache for {}", key),避免重复写
最容易被忽略的一点是:日志暴增往往不是因为穿透本身,而是开发阶段开启了全量 debug 日志,上线后忘了关——尤其是 MyBatis 的 logging.level.org.mybatis=debug 和 Spring 的 logging.level.org.springframework.web=debug。上线前务必核对 application-prod.yml 中的 logging.level 配置项。










