如何进行延续并将其作为 MULTI 命令的一部分包含在内?

How to do a continuation and include it as part of the MULTI command?

总而言之,我正在尝试调用 ITransaction.SetPopAsync(...) 并执行其他写入操作作为底层 MULTI Redis 原子操作的一部分。

我尝试执行SetPopAsync(...)调用Task.ContinueWith的延续,并在回调中执行所谓的其他写入操作,但当我发现这些被执行时,我感到很惊讶在基础 MULTI 操作结束后(即当 ITransaction.ExecuteAsync() 完成时)。

也许我弄错了,但我觉得只有将 LUA 脚本加载到 Redis 并从 C# 调用它才能完成此原子操作。

对于我的案例,是否有任何其他解决方案可以完全在 C# 和 StackExchange.Redis 库中实现?

更多信息...

我发现有关于此主题的特定文档 here

现在我更困惑了,因为 *如果你执行整个 spop 而它不是 MULTI 命令的一部分,并且当你在 [= 之后创建其他键时会发生什么18=], 系统崩溃导致数据损坏?

所以...我是否遗漏了一些可以让我执行整个 spop 并确保所有其他操作都将完成并持续存在的东西?

示例场景...

正如@BerinLoritsch 在这里的一些评论中所要求的,我将在这里抛出一个 伪代码 使用 Redis 命令来举例说明这里发生了什么:

MULTI
key1MemberValue = spop key1 1

sadd key2 key1MemberValue 
zadd key3 1 key1MemberValue 
EXEC

请注意,我不能使用 smove,因为我不知道真实情况下的集合成员。我需要随机弹出一个。

现在,实际上在我的特殊情况下,有另一种方法可以解决同样的问题,它允许我以原子性进行操作。

不使用 spop,而是使用 Redis 命令检查以下伪代码:

time = [current time]
member = zrangebyscore key1 -inf +inf limit 0 1

zrem key1 member
zadd key2 time member
zadd key3 time member

目前,这里的最佳解决方案似乎是 LUA 调用上述代码清单命令的脚本,因为 Redis 是单线程的,它可以一次发生一个操作,LUA 脚本本身被认为是一个操作,如果我想确保其他操作不会尝试弹出相同的 set 值,我觉得这应该是要走的路......

此外,我不得不将 常规集 转换为 有序集 。由于 Redis 在复制和集群环境中避免数据损坏的限制,您不能在非确定性命令之后调用写入命令,无论是读取还是写入操作。感谢 zrangebyscore(也可能是 zrangezrevrangezrevrangebyscore...),因为 它是确定性的 ,我可以模拟pop操作,仍然使用集合语义

最终,您需要了解 Redis multi/exec 块不像 ADO.NET 事务。从根本上说,以下(来自问题)在redis中是不可能的:

MULTI
key1MemberValue = spop key1 1

sadd key2 key1MemberValue 
zadd key3 1 key1MemberValue 
EXEC

因为你从 spop 得到的是:QUEUED。就这样。在 EXEC 之前,您不会得到任何 答案,因此您不能sadd/zaddMULTI.

里面

可以可以想象,在原始redis中,涉及WATCH的事情;然而,所有这一切意味着如果 WATCH-ed 键被另一个连接改变,MULTI/EXEC 块被 abandoned,未执行;你会仍然spop-ped.

所以从根本上说:是的,如果您希望在没有竞争连接使状态无效的情况下发生这种情况:您将必须通过 Lua 和 EVAL/EVALSHA 来实现 - 或者它会需要更改 Condition API.