【问题标题】:Efficient way to append data to REDIS hash将数据附加到 REDIS 哈希的有效方法
【发布时间】:2020-08-24 15:31:21
【问题描述】:

我正在做一些计算,将结果存储在 REDIS 数据库中,然后再发送到数据库。

目前我在单独的 GAE 实例(使用 NodeJS 的单线程计算)中处理每个块大小为 10k 个项目的批量操作,虽然计算速度非常好,但执行 HSET 操作的 PUSH 操作需要很长时间时间,所以它会导致不同线程中的一些延迟(因为 REDIS 是单线程的 - fyi 我使用的是 Google REDIS Basic 实例)。

我做错了什么?我怎样才能让它比现在更快地推送(比如批量或一些)?

const key = '123';
for (const [column, value] of results) {
   await this.appendRedisHashValue(key, column, value);
}

public async appendRedisHash(key: string, field: string, value: any) {
        const appendRedisHashAsync = promisify(this.redisClient.hset).bind(this.redisClient);
        return appendRedisHashAsync(key, field, JSON.stringify(value));
}

如您所见,我只是使用HSET 逐个推送每个项目,想知道我们是否可以做某种SQL transactions 并在单个 HSET 事务中推送例如 10k 个项目而不是附加 REDIS 哈希每一次。

每个块(10k 项)在保存到 REDIS 后的大小约为 43MB(因此总共 100k 项提供 430MB)。对于某些架构设计它必须存储在一个单一的 REDIS 哈希中

当前速度(毫秒),每个作业都在并行单独的线程中运行:

"push": 13608
"finishedAt": "2020-05-08T22:51:26.045Z"

push": 13591,
"finishedAt": "2020-05-08T22:51:29.640Z"

"push": 15738,
"finishedAt": "2020-05-08T22:51:59.177Z"

"push": 21208,
"finishedAt": "2020-05-08T22:51:44.432Z"

"push": 13332,
"finishedAt": "2020-05-08T22:51:28.303Z"

"push": 10598,
"finishedAt": "2020-05-08T22:51:44.455Z"

"push": 27249,
"finishedAt": "2020-05-08T22:51:58.458Z"

"push": 36270,
"finishedAt": "2020-05-08T22:52:00.708Z"

"push": 25106,
"finishedAt": "2020-05-08T22:52:02.234Z"

"push": 12845,
"finishedAt": "2020-05-08T22:52:02.254Z"

我们将不胜感激。

【问题讨论】:

  • mset 不符合要求,因为这是用于简单的键/值存储而不是用于哈希,不确定管道,你有一些例子吗?
  • 你看过 Redis 流水线吗:redis.io/topics/pipelining?通过减少每个命令的 RTT(往返时间),它有助于在一个连接中向 Redis 发送一系列命令,而不是在那里连续发送单个命令。

标签: node.js typescript google-app-engine redis


【解决方案1】:

您正在做的是使用单个键/值多次调用 hset。由于往返延迟,这很糟糕。

对 10k 个键/值执行此操作将是 10k 次往返。

您可以将hset 与多个键/值一起使用,这样就可以单次访问redis。例如

hset field1 value1 field2 value2 field3 value3

【讨论】:

  • 是的,实际上正在考虑它,但不确定这是否可以通过我正在使用的“redis”库实现,我想我还找到了一个支持管道的 ioredis 库,我认为这将是此操作的最佳人选。
【解决方案2】:

我使用 HSET 和 HMSET 超过 10000 个值对其进行了测试,并创建了简单的批量函数来处理记录,从简单数据的角度来看,它看起来很棒,让我们看看它在生产环境中将如何结束。

虽然 npm redis 库不喜欢 hset 以这种方式表达,但 hmset 确实有效,这很奇怪。

const myarr = [];
const values = 10000;
for(let i = 0; i < values; i++) {
    myarr.push(`key${i}`);
    myarr.push('value');
}
await this.bulkRedisHash('myTest', myarr);
/*
    [Nest] 17800   - 2020-05-09 18:45:30   [FinalizeTaskService] starting +5ms
    [Nest] 17800   - 2020-05-09 18:45:30   [FinalizeTaskService] finished +21ms
 */
for (let i = 0; i < myarr.length; i++) {
    if (i % 2 !== 0) {
        await this.appendRedisHash('myTest2', myarr[i-1], myarr[i]);
    }
}
/*
   [Nest] 18396   - 2020-05-09 18:49:08   [FinalizeTaskService] starting +4ms
   [Nest] 18396   - 2020-05-09 18:49:09   [FinalizeTaskService] finished +795ms
*/

public async appendRedisHash(key: string, field: string, value: any) {
    const appendRedisHashAsync = promisify(this.redisClient.hset).bind(this.redisClient);
    return appendRedisHashAsync(key, field, value);
}

public async bulkRedisHash(key: string, keyValue: string[]) {
    const appendRedisHashAsync = promisify(this.redisClient.hmset).bind(this.redisClient);
    return appendRedisHashAsync(key, [...keyValue]);
}

批量追加

【讨论】:

  • JFYI,根据documentationHMSET 自 Redis 4.0.0 起被视为已弃用
  • 是的,我知道,但是当前的 npm redis 软件包不支持具有类似功能的 HSET,目前它仅支持 key,column,value
  • 它支持流水线吗?
  • 不,我认为是ioredis
  • 是的,即使 Redis.io 也有 starred 它。如果您有幸切换到另一个库,那么您可以尝试流水线输出。示例代码here.
猜你喜欢
  • 2018-01-28
  • 2016-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-08
  • 2011-06-23
  • 1970-01-01
相关资源
最近更新 更多