Pes*_*med 6 javascript redis node.js ioredis keydb
我们使用 Redis 很长时间,直到我们得出结论,迁移到 KeyDB 可能是其功能的不错选择。
环境
OS: Centos 7
NodeJs: v12.18.0
Redis: v6.0.5
Targeted KeyDB: v0.0.0 (git:1069d0b4) // keydb-cli -v showed this. Installed Using Docker.
ioredis: v4.17.3
pm2: v4.2.1 // used for clustering my application.
Run Code Online (Sandbox Code Playgroud)
背景
参考 KeyDB 文档,KeyDB 兼容最新版本的 Redis。
KeyDB 仍然与 Redis 模块 API 和协议完全兼容。因此,从 Redis 到 KeyDB 的迁移非常简单,并且类似于您在 Redis 到 Redis 场景中所期望的迁移。https://docs.keydb.dev/docs/migration/
在同一页面中,他们提供了与 KeyDB 兼容的 redis 客户端列表。该列表包含我正在使用的 ioredis。
KeyDB 与此处列出的所有 Redis 客户端兼容,因此不必担心。只需像使用 Redis 一样使用您的客户端。 https://docs.keydb.dev/docs/migration/
问题
正如文档中所说。我应该能够在几个小时内轻松迁移到 KeyDB。好吧,事实并非如此!至少对我来说不是!我花了我最后 3 天在互联网上搜索解决方案。我得出的结论是我应该写信给 stackoverflow :)
这个问题在某种程度上很有趣。客户端实际上正在使用 KeyDb 并且该过程实际上是在设置和检索密钥(不确定但可能会在错误期间丢失一些数据。)。但是在 10% 的时间里,它给了我以下错误,并在一段时间后继续工作。因为我使用 Redis 在我的生产环境中存储会话和其他东西;我不能冒险忽视这种坚持的错误。
error: message=write EPIPE, stack=Error: write EPIPE
./app-error-1.log:37: at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:92:16), errno=EPIPE, code=EPIPE, syscall=write
Run Code Online (Sandbox Code Playgroud)
我在几乎所有的互联网上搜索了这个错误,但没有人提供解决方案,也没有人解释出了什么问题。
幸运的是,该过程“有时”会显示错误堆栈。它指向lib/redis/index.ts:711ioredis 代码内部。我不知道它是做什么的。
(stream || this.stream).write(command.toWritable());
Run Code Online (Sandbox Code Playgroud)
https://github.com/luin/ioredis/blob/master/lib/redis/index.ts#L711
我在 ioredis github 存储库上发现了一些问题,提到了一些 EPIPE 错误。但其中大部分是关于错误处理的内容,并且都标记为已解决。
我还在 google 上发现了一些一般的 EPIPE 错误(其中大多数是关于 socket.io 的,这不是我使用的。)
包起来
这件事有什么问题?
因为没有人在赏金结束时写下答案。我正在为以后遇到此错误的人们写下解决该问题的经验。
请注意,这不是规范的答案。但这只是一种解决方法
我首先分享正在发生的事情。
我们尝试从托管近 600,000 个密钥的 Redis 服务器进行迁移。标准迁移过程需要花费大量时间才能将大量密钥从 Redis 传输到 keyDB。所以我遇到了一个不同的解决方案。
我们的 KeyDB 在 2 个主动-主动副本服务器上运行。我将向那些想知道该系统如何工作的人提供链接。
https://medium.com/faun/failover-redis-like-cluster-from-two-masters-with-keydb-9ab8e806b66c
解决方案是使用一些 MongoDB 数据库聚合重新构建我们的 Redis 数据,并在 KeyDB 上执行一些批量操作。
这是一个模拟(与源不完全一样。而且我没有测试语法错误)
const startPoint =
(Number.parseInt(process.env.NODE_APP_INSTANCE) || 0) * 40000;
let skip = 0 + startPoint;
let limit = 1000;
let results = await SomeMongooseSchema.find({someQueries}).limit(1000).skip(skip);
let counter = 0;
while (results.length){
if(counter > 39) break;
for(const res of results){
const item = {
key: '',
value: ''
};
// do some build ups on item
...
// end n
app.ioRedisClient.set(item.key, item.value);
}
counter++;
skip = i * limit + startPoint;
results = await SomeMongooseSchema.find({someQueries}).limit(limit).skip(skip);
}
Run Code Online (Sandbox Code Playgroud)
使用pm2将所有密钥设置为 keyDB 在 16 个进程上运行此代码大约需要 45 分钟。(相比4-5小时)
pm2 start app.js -i 16
Run Code Online (Sandbox Code Playgroud)
当我们在 Redis 服务器上运行代码时。它可以工作,但在 KeyDB 上出现以下错误。
error: message=write EPIPE, stack=Error: write EPIPE
./app-error-1.log:37: at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:92:16), errno=EPIPE, code=EPIPE, syscall=write
Run Code Online (Sandbox Code Playgroud)
首先,我首先通过创建事务来调整代码,而不是单独设置每个键。并在每1000次操作之间设置1秒的间隙。代码更改如下。
const startPoint =
(Number.parseInt(process.env.NODE_APP_INSTANCE) || 0) * 40000;
let skip = 0 + startPoint;
let limit = 1000;
let results = await SomeMongooseSchema.find({someQueries}).limit(1000).skip(skip);
const batch = app.ioredisClient.multi();
let counter = 0;
while (results.length){
if(counter > 39) break;
for(const res of results){
const item = {
key: '',
value: ''
};
// do some build ups on item
...
// end n
batch.set(item.key, item.value);
}
counter++;
await batch.exec();
await sleep();
skip = i * limit + startPoint;
results = await SomeMongooseSchema.find({someQueries}).limit(limit).skip(skip);
}
Run Code Online (Sandbox Code Playgroud)
这样就把错误率降低了,操作时间只要20分钟。但错误仍然存在。
我怀疑这个错误可能是由于docker版本的一些权限错误造成的。因此,我要求我们的服务器管理员进行检查,如果可能的话,删除 docker 版本并从 rpm 存储库进行安装。
https://download.keydb.dev/packages/rpm/centos7/x86_64/
Run Code Online (Sandbox Code Playgroud)
这样做了并且成功了。所有错误均消失,并在 20 分钟内成功迁移。
我不认为这是一个真正的答案。但这对于一些专家找出问题所在应该很有用。