怎样在Node.js中防止Redis缓存击穿_使用Promise与锁机制

星芳吖_2598

星芳吖_2598

2026-07-09

489人浏览

原创

redis分布式锁需满足互斥性、安全性、超时释放、原子性及可重入性;核心用set key value nx ex t原子加锁,lua脚本校验值后删锁防误删,redisson提供自动续期等高级特性。

怎样在node.js中防止redis缓存击穿_使用promise与锁机制

缓存未命中时多个请求同时查库,怎么只让一个去执行?

核心是“请求排队”:当 redis.get(key) 返回 null,不能直接放行所有请求去查数据库,必须用某种机制让其余请求暂停,等第一个请求完成并写入缓存后,再统一返回结果。

Node.js 单线程特性反而帮了忙——不需要处理多线程锁的可见性问题,但得小心 Promise 状态共享和竞态。常见错误是每个请求都新建一个 Promise 去查库,导致 10 个并发请求各自触发一次 DB 查询。

  • 用一个全局 Map 存储正在重建缓存的 Promise:pendingPromises.set(key, promise)
  • 后续同 key 请求先查这个 Map,命中就直接 await 它,不新建 Promise
  • Promise resolve 后立即 pendingPromises.delete(key),避免内存泄漏
  • 务必加 catch 并主动 delete,否则失败后该 key 会永远卡在 pending 状态

用 Redis SETNX 实现分布式锁,为什么不能只靠它?

单纯用 SET key value NX EX 30 加锁,在 Node.js 中容易漏掉两个关键环节:锁释放时机不可控、锁被其他实例误删。比如请求 A 拿到锁,查库花了 35 秒,锁自动过期;此时请求 B 成功加锁,而请求 A 在写缓存时仍会执行 DEL key,把 B 的锁干掉了。

安全做法是用 Lua 脚本保证“判断+删除”原子性,且锁值必须是唯一标识(如 uuidv4()),不能写死。

  • 加锁:用 client.set(key, randomValue, 'NX', 'EX', 30),不是 SETNX 命令(ioredis 已弃用)
  • 解锁:必须用 Lua 脚本,检查 value 相等才删,示例:if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
  • 别在 try/finally 里无条件 DEL,那是典型误删来源
  • 锁超时时间要明显大于 DB 查询 + 缓存写入耗时,建议设为后者 2–3 倍

为什么用 EventEmitter 实现等待比纯 Promise Map 更稳?

当服务重启或进程崩溃,Map 里的 pending Promise 全丢,新请求又得重来一遍;而 EventEmitter 的 once 是事件驱动模型,天然支持“发布-订阅”,只要事件名一致,哪怕中间有延迟,监听者也能收到通知。

Browser Js
Browser Js

轻量级CDP浏览器控制,适用于AI代理。相较于内置浏览器工具,token消耗降低3‑10倍,仅在浏览时使用。

下载

不过要注意:事件名必须带业务上下文,比如 cache:rebuild:user:123,不能只用 rebuild,否则不同 key 的重建会互相干扰。

  • 首次请求触发 emitter.emit(key, result),之后所有 emitter.once(key) 都能拿到结果
  • 必须在 Promise resolve 后立刻 emit,reject 时也 emit(传 error 或 null)
  • Node.js 的 EventEmitter 默认最大监听数 10,高并发下需调用 emitter.setMaxListeners(0)
  • 不用手动清理监听器,once 自动移除,比 Map 的 delete 更省心

本地内存锁(Mutex)在集群部署下完全失效

如果你用 const mutex = new Mutex() 这类纯内存锁,它只在当前 Node 进程内有效。一旦部署多实例(PM2 cluster、K8s 多 Pod),每个实例都有自己的 mutex 实例,根本起不到互斥作用——10 个实例各放行一个请求,还是 10 次 DB 查询。

真正的分布式场景,锁必须落在共享存储上。Redis 是最直接选择,但要注意:单点 Redis 故障会导致全部请求阻塞,生产环境建议搭配降级策略(如允许少量击穿,或 fallback 到本地 LRU 缓存)。

  • 不要在开发环境用内存锁测试,然后上线换 Redis 锁——行为差异太大
  • ioredis 的 lock 插件(如 redlock)比手写 SETNX 更可靠,内置租约续期和故障转移逻辑
  • 如果 Redis 不可用,mutex 应快速失败(比如 acquire(timeout=100)),而不是无限等待
  • 锁粒度宁细勿粗,按业务 key 分,别用一个全局锁锁住所有缓存重建

真正难的不是写对一行 SETNX,而是想清楚“谁在等谁”“等多久”“等不到怎么办”。很多线上事故,都出在锁释放逻辑没覆盖异常分支,或者降级开关压根没测过。

相关专题

更多
node.js调试
node.js调试

node.js调试可以使用console.log()输出调试信息、断点调试和第三方调试工具。详细介绍:1、console.log()输出调试信息,通过在代码中插入console.log()语句,开发人员可以在控制台输出变量的值、函数的执行结果等信息,以便观察代码的执行流程和状态;2、断点调试,可以在代码中设置断点,以便在特定位置暂停代码的执行,观察变量的值和执行流程等。

2023.09.19

9769

8

JavaScript 全栈开发基础(Node.js + 前端)
JavaScript 全栈开发基础(Node.js + 前端)

本专题系统介绍 JavaScript 在全栈开发中的核心知识结构,涵盖 Node.js 基础、Express/Koa 接口构建、前端交互设计、模块化与包管理、数据库连接、前后端数据通信与部署流程。通过完整项目示例,帮助学习者掌握从浏览器到服务器的一体化开发能力,实现真正意义上的全栈入门。

2025.11.26

414

14

Node.js后端开发与Express框架实践
Node.js后端开发与Express框架实践

本专题针对初中级 Node.js 开发者,系统讲解如何使用 Express 框架搭建高性能后端服务。内容包括路由设计、中间件开发、数据库集成、API 安全与异常处理,以及 RESTful API 的设计与优化。通过实际项目演示,帮助开发者快速掌握 Node.js 后端开发流程。

2026.02.10

741

17

JavaScript Node.js流处理与高效数据管道构建实践
JavaScript Node.js流处理与高效数据管道构建实践

本专题聚焦 Node.js 在大规模数据处理场景中的应用,系统讲解 Stream 流机制、背压控制、管道(Pipe)处理以及文件与网络数据流的高效传输方式。内容包括可读流、可写流、双工流与转换流的实现原理,并结合实际案例构建高性能数据处理管道,帮助开发者提升数据处理效率与系统吞吐能力。

2026.04.07

304

13

TypeScript Node.js 全栈工程化与Monorepo架构实践
TypeScript Node.js 全栈工程化与Monorepo架构实践

本专题围绕 TypeScript 在 Node.js 全栈开发中的工程化实践展开,系统讲解 Monorepo 架构设计、包管理策略、模块复用机制以及服务端与前端统一类型系统的构建方法。通过真实项目案例,帮助开发者提升大型全栈项目的可维护性与协作效率。

2026.06.16

518

8

promise的用法
promise的用法

“promise” 是一种用于处理异步操作的编程概念,它可以用来表示一个异步操作的最终结果。Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。Promise的用法主要包括构造函数、实例方法(then、catch、finally)和状态转换。

2023.10.12

4220

6

html文本框类型介绍
html文本框类型介绍

html文本框类型有单行文本框、密码文本框、数字文本框、日期文本框、时间文本框、文件上传文本框、多行文本框等等。详细介绍:1、单行文本框是最常见的文本框类型,用于接受单行文本输入,用户可以在文本框中输入任意文本,例如用户名、密码、电子邮件地址等;2、密码文本框用于接受密码输入,用户在输入密码时,文本框中的内容会被隐藏,以保护用户的隐私;3、数字文本框等等。

2023.10.12

3543

6

常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.02

4309

19

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.14

3815

11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

RabbitMQ 教程手册
RabbitMQ 教程手册

共0课时 | 0人学习

Node.js 教程
Node.js 教程

共57课时 | 25.1万人学习