ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

.NET 10 WebAPI + Redis 分布式锁:解决高并发超卖与重复请求

.NET 10 WebAPI + Redis 分布式锁:解决高并发超卖与重复请求 .NET 10 正式版发布后很多后端团队开始把 WebAPI 集群化改造提上日程。单机阶段写一个lock(obj)就能解决的并发冲突一旦部署多个实例、前面再挂上负载均衡锁就直接失效。库存被扣成负数、同一笔订单被创建两次、用户重复点击提交按钮出现多条订单——这些问题不会因为你换语言而消失。本文围绕一套可落地的架构.NET 10 WebAPI Redis 实现分布式锁重点解决集群并发冲突、超卖和重复请求问题并给出从项目创建到接口压测的完整教学路径。这套方案的核心特点可以概括为不依赖具体部署环境Windows/Linux 都能跑不追求过度设计锁的抽象只有 Acquire、Release、Renew 三个方法不改业务主流程只在关键临界区加锁附带幂等防重设计双击提交、网关重试都能兜住。全程会使用 Redis 7 StackExchange.Redis 作为底层依赖代码尽量保持短、可读、可直接进入业务改造成。文章包含四部分实操一是 .NET 10 WebAPI 项目创建与 Redis 环境准备二是基于 StackExchange.Redis 手写分布式锁含安全释放和看门狗续期三是把锁应用到库存扣减和订单防重接口四是接口压测和常见问题排查。全程提供可复制的命令和代码不是只讲概念。适合正在做 .NET WebAPI 高并发改造的中高级开发也适合准备分布式锁面试题的同学。如果你是刚学 .NET 的初学者建议先把 Redis 基本命令和 WebAPI 依赖注入机制过一遍再上手。1. 核心能力速览能力项说明技术栈.NET 10 WebAPI Redis 7 StackExchange.Redis目标问题集群并发下的超卖、重复请求、库存扣减冲突核心机制Redis 分布式锁SET NX EX 原子加锁 Lua 安全释放 看门狗续期运行平台Windows / Linux / macOS 开发环境生产建议 Linux 容器GPU 依赖不需要硬性门槛本机已安装 .NET 10 SDKRedis 服务可达部署方式dotnet 命令行发布或打包 Docker 镜像Redis 独立部署接口能力WebAPI 返回 JSON支持 Swagger、curl、Postman、Vue 前端联调批量任务支持 ab / JMeter 并发压测业务侧可配合消息队列做削峰适合场景订单、库存、优惠券、积分等写多读少的高并发业务先泼一盆冷水如果只是写一个 CRUD 接口这套东西属于过度设计。但如果你正在做集群化改造订单和库存接口要扛住几十上百的瞬时并发那分布式锁就是绕不开的基础设施。下面直接进入部署和编码。2. 适用场景与业务边界2.1 适合解决的业务问题第一类问题是“库存/优惠券/积分扣减”。典型流程是先查库存再判断是否足够最后扣减。单机环境下这段逻辑用lock或数据库行锁就能控制到了多实例部署不同实例上的请求同时操作同一商品时Redis 分布式锁可以提供互斥入口。第二类问题是“订单幂等防重”。用户双击提交、前端重试、API 网关超时重发都会让同一个业务请求到达后端多次。治理思路不是让后端“别接到重复请求”而是让后端识别这是同一个请求并且只处理第一次。Redis 的SET NX EX天然适合做这个幂等标记。第三类问题是“分布式定时任务互斥”。多实例部署后定时任务会在每个实例上都触发一次如果没有互斥机制数据会被重复处理。用一个 Redis 锁谁拿到锁谁执行能避免大部分重复任务问题。2.2 不适合什么场景如果并发量本身不高或者业务是纯查询、读多写少完全没有必要引入分布式锁。Redis 锁引入之后还多一个依赖点Redis 抖动、网络超时、锁过期时间设置不当反而会拖垮业务。还要提醒一点Redis 分布式锁解决的是“多实例互斥”它不能替代数据库的原子更新。库存扣减这类操作即使有了分布式锁数据库侧仍然要写UPDATE ... SET stock stock - quantity WHERE id id AND stock quantity。锁负责让同一时刻只有一个实例进入临界区数据库条件更新负责兜底防超卖两者配合而不是二选一。2.3 合规与安全边界涉及订单、用户 ID、商品等真实业务数据时需要注意个人信息保护和数据合规。生产环境的 Redis 必须开启密码认证并做好网络隔离不能把 6379 端口直接暴露到公网。锁使用的 key 命名要规范避免和缓存 key 混在一起。后续示例代码中的 user id、订单号都是测试数据落地到生产要按实际业务字段调整。3. .NET 10 WebAPI 环境准备与项目创建3.1 安装 .NET 10 SDK.NET 10 是微软在 2025 年 11 月发布的长期支持版本。开发机需要安装 .NET 10 SDK。如果使用 Visual Studio 2022需要将版本更新到支持 .NET 10 的最新版本。很多同学卡在这一步VS 装的是旧版本打开项目后提示模板不可用。我的建议是不管用不用 VS先把dotnet --version跑通确保命令行工具可用。dotnet --version能正常输出版本号再继续下一步。3.2 创建 WebAPI 项目用命令行创建带 Controller 的 WebAPI 项目比在 VS 里点模板更不容易踩雷dotnet new webapi -n HighConcurrencyDemo --use-controllers cd HighConcurrencyDemo如果模板提示参数不存在执行dotnet new webapi --help查看当前 SDK 支持的是--use-controllers还是-controllers按实际参数调整即可。项目创建后添加 Redis 客户端依赖dotnet add package StackExchange.Redis3.3 注册 Redis 连接打开Program.cs注册IConnectionMultiplexer单例。这里不使用每请求创建连接的方式ConnectionMultiplexer本身就是为多路复用设计的一个全局实例足够using StackExchange.Redis; var builder WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var redisConnectionString builder.Configuration.GetConnectionString(Redis); builder.Services.AddSingletonIConnectionMultiplexer( ConnectionMultiplexer.Connect(redisConnectionString)); var app builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseAuthorization(); app.MapControllers(); app.Run();在appsettings.json里配置 Redis 连接字符串{ ConnectionStrings: { Redis: 127.0.0.1:6379,passwordyourpassword,abortConnectfalse,connectTimeout3000 }, Logging: { LogLevel: { Default: Information, Microsoft.AspNetCore: Warning } }, AllowedHosts: * }abortConnectfalse的作用是Redis 暂时不可用时应用能正常启动不会直接把整个 WebAPI 进程拖死。但生产环境仍然要先确保 Redis 可用再启动业务服务。4. Redis 本地安装与连接配置4.1 Windows 下怎么装 RedisRedis 官方并不直接支持 Windows。在 Windows 开发环境下比较推荐的方案有三个Docker Desktop 运行 Redis 容器最省事WSL2 里安装 Linux 版 Redis使用 Memurai 等 Windows 兼容实现。不推荐在 Windows 下从源码编译 Redis编译依赖多后续升级也麻烦。开发阶段直接用 Docker 最合适。4.2 Docker 启动 Redis 7使用 Redis 7 镜像启动一个带密码、开启 AOF 持久化的实例docker run -d \ --name redis-highconcurrency \ -p 6379:6379 \ -v redis-highconcurrency-data:/data \ redis:7-alpine \ redis-server --appendonly yes --requirepass yourpassword端口使用默认的 6379。生产环境建议把密码放到配置文件里不要裸写在命令行命令行里的启动参数容易被进程列表看到。4.3 验证 Redis 连通性用redis-cli验证连接redis-cli -h 127.0.0.1 -p 6379 -a yourpassword ping返回PONG说明连接正常。在进入代码之前先用 Redis 命令手工验证一下锁的原子操作理解 NX 和 EX 的组合含义redis-cli -h 127.0.0.1 -p 6379 -a yourpassword SET inventory:lock:SKU-1001 request-token NX EX 30 GET inventory:lock:SKU-1001 DEL inventory:lock:SKU-1001SET key value NX EX 30的含义是只有当 key 不存在时才写入并且设置 30 秒过期时间。第一个客户端执行成功后第二个客户端再执行同样命令会失败这就是分布式锁的最小语义。实际开发过程中可以用 Another Redis Desktop Manager 这类可视化工具查看 key 的变化检查锁是否异常残留、TTL 是否符合预期排查效率会高很多。5. Redis 分布式锁核心实现原子加锁、安全释放与看门狗5.1 为什么单机 lock 不可用多实例部署后一个请求被负载均衡分配到实例 A另一个请求被分配到实例 B。lock(obj)锁的是进程内对象实例 A 的锁对实例 B 没有任何约束力。分布式锁的本质是把“互斥状态”放到所有实例都能访问的公共存储上Redis 就是这样一个公共存储。5.2 锁的抽象接口先把锁抽象成三个方法这样可以很自然地把 Redis 实现和业务逻辑解耦public interface IDistributedLock { Taskbool AcquireAsync(string lockKey, string requestId, TimeSpan expiry); Taskbool ReleaseAsync(string lockKey, string requestId); Taskbool RenewAsync(string lockKey, string requestId, TimeSpan expiry); }requestId必须由调用方每次生成一个唯一值通常用Guid.NewGuid().ToString(N)。它的作用是标识“这把锁是谁加的”释放锁时只有持有者才能释放避免误删别人的锁。5.3 Redis 实现加锁、释放、续期下面是基于 StackExchange.Redis 的完整实现using StackExchange.Redis; public class RedisDistributedLock : IDistributedLock { private readonly IConnectionMultiplexer _redis; public RedisDistributedLock(IConnectionMultiplexer redis) { _redis redis; } public async Taskbool AcquireAsync(string lockKey, string requestId, TimeSpan expiry) { var db _redis.GetDatabase(); return await db.StringSetAsync(lockKey, requestId, expiry, When.NotExists); } public async Taskbool ReleaseAsync(string lockKey, string requestId) { var db _redis.GetDatabase(); var script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end ; var result await db.ScriptEvaluateAsync( script, new RedisKey[] { lockKey }, new RedisValue[] { requestId }); return (long)result! 1; } public async Taskbool RenewAsync(string lockKey, string requestId, TimeSpan expiry) { var db _redis.GetDatabase(); var script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(pexpire, KEYS[1], ARGV[2]) else return 0 end ; var result await db.ScriptEvaluateAsync( script, new RedisKey[] { lockKey }, new RedisValue[] { requestId, expiry.TotalMilliseconds }); return (long)result! 1; } }三个关键点要理解到位第一加锁为什么用When.NotExists。这个枚举对应 Redis 的NX参数保证“判断 key 不存在 写入”是一个原子操作不存在并发窗口。第二释放锁为什么要写 Lua 脚本。释放之前先GET比较 value 是不是自己的requestId再DEL。如果不用 Lua而是先GET再DEL两步之间存在时间差当前线程的锁到期另一个线程拿到锁当前线程再执行DEL就把别人的锁删掉了。用 Lua 脚本可以把“判断 删除”封装成原子操作。第三为什么需要续期。如果锁过期时间设置成 10 秒但业务执行了 20 秒第二个线程在第一个线程业务还没结束时就拿到了锁冲突依旧。续期的作用是业务还活着锁就一直延长有效期。5.4 看门狗续期思路看门狗是一个后台任务周期性对当前进程持有的锁做续期。用 BackgroundService 可以实现核心思路是维护一个“当前持有锁”的内存注册表public class LockWatchdog : BackgroundService { private readonly IDistributedLock _distributedLock; private readonly TimeSpan _expiry TimeSpan.FromSeconds(30); private readonly TimeSpan _renewInterval TimeSpan.FromSeconds(10); // 业务代码通过 RegisterAsync / UnregisterAsync 维护当前持有的锁集合 protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { await Task.Delay(_renewInterval, stoppingToken); // 遍历注册表中所有锁逐个调用 RenewAsync } } }这是简化示意。生产实现需要用ConcurrentDictionary管理锁的requestId和到期时间并且业务结束时从注册表移除。如果不想手写业界也有 RedLock.net 等现成库但需要评估其实现是否符合你的场景。官方文档也明确警告过RedLock 本身并不是绝对安全的锁方案在 Redis 主从切换、网络分区等场景下依然存在锁丢失的可能。我的建议是先自己实现一套短小精悍的锁把原理吃透再决定用不用现成库。6. 业务接口实战库存扣减、订单幂等与重复请求拦截6.1 库存扣减接口库存接口是超卖问题最典型的发生点。锁的目的是让同一商品同一时刻只有一个请求进入扣减流程但数据库条件更新仍然要保留。using Microsoft.AspNetCore.Mvc; using StackExchange.Redis; [ApiController] [Route(api/inventory)] public class InventoryController : ControllerBase { private readonly IDistributedLock _distributedLock; private readonly IConnectionMultiplexer _redis; private readonly ILoggerInventoryController _logger; public InventoryController( IDistributedLock distributedLock, IConnectionMultiplexer redis, ILoggerInventoryController logger) { _distributedLock distributedLock; _redis redis; _logger logger; } [HttpPost(deduct)] public async TaskIActionResult Deduct(DeductRequest request) { var lockKey $inventory:lock:{request.ProductId}; var requestId Guid.NewGuid().ToString(N); var expiry TimeSpan.FromSeconds(10); var acquired await _distributedLock.AcquireAsync(lockKey, requestId, expiry); if (!acquired) { return StatusCode(409, new { code 409, message 系统繁忙请稍后重试 }); } try { var db _redis.GetDatabase(); var stockKey $inventory:stock:{request.ProductId}; var currentStock (int)await db.StringGetAsync(stockKey); if (currentStock request.Quantity) { return BadRequest(new { code 400, message 库存不足 }); } await db.StringDecrementAsync(stockKey, request.Quantity); // 真正落地库存时必须再执行数据库原子更新 // UPDATE product SET stock stock - quantity // WHERE id productId AND stock quantity // Redis 锁只进入一层互斥数据库条件更新才是最终兜底 return Ok(new { code 200, data new { remaining currentStock - request.Quantity } }); } finally { await _distributedLock.ReleaseAsync(lockKey, requestId); } } public record DeductRequest(string ProductId, int Quantity); }这个案例演示的锁用法是“临界区保护”加锁成功后先查 Redis 里的库存判断是否足够再扣减。实际生产环境库存数据的主存储肯定在关系型数据库Redis 缓存可能只是热点数据加速。无论数据放在哪一层都要记住锁负责互斥数据库的条件更新负责最终一致性。如果锁超时导致业务还在执行但锁已经释放另一个请求进入临界区最终数据库条件更新WHERE stock quantity也能拦住超卖。这就是前面说的双保险。6.2 订单幂等防重接口幂等防重的思路和加锁不一样。加锁解决的是“同时抢资源”幂等解决的是“同一个请求只处理一次”。最简单的做法是客户端每次请求携带一个幂等键比如IdempotencyKey或业务订单号服务端用 Redis 判断这个键是否已经处理过。[ApiController] [Route(api/orders)] public class OrderController : ControllerBase { private readonly IDistributedLock _distributedLock; private readonly IConnectionMultiplexer _redis; public OrderController( IDistributedLock distributedLock, IConnectionMultiplexer redis) { _distributedLock distributedLock; _redis redis; } [HttpPost(create)] public async TaskIActionResult CreateOrder(OrderCreateRequest request) { var idempotencyKey request.IdempotencyKey ?? Guid.NewGuid().ToString(N); var db _redis.GetDatabase(); var idempotencyKeyName $idempotency:order:{idempotencyKey}; bool isFirstRequest await db.StringSetAsync( idempotencyKeyName, processing, TimeSpan.FromHours(24), When.NotExists); if (!isFirstRequest) { return Ok(new { code 200, message 订单已处理无需重复提交, orderId await db.StringGetAsync($idempotency:orderId:{idempotencyKey}) }); } var lockKey $order:lock:{request.UserId}; var requestId Guid.NewGuid().ToString(N); var acquired await _distributedLock.AcquireAsync(lockKey, requestId, TimeSpan.FromSeconds(10)); if (!acquired) { return StatusCode(409, new { code 409, message 订单处理中请勿重复提交 }); } try { // 真正创建订单的逻辑 // 创建成功后把订单 ID 写入 Redis后续重复请求直接返回 var orderId Guid.NewGuid().ToString(N); await db.StringSetAsync($idempotency:orderId:{idempotencyKey}, orderId); await db.StringSetAsync(idempotencyKeyName, completed, TimeSpan.FromHours(24)); return Ok(new { code 200, orderId orderId }); } catch { // 业务失败要删除幂等标记否则用户永远无法重试 await db.KeyDeleteAsync(idempotencyKeyName); throw; } finally { await _distributedLock.ReleaseAsync(lockKey, requestId); } } public record OrderCreateRequest(string UserId, string IdempotencyKey, int Quantity); }这里两把锁的功能不同。幂等标记负责拦截重复请求分布式锁负责同一用户并发创建订单时的互斥。实际项目中如果前面有 API 网关并且网关已经做了幂等去重后端可以只保留一层。但从接口自身健壮性考虑两层的抗压能力会明显更好。注意一个细节业务失败时要把幂等标记删掉否则用户修正参数后还是会被当成“重复请求”拦截导致永远无法下单。这种问题在自测阶段很难发现通常要到联调阶段才能暴露。6.3 锁过期时间怎么定锁的过期时间不是拍脑袋写的判断依据是“临界区最大可能执行时长”。如果业务一般 200 毫秒内完成可以设置 3 到 5 秒如果涉及多个远程调用可以把过期时间放宽到 10 到 30 秒。过期时间太短业务没跑完锁就释放太长Redis 宕机时锁的阻塞时间也会更长。稳妥做法是设置一个合理的默认值再配合看门狗续期。7. 接口 API 测试、压测与性能验证7.1 启动服务并访问 Swagger执行以下命令启动服务dotnet run浏览器访问/swagger能看到api/inventory/deduct和api/orders/create两个接口。先用 Swagger 手动点击测试确认参数校验和返回值正常再上压测。7.2 curl 调用接口实际联调时前端是 Vue 或者小程序的情况下用 curl 验证最直接curl -X POST http://localhost:5000/api/inventory/deduct \ -H Content-Type: application/json \ -d {productId:SKU-1001,quantity:1}正常返回{ code: 200, data: { remaining: 0 } }库存用完后再次请求会返回库存不足的提示。这里为了测试方便可以先在 Redis 里手动初始化库存 keyredis-cli -h 127.0.0.1 -p 6379 -a yourpassword SET inventory:stock:SKU-1001 107.3 并发压测观察超卖与重复请求使用 ApacheBench 做最简单的小并发压测ab -n 500 -c 20 -p order.json -T application/json \ http://localhost:5000/api/orders/createorder.json内容{ userId: user-10086, idempotencyKey: idempotency-test-001, quantity: 1 }保持idempotencyKey不变重复执行压测理想结果是无论请求打多少次后端只创建一单。这个场景验证的就是幂等防重能力。如果想看抢库存场景可以把idempotencyKey改成随机值然后观察库存从 10 扣到 0 的过程整个过程不能出现扣成负数、不能出现超卖。压测关注三个指标错误率、接口耗时、库存或订单数量是否正确。如果压测过程中出现大量 409说明锁冲突明显如果出现超卖优先查数据库条件更新是否写对如果出现重复订单优先查幂等标记是否生效。7.4 用 Redis monitor 观察锁过程压测时打开另外一个终端用 Redis monitor 实时观察命令redis-cli -h 127.0.0.1 -p 6379 monitor能看到锁的 SET、释放锁的 Lua 脚本调用、库存扣减命令。这个操作对排查锁是否生效非常直观。生产环境不要长期开 monitor它会对 Redis 性能产生明显影响只在测试阶段使用。7.5 观察 .NET 进程资源占用压测过程中可以用dotnet-counters观察进程的 GC 和线程池状态dotnet-counters monitor --process-id 进程ID --counters System.Runtime如果耗时集中在 Redis 操作上优先确认ConnectionMultiplexer是否注册为单例。很多初学者在每次请求里ConnectionMultiplexer.Connect压测一上来就会把连接数打爆性能表现会很差。正确的做法就是第 3 章那样在依赖注入容器里注册单例。8. 常见问题与排查方法问题现象可能原因排查方式解决方案启动后 Redis 连接失败地址或密码错误、Redis 未启动执行redis-cli ping修改连接串确认 Redis 容器状态接口持续返回系统繁忙锁未释放、锁过期时间过短、死锁用 Redis 可视化工具查看锁 key 的 TTL检查 finally 释放逻辑调整过期时间增加看门狗释放锁误删他人锁释放时没有校验 requestId查看释放脚本是否存在 GET DEL 分步执行必须用 Lua 脚本实现比较后删除压测后库存出现负数只用了 Redis 锁数据库条件更新缺失排查 SQL 是否有stock quantity数据库 UPDATE 增加条件锁和条件更新双保险幂等接口业务失败后无法重试异常时没有删除幂等标记查看 Redis 中idempotency:*keycatch 中删除幂等标记Redis 锁 key 和缓存 key 冲突key 命名未加前缀查看 key 列表锁 key 统一加lock:前缀服务重启后锁一直存在加锁时没有设置过期时间查看锁 key 的 TTL 是否为 -1使用SET NX EX设置过期时间压测时 Redis 连接数暴涨每请求都创建 ConnectionMultiplexer查看连接数和日志注册单例复用连接Redis 主从切换后锁丢失锁写入 master 后未同步到 slave查看主从日志评估锁方案部署 Redis 哨兵或 Cluster评估 RedLock 的适用性Redis 连接超时网络抖动、Redis 负载过高查看 Redis 慢日志和网络延迟增加超时时间使用连接池控制客户端重试9. 最佳实践与使用建议9.1 锁的使用规范分布式锁不是越重越好。尽可能缩短临界区范围只在真正需要互斥的代码段加锁避免把日志、外部 HTTP 调用、文件写入都放在锁内否则锁的持有时间会被无限拉长。锁的命名要有规范。推荐格式是业务:资源类型:资源ID:lock例如order:user:10086:lock。带上业务前缀可以避免不同业务共用同一个 Redis 实例时 key 互相污染。锁的 value 必须是唯一标识。所有示例都使用Guid.NewGuid().ToString(N)这是分布式系统里的通用做法。没有这个标识就无法安全释放锁。9.2 三层防御体系从整个高并发后端架构来看Redis 分布式锁只是其中一环。更完整的防御体系是三层的第一层Redis 锁或幂等标记解决并发进入和重复请求的互斥问题 第二层数据库条件更新比如UPDATE ... WHERE stock quantity解决真正的超卖问题 第三层消息队列和批量任务削峰防止短时冲击把接口打垮。这套思路对 .NET 和 Java 是相通的。很多团队还在纠结 .NET 和 Java 怎么选其实高并发分布式锁、缓存、消息队列这些技术在中后台架构上是同构的先把一条链路跑通再做选型更有意义。9.3 AI 辅助开发的正确用法AI 辅助开发在这类项目中可以明显提速但要用对地方。可以让 AI 生成 WebAPI 接口骨架、Swagger 注释、DTO 定义、单元测试、压测脚本这些内容模式化、重复度高交给 AI 很合适。也可以把报错日志贴给 AI让它给出初步排查方向节省查资料的时间。但分布式锁的核心代码包括 Lua 脚本、锁的释放逻辑、看门狗续期强烈建议自己手动写一遍并且逐行理解。原因很直接这些代码属于“一旦出错后果严重”的底层基础设施靠 AI 生成容易拿到表面能用、边界情况全错的版本。面试时考分布式锁考官想听的是你对锁语义和原子性的理解不是你有没有调过某个库。10. 总结与下一步这套基于 .NET 10 WebAPI Redis 的分布式锁骨架最值得尝试的地方是把“锁”抽象成了三个方法加锁、释放、续期。业务代码只需要关心 key 和 requestId不需要每次重新实现 Redis 原子操作。这个抽象可以在 RedLock、自研锁、数据库锁之间随时切换。第一步建议先验证的功能启动 Redis初始化一个库存 key然后用ab -n 500 -c 20压测库存扣减接口。看两个结果库存没有被扣成负数重复请求没有产生多笔订单。如果这两个结果都正确集群并发冲突的最核心问题就已经解决了。最容易踩的坑有两个一个是锁过期太快导致业务没跑完锁就释放另一个是释放锁时没有校验 requestId 导致误删别人的锁。前者靠看门狗续期缓解后者靠 Lua 脚本兜底。这两个点也是分布式锁面试题的高频考点可以重点准备。后续可以继续扩展的方向包括接入 Redis 哨兵或 Cluster 解决单点问题用消息队列做订单创建削峰给接口增加限流组件把幂等逻辑封装成中间件或 ActionFilter避免每个接口重复写一遍。整个架构从“能用”到“生产可用”中间还需要补日志、监控、告警这些工程化能力先把最小骨架跑通再逐步补齐。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进