.net-core - 如何使用 StackExchange.Redis 使多个操作原子化

标签 .net-core redis transactions locking stackexchange.redis

你好我有以下问题:

我有一个包含字符串的哈希。这个哈希将被多个用户查询。

当用户带有 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 命令在两个操作上放置类似锁定的东西:
  • 检查hashentry是否存在
  • 添加哈希条目
  • 最佳答案

    坏消息,好消息,以及其他一切......

    坏消息

    这不起作用,因为 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/

    相关文章:

    c# - 急切加载导航属性时,Entity Core 查询仅返回一项

    node.js - 在 Redis 中设置键前缀的正确方法是什么?

    mysql - Docker 故障转移 : Redis, MySQL 和 Nginx

    mysql - 从关系型数据库到Redis,N对N关系

    java - 如何在 Spring Boot 上以相同的方法回滚 2 个事务

    TransactionScope 提升到 MSDTC 虽然我使用相同的连接字符串

    c# - 在 CoreFx 上使用 System.Data.SqlClient 时出现 UDT 不支持的 token 问题

    c# - .NET 核心日期时间?如果为 null,则字段 ModelState 无效

    c# - 如何使用 JSONConvert 将变量 Result 转换为对象?

    java - Hibernate 中的并发事务