Nodejs中node-redis不支持‘redis pipeline’该如何解决?

Nodejs中node-redis不支持‘redis pipeline’该如何解决?

我所说的‘redis pipeline’,是指多个redis命令的请求只产生一次网络IO(底层只发送一个包),这样减少了网络io的次数,因此就提高了性能。 经过抓包分析发现,目前的node-redis版本并不支持此功能。我去翻了一下issues,作者所指的pipeline,多个命令仍然是发送多次请求,只不过从server端的回复可能是一次而已。 后来我又试了一下python版本的redis客户端,发现它就是只发送一次请求的。 请问大家有没有遇到过类似问题? 如何解决的呢?是改了源代码,还是使用了别的库呢? 谢谢!


8 回复

Nodejs中node-redis不支持‘redis pipeline’该如何解决?

问题描述

在Node.js中,node-redis库虽然提供了Redis客户端的功能,但当前版本的node-redis并不支持真正的Redis管道(pipeline)功能。所谓的Redis管道,是指将多个命令一次性发送到Redis服务器,从而减少网络I/O开销,提高性能。

解决方案

要实现真正的Redis管道功能,可以通过以下几种方法来解决:

  1. 手动实现管道功能:自己构建一个函数,将多个命令收集起来,然后一次性发送给Redis服务器。
  2. 使用其他Redis客户端库:例如ioredis,它支持真正的Redis管道功能。

示例代码

方法一:手动实现管道功能
const redis = require('redis');

const client = redis.createClient({
    host: 'localhost',
    port: 6379,
});

function pipeline(commands, callback) {
    const replies = [];
    commands.forEach(([cmd, ...args]) => {
        client[cmd](...args, (err, reply) => {
            if (err) return callback(err);
            replies.push(reply);
        });
    });

    // Wait for all commands to complete
    client.on('ready', () => {
        callback(null, replies);
    });
}

// 使用管道功能执行多个命令
pipeline([
    ['set', 'key1', 'value1'],
    ['get', 'key1'],
], (err, replies) => {
    if (err) throw err;
    console.log(replies); // 输出: [null, 'value1']
});
方法二:使用ioredis

安装ioredis库:

npm install ioredis

使用ioredis实现管道功能:

const Redis = require('ioredis');

const client = new Redis({
    host: 'localhost',
    port: 6379,
});

client.pipeline()
    .set('key1', 'value1')
    .get('key1')
    .exec((err, replies) => {
        if (err) throw err;
        console.log(replies); // 输出: [[null, 'value1']]
    });

总结

通过手动实现管道功能或使用支持管道功能的第三方库如ioredis,可以有效地解决node-redis不支持Redis管道的问题。选择哪种方法取决于你的具体需求和项目复杂度。


难道没人用过这个库吗? 那都用的什么连接redis啊~

刚刚也碰到了,我改用redis-stream了,lz也可以试试

我觉得pipeling的性能提升来自发送多个指令,但是只接收一次回复,而不是将多个指令合并成一个只发送一次请求。 虽然我没做过对比测试,但从理论上发送单个指令可以让redis服务器更早相应请求,所以性能比延迟一段时间再发送合并的指令性能要更好。

这里有一篇讨论 http://informatikr.com/2012/redis-pipelining.html 作者的结论是(我觉得这个有待测试验证)

However, contrary to the synchronous approach, the pipelining is not optimal in the number of packets sent over the network. The reason is, that it’s not possible to follow the optimal algorithm and defer flushing the output buffer, since the library user never calls a blocking receive function. The drop in performance, measuring requests per second, will be about a factor of two for heavily pipelined command sequences.

好吧,根据官方说法,pipelining确实指的是指令合并。

This time is called RTT (Round Trip Time). It is very easy to see how this can affect the performances when a client needs to perform many requests in a row (for instance adding many elements to the same list, or populating a database with many keys). For instance if the RTT time is 250 milliseconds (in the case of a very slow link over the Internet), even if the server is able to process 100k requests per second, we’ll be able to process at max four requests per second. If the interface used is a loopback interface, the RTT is much shorter (for instance my host reports 0,044 milliseconds pinging 127.0.0.1), but it is still a lot if you need to perform many writes in a row.

但是在RTT很低,比如使用loopback interface时,没有进行指令合并的影响可以降到最小。

without pipelining 1.185238 seconds with pipelining 0.250783 seconds

node_redis的“自动pipelining"其实并不是真正的pipelining,而是利用node的异步特性尽可能多地发送指令给redis。

针对你提到的“redis pipeline”问题,在Node.js环境中,node-redis库确实支持管道操作,这可以帮助你减少网络I/O的次数,从而提高性能。

解决方案

你可以使用node-redis库中的multi方法来实现管道操作。以下是一个简单的示例:

const redis = require('redis');

// 创建客户端
const client = redis.createClient();

// 使用multi方法创建一个事务
const pipeline = client.multi([
  ['set', 'key1', 'value1'],
  ['set', 'key2', 'value2'],
  ['get', 'key1']
]);

// 执行管道操作
pipeline.exec((err, replies) => {
  if (err) throw err;
  console.log(replies); // 输出: [ null, null, 'value1' ]
});

// 如果你想立即执行而不需要等待响应,可以使用`.batch()`代替
client.batch([
  ['set', 'key3', 'value3'],
  ['set', 'key4', 'value4']
]).exec();

解释

  • client.multi([...]) 创建一个事务,并允许你添加多个命令到这个事务中。这些命令会被缓存起来,直到你调用 .exec() 方法时才会一起发送给Redis服务器。
  • pipeline.exec(callback) 发送所有缓存的命令,并提供一个回调函数来处理返回的结果。

如果你需要立即发送而不等待任何回复,可以使用 client.batch([...]).exec(),这样可以确保所有命令一次性发送到Redis服务器。

注意事项

  • 确保你的node-redis库是最新的,因为旧版本可能没有完全支持某些特性。
  • 使用管道操作时,确保在所有命令完成后调用 .exec().batch().exec() 来实际发送命令,否则命令不会被执行。

希望这个解答能帮助你解决问题!

回到顶部