You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core API实现FIFO处理,避免活动预订并发冲突

解决活动预订并发超卖问题的方案

一、EF Core原生解决方案

这是最简洁的实现路径,直接利用数据库锁机制避免并发冲突,无需额外组件。

1. 悲观锁(行级锁定)

通过数据库的行锁语法,确保同一时间只有一个请求能修改目标活动的名额数据,彻底串行化对同一场活动的预订操作:

public async Task<bool> BookTickets(int eventId, int userId, int quantity)
{
    using var transaction = await _dbContext.Database.BeginTransactionAsync();
    try
    {
        // 锁定活动行,直到事务结束(SQL Server语法,其他数据库需调整锁语句)
        var eventEntity = await _dbContext.Events
            .FromSqlRaw("SELECT * FROM Events WHERE Id = {0} WITH (UPDLOCK, HOLDLOCK)", eventId)
            .FirstOrDefaultAsync();

        if (eventEntity == null || eventEntity.RemainingTickets < quantity)
        {
            await transaction.RollbackAsync();
            return false;
        }

        eventEntity.RemainingTickets -= quantity;
        _dbContext.Bookings.Add(new Booking { EventId = eventId, UserId = userId, Quantity = quantity });
        
        await _dbContext.SaveChangesAsync();
        await transaction.CommitAsync();
        return true;
    }
    catch
    {
        await transaction.RollbackAsync();
        throw;
    }
}

2. 乐观锁(版本控制)

给活动表添加版本列,EF Core会自动检测并发修改冲突,适合并发量不是极高的场景,实现更轻量化:

public class Event
{
    public int Id { get; set; }
    public int RemainingTickets { get; set; }
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

public async Task<bool> BookTickets(int eventId, int userId, int quantity)
{
    try
    {
        var eventEntity = await _dbContext.Events.FindAsync(eventId);
        if (eventEntity == null || eventEntity.RemainingTickets < quantity)
            return false;

        eventEntity.RemainingTickets -= quantity;
        _dbContext.Bookings.Add(new Booking { EventId = eventId, UserId = userId, Quantity = quantity });
        
        await _dbContext.SaveChangesAsync();
        return true;
    }
    catch (DbUpdateConcurrencyException)
    {
        // 并发冲突,返回失败让前端引导用户重试
        return false;
    }
}

二、Azure生态下的分布式场景方案

如果系统是多实例部署,单实例锁机制失效,可选用Azure的组件解决:

1. Azure Service Bus队列

将预订请求异步排入队列,用单消费者(或单消费者组)串行处理,彻底避免并发冲突:

  • 前端调用API时,直接发送预订消息到队列,返回"排队处理中"给用户
  • 后端部署Worker Service或Azure Function,从队列拉取消息逐个执行预订逻辑(配合EF Core锁保证数据库操作安全)
  • 处理完成后通过SignalR等方式通知用户结果

这种方式适合高并发场景,但需要额外处理消息重试、死信队列等异步逻辑。

2. Azure Redis分布式锁

用Redis实现分布式信号量,确保同一活动的预订请求同一时间只有一个在执行:

public async Task<bool> BookTicketsWithRedisLock(int eventId, int userId, int quantity)
{
    var lockKey = $"event:{eventId}:booking-lock";
    using var redis = await ConnectionMultiplexer.ConnectAsync("your-redis-connection-string");
    var db = redis.GetDatabase();

    // 获取锁,设置5秒过期时间防止死锁
    var lockToken = Guid.NewGuid().ToString();
    var lockAcquired = await db.LockTakeAsync(lockKey, lockToken, TimeSpan.FromSeconds(5));
    if (!lockAcquired)
        return false;

    try
    {
        // 复用EF Core的预订逻辑
        return await BookTickets(eventId, userId, quantity);
    }
    finally
    {
        await db.LockReleaseAsync(lockKey, lockToken);
    }
}

三、单实例内存级信号量方案

如果是单实例部署,直接用.NET原生SemaphoreSlim实现内存锁,按活动ID隔离:

private static readonly Dictionary<int, SemaphoreSlim> _eventSemaphores = new();
private static readonly object _semaphoreSyncLock = new();

public async Task<bool> BookTicketsWithSemaphore(int eventId, int userId, int quantity)
{
    SemaphoreSlim semaphore;
    lock (_semaphoreSyncLock)
    {
        if (!_eventSemaphores.TryGetValue(eventId, out semaphore))
        {
            semaphore = new SemaphoreSlim(1, 1);
            _eventSemaphores[eventId] = semaphore;
        }
    }

    await semaphore.WaitAsync();
    try
    {
        return await BookTickets(eventId, userId, quantity);
    }
    finally
    {
        semaphore.Release();
        // 可选:活动结束后移除对应信号量节省内存
    }
}

方案选择建议

  • 单实例+低并发:优先用EF Core乐观锁,实现最简洁
  • 单实例+高并发:SemaphoreSlim搭配EF Core悲观锁
  • 分布式部署:选Azure Redis分布式锁(同步场景)或Azure Service Bus队列(异步场景)

内容的提问来源于stack exchange,提问作者achilles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 09:15:34