.net core 如何使用StackExchange.Redis使多个操作原子化
您好,我有以下问题: 我有一个包含字符串的哈希。此哈希将被多个用户查询 当用户使用.net core 如何使用StackExchange.Redis使多个操作原子化,.net-core,redis,transactions,locking,stackexchange.redis,.net Core,Redis,Transactions,Locking,Stackexchange.redis,您好,我有以下问题: 我有一个包含字符串的哈希。此哈希将被多个用户查询 当用户使用键来首先检查此哈希中是否存在此哈希,如果不存在,则添加它 如何使“检查哈希是否存在”、“如果不存在则添加”操作成为原子操作? 阅读redis文档,我需要的似乎是Watch。基本上,启动一个事务,并在变量发生变化时结束它 我已尝试使用条件。HashNotExists无效: class Program { public static async Task<bool> LockFileFor
键来首先检查此哈希中是否存在此哈希,如果不存在,则添加它
如何使“检查哈希是否存在”、“如果不存在则添加”操作成为原子操作?
阅读redis文档,我需要的似乎是Watch
。基本上,启动一个事务,并在变量发生变化时结束它
我已尝试使用条件。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);
}
}
类程序{
公共静态异步任务LockFileForEditAsync(int-fileId){
var database=ConnectionMultiplexer.Connect(CON).GetDatabase();
var exists=await database.HashExistsAsync(“files”,fileId);//如果存在哈希,此行用于缩短事务
如果(存在){
返回false;
}
var tran=database.CreateTransaction();
tran.AddCondition(Condition.HashNotExists(“files”,fileId));
var setKey=tran.HashSetAsync(“文件”,新HashEntry[]{newhashentry(fileId,1)});
var existsc=tran.HashExistsAsync(“文件”,fileId);
如果(!wait tran.ExecuteAsync()){
返回false;
}
var rezult=等待退出;
返回rezult;
}
public const string CON=“127.0.0.1:6379,ssl=False,allowAdmin=True,abortConnect=False,defaultDatabase=0”;
静态异步任务主(字符串[]args){
int fid=1;
var locked=等待LOCKFILEFOREDITANCH(fid);
}
}
如果我通过redis cli
连接并在cli
中发出:hset files{fileId}1
,在发出ExecuteAsync
(在调试器中)之前发出ExecuteAsync
,我预计该事务将失败,因为我放置了条件。但事实并非如此
基本上如何使用redis命令在两个操作上放置类似于锁的东西:
检查hashentry是否存在
添加hashentry
坏消息,好消息,还有其他一切
坏消息
这不起作用,因为SE.Redis通过管道传输事务。这意味着调用ExecuteAsync()
时,所有事务命令都会同时发送到服务器。但是,先评估条件
tran.AddCondition(Condition.HashNotExists(“files”,fileId))代码>转换为:
WATCH "files"
HEXISTS "files" "1"
当调用ExecuteAsync()
来评估条件时,首先发送这两个命令。如果条件为true(hexist“files”1
=0),则发送其余的事务命令
这有效地确保了没有误报,因为如果文件
键在其间被修改(而SE.Redis正在评估条件),监视
将使事务失败
问题在于假阴性。例如,如果在SE.Redis评估条件时设置了哈希的另一个字段,则事务也将失败。WATCH“files”
就是这样做的
我通过调用ExecuteAsync()
时运行redis benchmark-c5hset files2 1
来测试这一点。条件已通过,但事务失败,尽管字段“1”不存在,因为字段“2”设置在两者之间
我在单独的redis cli窗口中使用该命令进行了验证。这对于排除未达到的期望值很方便,或者只是查看服务器的实际运行情况和运行时间
当您关心散列的某个字段时,WATCH
是没有帮助的,因为当触及其他字段时,它会产生误报
解决这个问题的一个方法是使用一个常规键(files:1
)
好消息
有一个命令可以完全按照您的要求执行:
这将完全简化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;
}
公共静态异步任务LockFileForEditAsync(int-fileId)
{
var database=ConnectionMultiplexer.Connect(CON).GetDatabase();
var setKey=await database.HashSetAsync(“files”,fileId,1,When.NotExists);
返回设置键;
}
请注意.note存在时的
参数。它会导致发送HSETNX“文件”“1”“1”
命令
其他一切…
您可以在这样的情况下使用,在这种情况下,您希望以原子方式完成一些条件操作,例如
似乎您正在尝试执行分布式锁定。请参阅其他您可能需要考虑的方面。请参阅,以获取关于此版本的精彩故事。显然它适用于我的变体。如果不存在,我不想只添加。我想添加并返回true(如果它不存在并且创建成功),如果它确实存在,则返回false,或者,它试图创建它,但同时有人创建了它。(事务)这正是HSETNX所做的:“仅当字段尚不存在时,才在“键到值”处存储的哈希中设置字段。若密钥不存在,则创建一个包含散列的新密钥。如果字段已存在,则此操作无效。“.Your last condition”它试图创建它,但有人同时创建了它。(事务)“如果使用一个命令,则不适用,Redis是单线程的,因此一个命令自动是原子的。如果字段是散列中的新字段并且设置了值,请参阅HSETNX:1的返回值。”。如果散列中已存在字段且未执行任何操作,则为0。我看不出这与您想要的不完全匹配:-)您指的是什么变体?在我的代码中,您可以看到每个用户首先尝试查看哈希是否存在,然后添加它。如果用户1检查哈希并成功,则在写入哈希u之前