导读:本期聚焦于小伙伴创作的《C#如何在高并发下保证Redis操作的原子性 Lua脚本和事务》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#如何在高并发下保证Redis操作的原子性 Lua脚本和事务》有用,将其分享出去将是对创作者最好的鼓励。

在高并发的业务场景中,Redis作为常用的缓存和分布式存储组件,经常需要执行多个关联操作,比如先查询库存再扣减库存,或者先获取用户积分再更新积分。如果这些操作不保证原子性,多个请求同时执行时就会出现数据竞争,导致最终结果不符合预期。针对C#开发场景,Redis提供了事务和Lua脚本两种方案来保证操作的原子性,下面分别介绍两者的实现方式和特点。

C#如何在高并发下保证Redis操作的原子性 Lua脚本和事务

Redis原子性的基本概念

Redis的原子性指的是一个或者多个操作在执行过程中不会被其他命令打断,要么全部执行成功,要么全部不执行。需要注意的是,Redis的单条命令本身就是原子性的,但是多条命令组合起来如果不做特殊处理,就不具备原子性。

使用Redis事务保证原子性

Redis事务的基本原理

Redis事务通过MULTI、EXEC、DISCARD三个命令实现,MULTI标记事务开始,之后的命令会进入队列暂存,EXEC命令触发所有队列中的命令按顺序执行,DISCARD可以取消事务。事务中的所有命令会作为一个整体执行,中间不会被其他客户端的命令插入。

C#中使用StackExchange.Redis实现事务

StackExchange.Redis是C#中最常用的Redis客户端库,下面是使用事务实现库存扣减的示例代码:

using StackExchange.Redis;
using System;

class RedisTransactionDemo
{
    static void Main()
    {
        // 创建Redis连接
        ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
        IDatabase db = redis.GetDatabase();
        
        // 初始化库存为100
        db.StringSet("product_stock", 100);
        
        // 开启事务
        ITransaction transaction = db.CreateTransaction();
        
        // 事务中的命令会进入队列,不会立即执行
        // 先获取当前库存
        var stockTask = transaction.StringGetAsync("product_stock");
        // 扣减库存,这里先模拟获取库存后的逻辑,实际事务中无法依赖前一个命令的结果
        transaction.AddCondition(Condition.KeyExists("product_stock"));
        var decrementTask = transaction.StringDecrementAsync("product_stock");
        
        // 提交事务,执行所有队列中的命令
        bool committed = transaction.Execute();
        
        if (committed)
        {
            Console.WriteLine("事务执行成功");
            // 注意:异步任务的结果需要在Execute之后获取
            var stock = stockTask.Result;
            Console.WriteLine($"当前库存:{stock}");
        }
        else
        {
            Console.WriteLine("事务执行失败,可能是条件不满足");
        }
        
        redis.Close();
    }
}

Redis事务的局限性

  • Redis事务不支持回滚,如果队列中的某个命令执行失败,其他命令仍然会继续执行。
  • 事务中的命令在执行前不会返回结果,无法在事务内根据前一个命令的结果决定后续操作,比如无法先判断库存大于0再扣减。
  • 事务通过乐观锁实现,使用WATCH命令监视的键如果被修改,事务会执行失败,需要重试。

使用Lua脚本保证原子性

Lua脚本的基本原理

Redis支持执行Lua脚本,脚本会被当作一个整体执行,执行期间不会被其他命令打断,天然具备原子性。同时Lua脚本内部可以编写逻辑判断,根据中间结果决定后续操作,弥补了事务的不足。

C#中执行Lua脚本实现库存扣减

下面是使用Lua脚本实现先判断库存再扣减的示例代码,逻辑更灵活:

-- Lua脚本:如果库存大于0则扣减1,返回1表示成功,0表示库存不足
local stock = tonumber(redis.call('get', KEYS[1]))
if stock and stock > 0 then
    redis.call('decr', KEYS[1])
    return 1
else
    return 0
end

C#中调用该Lua脚本的代码:

using StackExchange.Redis;
using System;

class RedisLuaDemo
{
    static void Main()
    {
        ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
        IDatabase db = redis.GetDatabase();
        
        // 初始化库存
        db.StringSet("product_stock", 100);
        
        // 定义Lua脚本
        string luaScript = @"
local stock = tonumber(redis.call('get', KEYS[1]))
if stock and stock > 0 then
    redis.call('decr', KEYS[1])
    return 1
else
    return 0
end
";
        
        // 加载脚本,避免每次执行都传输完整脚本
        LoadedLuaScript loadedScript = LuaScript.Prepare(luaScript);
        
        // 执行脚本,传入键名参数
        RedisResult result = db.ScriptEvaluate(loadedScript, new RedisKey[] { "product_stock" });
        
        if (result.ToString() == "1")
        {
            Console.WriteLine("库存扣减成功");
            var currentStock = db.StringGet("product_stock");
            Console.WriteLine($"当前库存:{currentStock}");
        }
        else
        {
            Console.WriteLine("库存不足,扣减失败");
        }
        
        redis.Close();
    }
}

Lua脚本的优势

  • 原子性更强,脚本执行过程中不会被其他命令打断,也不需要额外的乐观锁机制。
  • 支持逻辑判断,可以在脚本内部根据中间结果决定后续操作,适合复杂的业务逻辑。
  • 减少网络往返,多个操作放在一个脚本中执行,只需要一次网络请求,性能更好。

两种方案的对比与选型建议

对比维度Redis事务Lua脚本
原子性保证队列中的命令整体执行,无回滚脚本整体执行,天然原子性
逻辑灵活性无法依赖前序命令结果支持条件判断、循环等逻辑
性能多次网络请求(MULTI、命令、EXEC)一次网络请求
适用场景简单的多条命令批量执行,无逻辑依赖复杂逻辑,需要条件判断的操作

在高并发场景下,如果操作逻辑简单,不需要根据前序结果判断,可以选择Redis事务;如果操作需要条件判断,或者逻辑相对复杂,优先选择Lua脚本,既能保证原子性,又能减少网络开销,性能更优。

C#Redis高并发原子性Lua脚本修改时间:2026-06-16 17:09:20

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。