最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
怎样防止Redis穿透攻击引发数据库连接池耗尽_增加连接池扩容策略
时间:2026-07-09 10:27:52 编辑:袖梨 来源:一聚教程网
单纯扩容数据库连接池不能解决缓存穿透,必须在Redis层用空值缓存(如SETEX key 30 "")、布隆过滤器(需全量初始化+误判兜底)和接口层参数校验三者至少落地两项。
单纯扩容数据库连接池不能解决缓存穿透,反而会掩盖真实问题、加剧资源争抢。 穿透请求打到数据库时,哪怕连接池从 50 扩到 500,只要请求持续构造无效 user_id 或 product_sku,连接照样会被占满、超时堆积、触发连接泄漏或事务卡死。真正要拦,得在 Redis 层就让这些请求“止步”。
空值缓存必须设 TTL,且不能用永久存储
空值缓存是防御穿透最直接有效的手段,但很多人栽在 TTL 设置上:
- 不设过期时间(如用
SET key "")→ 空值永久驻留,Redis 内存缓慢上涨,最终 OOM - 过期时间设太长(如 24 小时)→ 无效 key 长期占用内存,且无法响应业务变更(比如某类 ID 规则已弃用)
- 过期时间设太短(如 1 秒)→ 拦不住并发请求,多个线程几乎同时查到空,仍会重复打库
推荐值:对大多数业务,SETEX key 30 ""(30 秒)是平衡点。它足够挡住突发扫描,又不会长期污染缓存。PHP 中用 $redis->setex($key, 30, '');Java RedisTemplate 用 opsForValue().set(key, "", 30, TimeUnit.SECONDS)。
布隆过滤器不是“加了就完事”,得管住初始化和误判兜底
布隆过滤器能前置拦截 99% 的无效 key,但实际落地常忽略两点:
- 初始化阶段没全量加载合法 key → 过滤器形同虚设。例如用户表有 800 万记录,只导入前 10 万,漏掉的 key 全部穿透
- 遇到误判(
bf.exists('product_bf', '123456789')返回True,但 DB 实际无此商品)→ 后续流程没做空值缓存,导致该 key 下次仍穿透
务必补上兜底:即使布隆说“可能存在”,查库为空后,仍要走一遍空值缓存逻辑。Python 示例中,bf_client.exists() 返回 False 直接拒掉;返回 True 后,DB 查询为空 → 立即 redis.setex('user:123456789', 30, '')。
接口层校验比缓存层拦截更早、更省资源
很多穿透源于参数本身非法,比如 user_id=abc、order_no=(空)、sku=999999999999999999999(超出位数)。这类请求根本不该进 Redis:
- PHP 中用
filter_var($userId, FILTER_VALIDATE_INT)或正则/^d{1,10}$/提前拦截 - Spring Boot 用
@Pattern(regexp = "^d{1,10}$")注解校验入参 - 网关层(如 Nginx、Kong)配置
if ($args ~* "user_id=[^0-9]") { return 400; }
这一层拦截成功,连 Redis connect 都省了,CPU 和网络开销直接归零。别等请求跑到缓存再处理。
数据库连接池耗尽时,光看“连接数满”会误判根本原因
当监控显示 HikariCP - ActiveConnections=50/50 或 MySQL Threads_connected 持续高位,第一反应不该是调大池子,而是查三件事:
- 查慢日志:是否存在
SELECT * FROM user WHERE id = ?绑定参数为null或极长字符串,导致全表扫描 - 查应用线程堆栈:是否有大量线程卡在
JDBCStatement.execute(),说明 SQL 本身执行慢,不是连接不够 - 查 Redis
info stats:若rejected_connections在涨,说明穿透已严重到 Redis 自身都开始拒绝新连接,此时扩 DB 连接池毫无意义
穿透引发的连接池耗尽,本质是“无效请求 + 无防护逻辑”双击打穿。修复必须回到请求入口——校验、布隆、空值,三者至少落地两项。连接池只是最后一道缓冲,不是保险丝。