使用ioredis从Redis查询大字符串时延迟递增的原因排查
Node.js中ioredis重复获取大值耗时递增问题排查
在Node.js环境使用ioredis高频获取约100KB的值时,发现每次后续get操作耗时均比前一次略长。复现测试中,仅200次查询后耗时就增至初始值的6倍;且脚本运行在Redis主机上,无网络延迟,但首次查询延迟仍高达43ms。
复现耗时结果
0: time: 43ms 1: time: 45ms 2: time: 50ms ... 199: time: 234ms
复现脚本(需更新Redis连接信息)
import Redis from "ioredis"; const con = await new Redis({ port: 6379, host: "somehost", family: 4, password: "somepassword", db: 5, }); con.on("ready", function () { console.log("REDIS is ready for action"); }); // 等待1秒确保Redis连接就绪 await new Promise((resolve) => setTimeout(resolve, 1000)); // 存入100KB的测试值 const key = "some:test:key"; const value = "a".repeat(100000); await con.set(key, value); // 异步获取值200次并打印耗时 for (let i = 0; i < 200; i++) { let t1 = Date.now(); con.get(key).then((value) => { let t2 = Date.now(); console.log(`${i}: time: ${t2 - t1}ms`); }); }
问题原因分析
- 计时逻辑偏差:脚本中
t1是循环立即执行的时间,而get请求在单连接下会排队处理。后续请求需要等待前面所有请求完成才能被Redis响应,导致t2 - t1的差值包含了前面所有请求的排队等待时间,并非单次get的实际处理耗时。 - 单连接瓶颈:ioredis默认使用单连接,高频并发请求会在客户端排队,随着请求累积,等待时间持续增加,最终表现为耗时递增。
- 首次请求初始化开销:首次
get的43ms延迟可能来自Node.js模块初始化、ioredis首次请求的连接握手,或Redis内部短暂的内存同步过程(即使刚存入的值,也可能存在初始化步骤)。
解决方案
1. 修正计时逻辑
将并发请求改为串行执行,确保每次计时仅统计单次get的实际耗时:
// 串行执行get请求,准确统计单次耗时 for (let i = 0; i < 200; i++) { const t1 = Date.now(); await con.get(key); const t2 = Date.now(); console.log(`${i}: time: ${t2 - t1}ms`); }
若需保留并发测试,需为每个请求单独记录发起时间,避免排队时间干扰:
// 并发请求但单独计时 for (let i = 0; i < 200; i++) { (async () => { const t1 = Date.now(); await con.get(key); const t2 = Date.now(); console.log(`${i}: time: ${t2 - t1}ms`); })(); }
2. 启用ioredis连接池
通过配置连接池让多个请求并行处理,减少排队等待:
const con = await new Redis({ port: 6379, host: "somehost", family: 4, password: "somepassword", db: 5, poolSize: 10, // 根据业务并发量调整连接池大小 lazyConnect: true, maxRetriesPerRequest: 3 });
3. 排查Redis服务性能
- 查看Redis慢日志,确认实际请求的处理耗时:
redis-cli slowlog get 10 - 检查Redis是否在后台执行持久化操作(如RDB快照、AOF重写),这类操作会短暂阻塞进程,可调整持久化策略降低影响。
- 监控Redis内存使用情况,避免内存不足触发swap交换,导致性能下降。
4. 预热优化
首次请求前先执行几次get操作,预热连接和缓存,消除初始化开销:
// 预热请求 for (let i = 0; i < 5; i++) { await con.get(key); } // 正式测试 for (let i = 0; i < 200; i++) { const t1 = Date.now(); await con.get(key); const t2 = Date.now(); console.log(`${i}: time: ${t2 - t1}ms`); }
内容的提问来源于stack exchange,提问作者Joram
相关产品推荐
相关产品推荐

