.NET Core如何处理并发请求?多用户同座位同时预订场景如何解决?
.NET Core 多独立请求处理机制
.NET Core 默认采用基于线程池的异步请求处理模型,核心逻辑如下:
- 内置的 Kestrel 服务器会维护可动态扩容的线程池,收到独立请求时优先分配空闲线程处理对应请求
- 不同请求的执行上下文默认完全隔离,只要线程池有可用资源,多个请求就可以并行处理
- 当请求处理逻辑涉及异步IO操作(比如数据库查询、文件读写)时,当前处理线程会被释放回线程池承接新请求,IO完成后再重新分配线程执行后续逻辑,大幅提升并发吞吐能力
订票场景并发请求执行结果
你描述的场景属于典型的*竞态条件(Race Condition)*问题,在没有做并发控制的默认情况下,会出现座位超售的业务异常,具体执行流程如下:
用户A和用户B的预订请求几乎同时到达服务器,分别被分配到不同的线程并行执行
- 线程A执行座位校验方法,查询数据库得到当前座位状态为「未预订」,校验通过
- 同一时间线程B也执行座位校验方法,查询到的同样是未修改的座位状态,校验也通过
- 线程A先执行预订操作,将座位状态修改为「已预订」写入数据库,返回预订成功
- 线程B紧接着执行预订操作,同样把座位状态修改为「已预订」写入数据库,也返回预订成功
最终结果就是同一个座位被成功预订给两名用户,业务数据完全不符合预期。
常见的解决方案
要避免这类问题,有几种成熟的实现方案可选:
- 原子操作合并:不要把校验和预订拆成两个独立的方法调用,直接在数据库层用单条SQL完成校验+修改的原子操作,避免中间状态被其他请求篡改
示例SQL:UPDATE seat SET status = '已预订' WHERE id = @seatId AND status = '未预订',执行后判断影响行数,大于0则预订成功,否则返回座位已被预订 - 乐观锁:给座位表加
version版本号字段,执行更新时携带校验阶段查到的版本号,更新同时把版本号+1
示例SQL:UPDATE seat SET status = '已预订', version = version + 1 WHERE id = @seatId AND version = @oldVersion AND status = '未预订' - 分布式锁:如果是多实例部署的服务,可以用Redis等中间件实现分布式锁,同一个座位的预订请求需要先拿到对应锁才能执行后续逻辑,执行完成后释放锁
- 数据库行级排他锁:校验座位状态的时候就加排他锁,保证同一时间只有一个请求能操作该座位的相关数据,其他请求会阻塞等待锁释放,再查询就能拿到最新的座位状态
内容的提问来源于stack exchange,提问作者Abinash Patra
相关产品推荐
相关产品推荐

