资讯动态

.NET 10 WebAPI集成Redis分布式锁生产级实践:看门狗与可重入

发布时间:2026/9/8 13:29:11 来源:尧图企业网站定制
在业务开发里分布式锁是个绕不开但又很容易出错的点。尤其是 .NET 后端项目从单机部署走向多实例部署之后lock、Monitor这些进程内锁就完全失效了必须借助 Redis 这类外部组件来做跨进程的互斥控制。我近期在一个基于 .NET 10 的 WebAPI 项目中把 Redis 分布式锁从“能跑”升级到了“生产可用”期间踩过锁误删、锁超时导致并发穿透、重入时死锁等好几个坑。这篇文章我把完整的实践过程整理出来从 Redis 分布式锁的核心原理到基于 StackExchange.Redis 的封装实现再到看门狗续期、可重入锁、防误删锁这些生产级特性最后结合 AI 编程助手辅助完成代码生成与优化希望能给你一份可以直接落地的参考方案。全程代码基于 .NET 10 与 WebAPI 编写部分代码通过 AI 编程助手辅助生成后进行人工校验改造。无论你是刚开始接触分布式锁还是想把现有锁方案做一次升级都可以照着这篇文章的节奏走一遍。1. 为什么业务系统需要分布式锁1.1 单机锁在多实例场景下的失效问题先看一个最常见的场景用户下单时后端需要扣减库存。单机部署时我们可以直接用lock关键字private static readonly object LockObj new(); public void DeductStock(int count) { lock (LockObj) { // 检查库存、扣减库存 } }这个写法在单进程内没问题但一旦服务部署多个实例请求被负载均衡到不同机器上LockObj是每个进程各自持有的A 实例的锁根本拦不住 B 实例的并发请求。再比如 Java 里常见的synchronized包括各种进程内的线程池队列都存在相同的边界问题它们的锁范围是“进程级”而不是“集群级”。因此当业务从单体走向微服务、从单机部署走向多实例部署时必须引入“跨进程共享的锁”也就是分布式锁。1.2 分布式锁的选型对比目前主流分布式锁方案有几种基于关系数据库、基于 ZooKeeper、基于 Redis。方案优点缺点适用场景数据库唯一索引 /for update实现简单不需要额外组件性能偏低存在数据库连接占用和死锁风险低频并发场景ZooKeeper 临时顺序节点可靠性高有 Watch 机制自动清理需要额外维护 ZooKeeper 集群客户端较重对一致性要求极高的场景RedisSET NX EX性能高API 丰富社区方案成熟依赖 Redis 高可用需要自己处理锁续期、误删等问题高并发、低延迟业务场景对于大部分互联网业务来说Redis 分布式锁是性价比最高的选择这也是面试中高频出现“Redis 分布式锁”相关知识点的原因。1.3 一个合格的分布式锁需要满足的条件网上很多教程只讲“加锁、释放锁”但生产环境里一个合格的分布式锁至少要满足以下条件互斥性任意时刻只有一个客户端能持有锁。防死锁持有锁的客户端崩溃后锁能自动释放不能永久占用。防误删只能释放自己持有的锁不能把别人的锁删掉。可重入同一个客户端可以重复获取同一把锁。高性能加锁、解锁的耗时要足够低。支持续期业务执行时间超过锁过期时间后锁不应该被提前释放。这篇文章的完整代码就是围绕这几点展开的。2. 环境准备与项目初始化2.1 环境版本说明本文涉及的运行环境如下操作系统Windows 11 / 任意支持 .NET 的 Linux 发行版开发工具Visual Studio 2022 或 Rider.NET SDK.NET 10RedisRedis 7.x也可以通过 Docker 启动Redis 客户端StackExchange.RedisAI 编程助手Visual Studio 内置的 AI 辅助能力或 GitHub Copilot版本需要根据你的项目实际情况调整本文示例以 .NET 10 环境为主重点演示设计思路与代码实现框架版本升级后接口基本兼容。2.2 安装与启动 RedisRedis 官方不提供 Windows 版本但微软维护了 Windows 移植版本也可以直接使用 Docker。如果你是 Windows 用户不想装 Docker可以下载 Windows 版本 Redis 包解压后直接运行redis-server.exe redis.windows.conf如果你本机装了 Docker推荐用容器方式启动隔离性更好docker run -d --name redis-local \ -p 6379:6379 \ -e TZAsia/Shanghai \ redis:7启动后验证一下是否可用redis-cli ping如果返回PONG说明 Redis 已经正常运行。2.3 创建 .NET 10 WebAPI 项目使用命令行创建项目dotnet new webapi -n RedisLockDemo cd RedisLockDemo也可以直接在 Visual Studio 2022 里选择“ASP.NET Core Web API”项目模板创建目标框架选择 .NET 10。创建完成后为项目添加 StackExchange.Redis 包dotnet add package StackExchange.Redis需要说明的是StackExchange.Redis 是一个非常成熟的 .NET Redis 客户端库支持连接池、异步 API、Lua 脚本执行等能力是本文分布式锁实现的基础依赖。3. Redis 分布式锁核心原理拆解3.1 从 SETNX 开始Redis 分布式锁最早最核心的命令是SETNX全称是SET if Not eXistsSET lockKey holderId NX EX 30000NX只有当 key 不存在时才设置成功。EX设置过期时间单位秒。也可以用PX设置毫秒。holderId锁持有者的唯一标识通常用Guid.NewGuid().ToString()生成。这个命令是原子性的多个客户端同时执行时只有一个能成功这就是互斥性的来源。在 StackExchange.Redis 中对应代码如下var isSuccess await db.StringSetAsync(lockKey, holderId, TimeSpan.FromMilliseconds(30000), When.NotExists);When.NotExists就是NX语义。3.2 为什么释放锁必须使用 Lua 脚本很多人初版实现的释放锁是这样写的if (await db.StringGetAsync(lockKey) holderId) { await db.KeyDeleteAsync(lockKey); }这段代码有两个操作判断持有者、删除 key。它们不是原子操作。如果判断通过后、删除之前锁刚好过期了另一个客户端抢到了锁那么当前客户端执行KeyDeleteAsync就会把别人的锁误删。正确的做法是使用 Lua 脚本让“判断持有者 删除 key”在 Redis 服务端原子执行if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) end return 0StackExchange.Redis 中通过ScriptEvaluateAsync执行private static readonly LuaScript ReleaseLockScript LuaScript.Prepare( if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) end return 0 );3.3 看门狗续期的基本思路锁过期时间设短了业务还没执行完锁就没了设长了客户端崩溃后锁要很久才能自动释放。这个矛盾催生了“看门狗”方案。看门狗的核心逻辑是客户端成功拿到锁后启动一个后台任务每隔一段时间通常是过期时间的三分之一自动为锁续期让锁的过期时间始终滚动往后推。业务执行完成后主动释放锁并停止续期。这样即使业务执行时间超过预期锁也不会提前失效一旦持有锁的进程崩溃续期任务随之中断锁最终会过期释放不会造成死锁。3.4 可重入锁的实现思路可重入的意思是同一个线程或同一个客户端在已经持有锁的情况下可以再次成功获取同一把锁并且释放时需要把重入次数减到 0 才真正删除锁。基于 Redis 实现可重入锁通常用 Hash 结构字段名锁的 keyHash 字段持有者标识Hash 值重入计数加锁时的 Lua 脚本逻辑-- 如果锁不存在直接创建并设置重入次数为 1 -- 如果锁存在且持有者是当前客户端重入次数 1 -- 否则返回失败释放锁时的 Lua 脚本逻辑-- 重入次数 -1 -- 如果重入次数大于 0刷新过期时间后返回 -- 如果重入次数等于 0删除锁因为 Hash 结构和 Lua 脚本的配合可重入锁、防误删锁、锁续期这三个能力可以统一实现。4. 使用 AI 编程助手快速搭建项目骨架4.1 AI 在开发流程中的位置在正式写代码前先聊一下 AI 编程助手在本文项目中的实际作用。现在 .NET 生态里的 AI 辅助工具已经很成熟Visual Studio 2022 也能直接接入 AI 编程助手。无论是生成 DTO、WebAPI Controller还是写出 Redis 工具类的初版代码AI 都能显著提升编码效率。但这里有一个重要原则AI 生成的代码不能直接上线。分布式锁这类基础组件涉及并发和原子性AI 生成的代码往往缺少对边界条件的处理。我的做法是让 AI 负责“脚手架代码”自己负责“核心逻辑与校验”。4.2 用 AI 生成项目骨架我把下面的需求描述给 AI 编程助手帮我生成一个 .NET 10 WebAPI 项目文件结构包含订单 Controller、库存 Service以及一个 Redis 分布式锁工具类的空壳。Controller 提供一个创建订单的 POST 接口Service 中预留扣减库存方法。AI 很快生成了一版项目结构和 Controller 骨架// 文件路径Controllers/OrderController.cs [ApiController] [Route(api/[controller])] public class OrderController : ControllerBase { private readonly IOrderService _orderService; public OrderController(IOrderService orderService) { _orderService orderService; } [HttpPost(create)] public async TaskIActionResult CreateOrder(OrderCreateRequest request) { var result await _orderService.CreateOrderAsync(request); return Ok(result); } }这个骨架可以直接用但它只给了接口壳子。接下来分布式锁的核心代码就必须要靠人工设计、人工校验尤其是 Lua 脚本不能完全信任 AI 生成的版本。4.3 AI 辅助代码审查在实际开发中我还会把写完的 RedisLockService 核心代码发给 AI让它做一次 code review。AI 能找出一些明显的逻辑问题比如续期任务没有停止导致内存泄漏释放锁时没有处理续期任务锁的 key 没有设置命名空间容易与业务 key 冲突。这些问题我都会再逐条手工确认后修复。AI 更像一个结对编程伙伴决策权始终在开发者这边。5. 完整实战WebAPI Redis 分布式锁生产级方案5.1 项目结构规划为了让代码结构清晰我在原有项目里增加了如下目录RedisLockDemo/ ├── Controllers/ │ └── OrderController.cs ├── Services/ │ ├── IOrderService.cs │ └── OrderService.cs ├── Infrastructure/ │ ├── RedisLockOptions.cs │ ├── RedisLockService.cs │ └── IRedisLockService.cs ├── Dtos/ │ └── OrderCreateRequest.cs ├── appsettings.json └── Program.csInfrastructure目录专门放分布式锁相关代码后续可以独立成类库供其他项目复用。5.2 注册 Redis 连接与配置在appsettings.json中增加 Redis 连接配置{ Redis: { ConnectionString: localhost:6379,password,defaultDatabase0, InstanceName: RedisLockDemo: }, RedisLock: { DefaultExpirySeconds: 30, WatchdogIntervalSeconds: 10 } }然后创建 RedisLockOptions// 文件路径Infrastructure/RedisLockOptions.cs public class RedisLockOptions { public int DefaultExpirySeconds { get; set; } 30; public int WatchdogIntervalSeconds { get; set; } 10; }在 Program.cs 中注册 Redisusing StackExchange.Redis; // 读取配置 var redisConnectionString builder.Configuration[Redis:ConnectionString]; var instanceName builder.Configuration[Redis:InstanceName] ?? RedisLockDemo:; // 创建 ConnectionMultiplexer var redis await ConnectionMultiplexer.ConnectAsync(redisConnectionString); // 注册为单例 builder.Services.AddSingletonIConnectionMultiplexer(redis); // 注册分布式锁服务 builder.Services.ConfigureRedisLockOptions( builder.Configuration.GetSection(RedisLock)); builder.Services.AddSingletonIRedisLockService, RedisLockService();5.3 定义锁服务接口// 文件路径Infrastructure/IRedisLockService.cs public interface IRedisLockService { /// summary /// 尝试获取锁如果获取失败返回 null。 /// /summary TaskRedisLock AcquireLockAsync(string lockKey, TimeSpan? expiry null, TimeSpan? retryDelay null, int retryCount 0, CancellationToken cancellationToken default); /// summary /// 释放锁。 /// /summary Task ReleaseLockAsync(RedisLock redisLock); } /// summary /// 锁对象封装持有者标识与看门狗资源。 /// /summary public class RedisLock : IAsyncDisposable { public string LockKey { get; init; } string.Empty; public string HolderId { get; init; } string.Empty; public TimeSpan Expiry { get; init; } internal CancellationTokenSource? WatchdogCts { get; set; } public async ValueTask DisposeAsync() { // 通过容器释放时自动解锁 } }5.4 实现核心锁服务下面这一段是整个项目的核心代码较长我分块讲。5.4.1 构造函数与 Lua 脚本定义// 文件路径Infrastructure/RedisLockService.cs using Microsoft.Extensions.Options; using StackExchange.Redis; public class RedisLockService : IRedisLockService { private readonly IDatabase _db; private readonly RedisLockOptions _options; private readonly string _instanceName; public RedisLockService(IConnectionMultiplexer redis, IOptionsRedisLockOptions options) { _db redis.GetDatabase(); _options options.Value; _instanceName RedisLockDemo:; } // 加锁锁不存在时直接创建锁存在且持有者是当前客户端时重入次数 1 private static readonly string AcquireLockScript if redis.call(exists, KEYS[1]) 0 then redis.call(hset, KEYS[1], ARGV[1], 1) redis.call(pexpire, KEYS[1], ARGV[2]) return 1 end if redis.call(hexists, KEYS[1], ARGV[1]) 1 then redis.call(hincrby, KEYS[1], ARGV[1], 1) redis.call(pexpire, KEYS[1], ARGV[2]) return 1 end return 0 ; // 释放锁重入次数减到 0 才删除 key private static readonly string ReleaseLockScript if redis.call(hexists, KEYS[1], ARGV[1]) 0 then return 0 end local count redis.call(hincrby, KEYS[1], ARGV[1], -1) if count 0 then return redis.call(pexpire, KEYS[1], ARGV[2]) end redis.call(del, KEYS[1]) return 1 ; // 看门狗续期脚本 private static readonly string RenewLockScript if redis.call(hexists, KEYS[1], ARGV[1]) 1 then return redis.call(pexpire, KEYS[1], ARGV[2]) end return 0 ; }这里的 Lua 脚本解决了前面提到的三个问题可重入通过hincrby增加重入计数。防误删释放时校验hexists持有者。续期只有持有者本人才能续期。5.4.2 获取锁方法public async TaskRedisLock? AcquireLockAsync(string lockKey, TimeSpan? expiry null, TimeSpan? retryDelay null, int retryCount 0, CancellationToken cancellationToken default) { if (string.IsNullOrWhiteSpace(lockKey)) { throw new ArgumentException(lockKey 不能为空, nameof(lockKey)); } var fullKey _instanceName lockKey; var holderId Guid.NewGuid().ToString(N); var expireMs (long)(expiry ?? TimeSpan.FromSeconds(_options.DefaultExpirySeconds)).TotalMilliseconds; for (var i 0; i retryCount; i) { cancellationToken.ThrowIfCancellationRequested(); var result await _db.ScriptEvaluateAsync(AcquireLockScript, new RedisKey[] { fullKey }, new RedisValue[] { holderId, expireMs }); if ((int)result 1) { var redisLock new RedisLock { LockKey fullKey, HolderId holderId, Expiry TimeSpan.FromMilliseconds(expireMs) }; // 启动看门狗自动续期 StartWatchdog(redisLock); return redisLock; } if (retryDelay.HasValue retryDelay.Value TimeSpan.Zero) { await Task.Delay(retryDelay.Value, cancellationToken); } } return null; }这段代码里几个参数需要说明retryDelay获取锁失败后等待多久重试。retryCount最大重试次数传 0 表示只尝试一次。expiry锁的过期时间默认取配置值。看门狗启动逻辑如下private void StartWatchdog(RedisLock redisLock) { var cts new CancellationTokenSource(); redisLock.WatchdogCts cts; var interval TimeSpan.FromSeconds(_options.WatchdogIntervalSeconds); var timer new Timer(async _ { try { if (cts.IsCancellationRequested) { return; } var renewMs (long)redisLock.Expiry.TotalMilliseconds; var result await _db.ScriptEvaluateAsync(RenewLockScript, new RedisKey[] { redisLock.LockKey }, new RedisValue[] { redisLock.HolderId, renewMs }); // 续期失败说明锁已经丢失停止续期 if ((int)result 0) { cts.Cancel(); } } catch (Exception ex) { // 这里记录日志后由上层感知锁丢失 Console.WriteLine($看门狗续期失败: {ex.Message}); cts.Cancel(); } }, null, interval, interval); // 将 timer 保存在锁对象中释放锁时停止 redisLock.WatchdogTimer timer; }5.4.3 释放锁方法public async Task ReleaseLockAsync(RedisLock redisLock) { if (redisLock null) { return; } // 先停止看门狗 redisLock.WatchdogTimer?.Dispose(); redisLock.WatchdogCts?.Cancel(); var expireMs (long)redisLock.Expiry.TotalMilliseconds; await _db.ScriptEvaluateAsync(ReleaseLockScript, new RedisKey[] { redisLock.LockKey }, new RedisValue[] { redisLock.HolderId, expireMs }); }这里必须先停止续期任务再释放锁否则可能存在竞态释放锁命令还没执行看门狗又续期了一次导致锁没有被正常删除。5.4.4 完整 RedisLock 对象定义为了保管看门狗 Timer我在前面的 RedisLock 类中补充一个属性public class RedisLock : IAsyncDisposable { public string LockKey { get; init; } string.Empty; public string HolderId { get; init; } string.Empty; public TimeSpan Expiry { get; init; } internal CancellationTokenSource? WatchdogCts { get; set; } internal Timer? WatchdogTimer { get; set; } public async ValueTask DisposeAsync() { // 使用 await 时依赖注入容器会调用这里 await Task.CompletedTask; } }5.5 编写业务 Service接下来我们模拟一个库存扣减场景。为了保证演示效果数据库操作部分用内存字段代替重点展示分布式锁的使用方式。// 文件路径Services/OrderService.cs public class OrderService : IOrderService { private readonly IRedisLockService _redisLockService; private readonly ILoggerOrderService _logger; // 模拟库存生产环境对应数据库字段 private static int _stock 10; public OrderService(IRedisLockService redisLockService, ILoggerOrderService logger) { _redisLockService redisLockService; _logger logger; } public async Taskbool CreateOrderAsync(OrderCreateRequest request) { var lockKey $stock:deduct:{request.ProductId}; await using var redisLock await _redisLockService.AcquireLockAsync( lockKey, expiry: TimeSpan.FromSeconds(30), retryDelay: TimeSpan.FromMilliseconds(200), retryCount: 5); if (redisLock null) { _logger.LogWarning(获取分布式锁失败订单创建被拒绝。ProductId{ProductId}, request.ProductId); return false; } try { if (_stock 0) { _logger.LogInformation(库存不足ProductId{ProductId}, request.ProductId); return false; } // 模拟耗时业务方便演示看门狗续期效果 await Task.Delay(TimeSpan.FromSeconds(5)); _stock--; _logger.LogInformation(扣减库存成功剩余库存{Stock}, _stock); return true; } finally { await _redisLockService.ReleaseLockAsync(redisLock); } } }这个用法有几点好处业务执行期间看门狗自动续期不担心锁提前过期。无论业务成功还是异常finally中都会释放锁。获取失败时可以根据业务需要返回错误或走排队策略。5.6 Controller 与 DTO// 文件路径Dtos/OrderCreateRequest.cs public class OrderCreateRequest { public string ProductId { get; set; } string.Empty; public int Count { get; set; } 1; }// 文件路径Controllers/OrderController.cs [ApiController] [Route(api/[controller])] public class OrderController : ControllerBase { private readonly IOrderService _orderService; public OrderController(IOrderService orderService) { _orderService orderService; } [HttpPost(create)] public async TaskIActionResult CreateOrder(OrderCreateRequest request) { if (string.IsNullOrWhiteSpace(request.ProductId)) { return BadRequest(ProductId 不能为空); } var success await _orderService.CreateOrderAsync(request); return success ? Ok(new { message 下单成功 }) : Conflict(new { message 系统繁忙请稍后重试 }); } }5.7 运行与验证启动项目后使用 Swagger 或 Postman 调用POST http://localhost:5000/api/order/create Content-Type: application/json { productId: P1001, count: 1 }为了验证并发控制可以同时开启多个请求。打开 Visual Studio 的终端使用 curl 并发调用for i in {1..10}; do curl -X POST http://localhost:5000/api/order/create \ -H Content-Type: application/json \ -d {\productId\:\P1001\,\count\:1} done wait同时打开 Redis Desktop Manager 或 another redis desktop manager 连接本地 Redis观察RedisLockDemo:stock:deduct:P1001这个 key 在业务执行期间会反复被续期业务结束后被删除。预期输出有几个请求返回“下单成功”库存扣减到 0 后后面的请求返回“系统繁忙”。Redis 中不会出现永久残留的锁 key。6. 分布式锁测试与边界验证6.1 单测覆盖关键场景分布式锁是基础组件建议为核心逻辑编写单元测试或集成测试。至少覆盖以下场景测试场景预期结果两个客户端竞争同一把锁只有一个客户端获取成功持有者释放自己的锁锁被删除非持有者尝试释放锁锁不会被删除同一客户端重复获取同一把锁重入计数增加获取成功锁过期后没有续期锁自动释放看门狗续期后过期时间刷新锁的 TTL 被延长业务执行超过锁过期时间锁仍被持有不会提前释放6.2 并行测试代码示例如果你的项目里已经引入了 xUnit可以写一个简单的并发测试[Fact] public async Task AcquireLock_Should_Only_Allow_One_Holder() { var lockKey test:parallel-lock; var tasks Enumerable.Range(0, 10) .Select(_ _lockService.AcquireLockAsync(lockKey, expiry: TimeSpan.FromSeconds(5), retryDelay: TimeSpan.FromMilliseconds(10), retryCount: 5)); var locks await Task.WhenAll(tasks); var successCount locks.Count(l l ! null); Assert.Equal(1, successCount); foreach (var redisLock in locks.Where(l l ! null)) { await _lockService.ReleaseLockAsync(redisLock!); } }7. 常见问题与排查思路7.1 常见报错与解决方案问题现象常见原因解决思路加锁一直返回失败Redis 连接不可用锁 key 被其他业务占用检查 Redis 连接字符串确认锁 key 命名唯一锁没有自动释放Lua 脚本执行异常看门狗未启动在 Redis CLI 中手动执行 Lua 脚本排查业务执行时锁被提前释放没有看门狗续期续期任务被取消确认StartWatchdog被调用检查定时器日志释放锁时报WRONGTYPE锁 key 被其他业务覆盖类型不再是 Hash为锁 key 增加统一前缀避免与业务 key 冲突可重入锁变成死锁同一线程持锁后等待另一把锁且该锁也在等待当前锁重入逻辑需要统计线程维度建议减小锁粒度7.2 排查清单如果线上出现和分布式锁相关的并发问题我一般按下面顺序排查确认 Redis 连接是否正常有没有频繁断线重连。确认锁 key 是否有统一前缀避免和缓存 key 冲突。确认看门狗有没有启动打日志看 Timer 是否触发。确认业务代码是否在finally中释放锁。确认 Redis 是否开启了持久化或集群模式主从切换可能导致锁丢失。抓取 Redis 慢日志确认 Lua 脚本执行耗时是否异常。8. 生产环境最佳实践8.1 锁 key 的命名规范锁的 key 必须有清晰的前缀比如业务名:锁场景:业务标识。我习惯统一在服务层拼前缀调用方只传业务核心 key底层自动加RedisLockDemo:前缀。示例RedisLockDemo:stock:deduct:P1001 RedisLockDemo:order:cancel:O20250101001 RedisLockDemo:user:point:U100868.2 获取锁失败的业务策略获取分布式锁失败时不要一律直接报错。可以根据业务场景选择不同策略快速失败适合对响应时间敏感的接口直接提示“系统繁忙”。排队重试适合后台任务、定时任务可以等待一段时间后重试。降级处理如果分布式锁组件不可用可以降级为进程内锁或直接拒绝请求避免资源耗尽。8.3 锁粒度控制锁粒度直接决定系统并发能力。比如扣减库存如果每件商品都锁同一个 key那么不同 SKU 的请求也会互相阻塞。正确做法是使用带业务标识的细粒度锁stock:deduct:{productId}如果业务场景要求同一用户的操作串行则可以使用userId作为锁维度user:operate:{userId}8.4 日志与监控分布式锁相关日志建议单独输出至少包含锁 key持有者 ID获取耗时持有时间释放结果看门狗续期次数方便出问题时快速定位。另外建议在 Redis 侧配置慢日志监控关注 Lua 脚本的执行耗时。9. 结语与下一步这篇文章我们完整走了一遍 .NET 10 WebAPI 集成 Redis 分布式锁的实战流程核心代码不只是停留在SETNXDEL的入门写法而是实现了锁超时自动释放、看门狗续期、可重入锁和防误删锁这些生产级特性。同时我也演示了如何用 AI 编程助手辅助生成项目骨架、用人工校验锁定核心 Lua 脚本逻辑这是目前 .NET 后端开发中比较高效的工作方式。你可以先照着本文把项目跑通再尝试把这些能力应用到你自己的高并发业务场景里。接下来可以继续研究的内容包括RedLock 红锁算法的局限性与适用条件、Redis 集群模式下分布式锁的一致性边界、以及基于数据库事务与 Redis 锁配合使用的最终一致性方案。把这些基础打牢以后无论是自己完成架构设计还是在面试中面对“Redis 分布式锁”相关追问你都会更有底气。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价