You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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`);
  });
}

问题原因分析

  1. 计时逻辑偏差:脚本中t1是循环立即执行的时间,而get请求在单连接下会排队处理。后续请求需要等待前面所有请求完成才能被Redis响应,导致t2 - t1的差值包含了前面所有请求的排队等待时间,并非单次get的实际处理耗时。
  2. 单连接瓶颈:ioredis默认使用单连接,高频并发请求会在客户端排队,随着请求累积,等待时间持续增加,最终表现为耗时递增。
  3. 首次请求初始化开销:首次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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 00:12:45