从 ioredis 迁移
了解 ioredis 与 node-redis 之间的区别。
Redis 之前推荐使用 ioredis 客户端库进行 Node.js 开发,但该库现已弃用,推荐使用 node-redis。本指南概述了这两个库之间的主要异同。如果您是 ioredis 用户,并且想启动新的 Node.js 项目或将现有的 ioredis 项目迁移到 node-redis,您可能会发现此信息很有用。
ioredis 与 node-redis 对比
下表总结了 ioredis 和 node-redis 如何实现 Redis 的一些关键特性。有关每个特性的更多信息,请参阅以下部分。
连接
| 特性 | ioredis |
node-redis |
|---|---|---|
| 初始化连接 | 在创建客户端实例时发生 | 需要您在客户端实例上调用一个方法 |
| 连接丢失后重新连接 | 默认为自动 | 默认为手动 |
| 连接事件 | 发出 connect、ready、error 和 close 事件 |
发出 connect、ready、error、end 和 reconnecting 事件 |
命令处理
| 特性 | ioredis |
node-redis |
|---|---|---|
| 命令大小写 | 仅小写(例如 hset) |
大写或驼峰式大小写(例如 HSET 或 hSet) |
| 命令参数处理 | 参数对象展平并直接传递各项 | 解析参数对象以生成正确的参数列表 |
| 异步命令结果处理 | 回调和 Promises | 仅支持 Promises |
| 执行任意命令 | 使用 call() 方法 |
使用 sendCommand() 方法 |
技巧
| 特性 | ioredis |
node-redis |
|---|---|---|
| 管道 | 自动,或使用 pipeline() 命令 |
自动,或使用 multi() 命令 |
| Scan 迭代 | 使用 scanStream() 等 |
使用 scanIterator() 等 |
| 订阅频道 | 使用客户端对象的 client.on('message', ...) 事件 |
使用 subscribe(...) 命令 |
特定命令
| 命令 | ioredis |
node-redis |
|---|---|---|
SETNX |
显式支持 | 作为 SET 命令的一个选项支持 |
HMSET |
显式支持 | 支持标准 HSET 功能 |
CONFIG |
显式支持 | 支持单独的 configGet(), configSet() 等方法 |
详细信息
以下部分更详细地解释了 ioredis 和 node-redis 之间的对比要点。
初始化连接
当您创建客户端对象实例时,ioredis 会连接到 Redis 服务器
const client = require('ioredis');
// Connects to localhost:6379 on instantiation.
const client = new Redis();
node-redis 要求您在客户端对象上调用 connect() 方法以建立连接
import { createClient } from 'redis';
const client = await createClient();
await client.connect(); // Requires explicit connection.
连接丢失后重新连接
如果连接因错误丢失,ioredis 会自动尝试重新连接。默认情况下,node-redis 不会尝试重新连接,但您可以在创建客户端对象时启用自定义重新连接策略。更多信息请参阅连接断开后重新连接。
连接事件
ioredis 发出的 connect、ready、error 和 close 事件等同于 node-redis 中的 connect、ready、error 和 end 事件,但 node-redis 还会发出 reconnecting 事件。更多信息请参阅连接事件。
命令大小写
ioredis 中的命令方法始终是小写。使用 node-redis 时,您可以使用方法名称的大写或驼峰式版本。
// ioredis
client.hset('key', 'field', 'value');
// node-redis
client.HSET('key', 'field', 'value');
// ...or
client.hSet('key', 'field', 'value');
命令参数处理
ioredis 将命令参数解析为字符串,然后将其传递给服务器,类似于 redis-cli 的方式。
// Equivalent to the command line `SET key 100 EX 10`.
client.set('key', 100, 'EX', 10);
作为参数传递的数组会被展平为单个元素,对象会被展平为顺序的键值对
// These commands are all equivalent.
client.hset('user' {
name: 'Bob',
age: 20,
description: 'I am a programmer',
});
client.hset('user', ['name', 'Bob', 'age', 20, 'description', 'I am a programmer']);
client.hset('user', 'name', 'Bob', 'age', 20, 'description', 'I am a programmer');
node-redis 对命令参数使用预定义格式。其中包括用于命令选项的特定类,这些类通常与 CLI 命令的语法不对应。在内部,node-redis 使用您传递的方法参数构建正确的命令。
// Equivalent to the command line `SET bike:5 bike EX 10`.
client.set('bike:5', 'bike', {EX: 10});
异步命令结果处理
ioredis 和 node-redis 的所有命令都异步执行。ioredis 支持回调和 Promise 返回值来响应命令结果
// Callback
client.get('mykey', (err, result) => {
if (err) {
console.error(err);
} else {
console.log(result);
}
});
// Promise
client.get('mykey').then(
(result) => {
console.log(result);
},
(err) => {
console.error(err);
}
);
node-redis 仅支持 Promise 对象作为结果,因此您必须始终使用 then() 处理程序或 await 操作符来接收它们。
执行任意命令
ioredis 允许您使用 call() 命令,以类似于 redis-cli 的格式发出任意命令
await client.call('JSON.SET', 'doc', "$", '{"f1": {"a":1}, "f2":{"a":2}}');
在 node-redis 中,您可以在事务外部使用 sendCommand() 实现相同的效果
await client.sendCommand(['hset', 'hash2', 'number', '3']);
在事务中,使用 addCommand() 包含任意命令。请注意,您可以在同一事务中自由混合使用 addCommand() 调用和标准命令
const responses = await client.multi()
.addCommand(['hset', 'hash3', 'number', '4'])
.hGet('hash3', 'number')
.exec();
管道
如果 ioredis 和 node-redis 在同一个事件循环的“tick”中执行命令,它们都会自动进行管道化(更多信息请参阅执行管道)。
您也可以在两个客户端中创建包含显式命令的管道。使用 ioredis,您可以使用 pipeline() 命令链式调用一系列命令,最后使用 exec() 运行管道
// ioredis example
client.pipeline()
.set('foo', '1')
.get('foo')
.set('foo', '2')
.incr('foo')
.get('foo')
.exec(function (err, results) {
// Handle results or errors.
});
对于 node-redis,方法类似,不同之处在于您调用 multi() 命令开始管道,并调用 execAsPipeline() 运行它
client.multi()
.set('seat:3', '#3')
.set('seat:4', '#4')
.set('seat:5', '#5')
.execAsPipeline()
.then((results) => {
// Handle array of results.
},
(err) => {
// Handle errors.
});
Scan 迭代
ioredis 支持 scanStream() 方法,用于从 SCAN 命令返回的键集合创建一个可读流
const client = new Redis();
// Create a readable stream (object mode)
const stream = client.scanStream();
stream.on('data', (resultKeys) => {
// `resultKeys` is an array of strings representing key names.
// Note that resultKeys may contain 0 keys, and that it will sometimes
// contain duplicates due to SCAN's implementation in Redis.
for (let i = 0; i < resultKeys.length; i++) {
console.log(resultKeys[i]);
}
});
stream.on('end', () => {
console.log('all keys have been visited');
});
您也可以使用类似的 hscanStream()、sscanStream() 和 zscanStream() 分别迭代哈希、集合或有序集合中的项。
node-redis 使用 scanIterator() 方法(以及相应的 hscanIterator()、sscanIterator() 和 zscanIterator() 方法)处理 Scan 迭代。这些方法为游标扫描的每一页返回一个集合对象(这有助于使用 MGET 和其他多键命令提高效率)
for await (const keys of client.scanIterator()) {
const values = await client.mGet(keys);
// Process values...
}
订阅频道
ioredis 通过客户端对象上的 message 事件报告传入的发布/订阅消息(更多关于消息的信息,请参阅发布/订阅)
client.on('message', (channel, message) => {
console.log(Received message from ${channel}: ${message});
});
使用 node-redis,您使用 subscribe() 命令注册消息回调。此外,当您使用一个连接进行订阅时,该连接不能发出任何其他命令,因此您必须为订阅创建一个专用连接。使用 client.duplicate() 方法创建一个与原始连接具有相同设置的新连接。
const subscriber = client.duplicate();
await subscriber.connect();
await subscriber.subscribe('channel', (message) => {
console.log(Received message: ${message});
});
SETNX 命令
ioredis 使用显式方法实现了 SETNX 命令
client.setnx('bike:1', 'bike');
node-redis 不提供 SETNX 方法,但通过 SET 命令的 NX 选项实现了相同的功能
await client.set('bike:1', 'bike', {'NX': true});
HMSET 命令
HMSET 命令自 Redis v4.0.0 起已被弃用,但 ioredis 仍支持它。使用 node-redis 时,您应该使用带有多个键值对的 HSET 命令。更多信息请参阅 HSET 命令页面。
CONFIG 命令
ioredis 支持 config() 方法来设置或获取服务器配置选项
client.config('SET', 'notify-keyspace-events', 'KEA');
node-redis 没有 config() 方法,但支持标准命令 configSet()、configGet()、configResetStat() 和 configRewrite
await client.configSet('maxclients', '2000');