你好我有以下问题:
我有一个包含字符串的哈希。这个哈希将被多个用户查询。
当用户带有 Key
首先检查它是否存在于此哈希中,如果不存在,则添加它。
如何使操作“检查哈希是否存在”、“如果不存在则添加”原子的?
阅读redis文档似乎Watch
是我需要的。如果变量发生变化,基本上开始一个事务并结束它。
我试过使用 Condition.HashNotExists
无济于事:
class Program {
public static async Task<bool> LockFileForEditAsync(int fileId) {
var database = ConnectionMultiplexer.Connect(CON).GetDatabase();
var exists = await database.HashExistsAsync("files", fileId); //this line is for shorting the transaction if hash exists
if (exists) {
return false;
}
var tran = database.CreateTransaction();
tran.AddCondition(Condition.HashNotExists("files", fileId));
var setKey = tran.HashSetAsync("files", new HashEntry[] { new HashEntry(fileId, 1) });
var existsTsc = tran.HashExistsAsync("files", fileId);
if (!await tran.ExecuteAsync()) {
return false;
}
var rezult = await existsTsc;
return rezult;
}
public const string CON = "127.0.0.1:6379,ssl=False,allowAdmin=True,abortConnect=False,defaultDatabase=0";
static async Task Main(string[] args) {
int fid = 1;
var locked = await LockFileForEditAsync(fid);
}
}
如果我通过
redis-cli
连接并在 cli
中发布: hset files {fileId} 1
, 对 之前 我发出 ExecuteAsync
(在调试器中)我预计此事务将失败,因为我放置了 Condition
.然而事实并非如此。我怎样才能基本上使用 redis 命令在两个操作上放置类似锁定的东西:
最佳答案
坏消息,好消息,以及其他一切......
坏消息
这不起作用,因为 SE.Redis 管道传输事务。这意味着所有事务命令在 ExecuteAsync()
时同时发送到服务器。叫做。但是,首先评估条件。tran.AddCondition(Condition.HashNotExists("files", fileId));
翻译为:
WATCH "files"
HEXISTS "files" "1"
而这两个命令是在
ExecuteAsync()
时先发送的。被调用来评估条件。如果条件成立 (HEXISTS "files" "1"
= 0),则发送其余的事务命令。这有效确保没有误报 , 因为如果
files
键在两者之间修改(SE.Redis 正在评估条件),WATCH
会使交易失败。问题是 假阴性 .例如,如果在 SE.Redis 评估条件时设置了哈希的另一个字段,则事务也会失败。
WATCH "files"
使它如此。我通过
redis-benchmark -c 5 HSET files 2 1
对此进行了测试运行时ExecuteAsync()
被称为。条件通过,但虽然字段“1”不存在,但交易失败,因为字段“2”设置在两者之间。我使用
MONITOR
进行了验证命令在单独的 redis-cli 窗口中。这对于排除未满足的期望或只是查看服务器的实际情况和时间非常方便。一个
WATCH
当您关心哈希的字段时没有帮助,因为当触及其他字段时它会产生误报。对此的解决方案是使用常规 key (
files:1
)。好消息
有一个命令可以做你想做的事:HSETNX .
这完全简化了您的 LockFileForEditAsync():
public static async Task<bool> LockFileForEditAsync(int fileId)
{
var database = ConnectionMultiplexer.Connect(CON).GetDatabase();
var setKey = await database.HashSetAsync("files", fileId, 1, When.NotExists);
return setKey;
}
注意
When.NotExists
范围。结果是 HSETNX "files" "1" "1"
正在发送的命令。对于其他一切...
您可以使用Lua scripts在这种情况下,您希望以原子方式完成一些条件操作,例如 how to use spop command with count if set have that much (count) element in set .
似乎您正在尝试执行分布式锁。见 Distributed locks with Redis对于您可能要考虑的其他方面。见 What is distributed Atomic lock in caches drivers?关于这个的一个好故事。
关于.net-core - 如何使用 StackExchange.Redis 使多个操作原子化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59968324/