redis-py 监视哈希键

5 投票
2 回答
5231 浏览
提问于 2025-04-17 12:21

我正在用redis-py这个库通过Python和Redis进行交互。现在我遇到一个情况,我需要原子性地更新一个哈希键,但在更新之前,我需要先获取这个键的值。在查阅文档时,我发现可以使用管道和WATCH命令来判断一个键是否发生了变化。请问有没有办法在哈希内部监视一个键?还是说这个方法只适用于单个键?

2 个回答

0

你觉得用 MULTI 这个功能怎么样?这样你就不用担心在哈希里面监视某个键了(正如你提到的,WATCH 似乎不支持这个)。

8

你不能直接监视哈希键,这在Redis中目前是不支持的。不过,你可以使用额外的“锁”字符串键,并定义一个规则,任何修改你哈希值的人都应该按照下面的步骤来操作任何哈希键 K

  1. WATCH lock:K
  2. HGET K,保存当前值
  3. 开始MULTI。
  4. SET lock:K ""
  5. HSET K 更新后的值
  6. EXEC

这样可以确保更新的哈希值不会被同时覆盖。

虽然这是一个Python的问题,但我提供了一个NodeJS的函数来实现上面的规则(只是为了展示一个思路):

/**
 * Concurrently updates Redis string and hash value under the specified key.
 *
 * @param redisCli Redis client.
 * @param hashName Hash name.
 * @param objId Object ID.
 * @param transFun Cache object transformation function (i.e. a modification that we need to apply).
 * @param cbFun Callback function, to which a modified object is passed in case of success.
 */
exports.redisUpdateHashConcurrently = function(redisCli, hashName, objId, transFun, cbFun) {
    var lockKey = hashName + ':' + objId + ':lock';

    redisCli.watch(lockKey); // Step 1.

    redisCli.hget(hashName, objId, function(err, obj) { // Step 2.
        if (err) {
            redisCli.unwatch();

            cbFun && cbFun(undefined, err);

            return;
        }

        if (obj) {
            var modObj = transFun(JSON.parse(obj));
            var value = JSON.stringify(modObj);

            redisCli.multi() // Step 3.
                .set(lockKey, '') // Step 4.
                .expire(lockKey, 3)
                .hset(hashName, objId, value) // Step 5.
                .exec(function(err, replies) { // Step 6.
                    if (!replies) { // Object was modified by someone else, retry.
                        exports.redisUpdateHashConcurrently(redisCli, hashName, objId, transFun, cbFun);
                    }
                    else { // We have succeeded.
                        cbFun && cbFun(modObj, undefined);
                    }
                });
        }
        else {
            redisCli.unwatch();
        }
    });
};

请注意,你可以为你的“锁”键设置一个TTL(生存时间),这样它们最终会被删除。

撰写回答