节点中原子性的redis事务的替代方案

An alternative to redis transactions for atomicity in node

我有一个 express 网络服务器,它在 Redis 中对值为列表的键执行一些操作,这些操作需要是原子的。更具体地说,这或多或少是我的代码结构

redisclient.lrange(key, 0, -1 (error, items) => {
    .
    .
    .
    //some slightly complex code that updates the list of strings
    //obtain newItems from items
    .
    .
    .
    redisclient.del(key);
    //push the newly updated list
    for (let item of newItems){
        redisclient.rpush(key,item);
    }
});

这里的问题是,根据我的理解,为了使这些操作成为原子操作,我需要使用 Lua 脚本。不过,我对 Lua 一无所知,在 Lua 脚本中转换我的 JS 逻辑并非易事。

Node 是单线程的,但是在像这样的代码中没有其他方法可以避免不同客户端之间的竞争条件吗?

您可以使用事务来自动执行 DEL 和 RPUSH 命令。参见 CLIENT.MULTI([COMMANDS])

如果您希望在您的列表在处理过程中被修改时交易不执行,您可以为您的密钥添加一个 WATCH。参见 OPTIMISTIC LOCKS。但是在这里,您需要 recovery/retry 逻辑以防失败。

要使用 WATCH,您首先开始观看,然后使用 LRANGE 阅读列表,进行操作,然后进行 MULTIDELRPUSHEXEC。如果在 WATCHEXEC 之间修改列表,则 EXEC 将失败。

client.watch(key, function( err ){
    if(err) throw err;

    client.lrange(key, 0, -1, function(err, result) {
        if(err) throw err;

        // Process the result

        client.multi()
            .del(key)
            .rpush(key, newItems)
            .exec(function(err, results) {

                /**
                 * If err is null, it means Redis successfully attempted 
                 * the operation.
                 */ 
                if(err) throw err;

                /**
                 * If results === null, it means that a concurrent client
                 * changed the key while we were processing it and thus 
                 * the execution of the MULTI command was not performed.
                 * 
                 * NOTICE: Failing an execution of MULTI is not considered
                 * an error. So you will have err === null and results === null
                 */
            });
    });
});
Redis 中的

RPUSH 支持同时包含多个元素。考虑直接使用 [send_command][3]ES6 spread syntax 或直接传递数组(取决于您的版本 运行)一次推送多个元素。

也就是说,考虑使用 Lua 脚本。这里有一点帮助你开始:

EVAL "local list1 = redis.call('LRANGE', KEYS[1], 0, -1) for ix,elm in ipairs(list1) do list1[ix] = string.gsub(elm, 'node', 'nodejs') end redis.call('DEL', KEYS[1]) redis.call('RPUSH', KEYS[1], unpack(list1)) return list1" 1 myList

这里是 Lua 脚本的友好视图:

local list1 = redis.call('LRANGE', KEYS[1], 0, -1)
for ix,elm in ipairs(list1) do
    list1[ix] = string.gsub(elm, 'node', 'nodejs')
end
redis.call('DEL', KEYS[1])
redis.call('RPUSH', KEYS[1], unpack(list1))
return list1

这只是将列表的所有元素中的 node 替换为 nodejs。在 String Library Tutorial.

查看更多关于字符串操作的信息