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

.NET Core 6 API+React+PostgreSQL实现资源互斥操作方案咨询

数据项操作锁定机制实现方案(React + .NET Core 6 + PostgreSQL)

针对你提出的需求,以下是三种适配技术栈的可行方案,从轻量到分布式场景覆盖:

方案一:数据库级操作会话锁(单实例后端首选)

1. 数据库表调整

在目标数据项表(例如items)中新增两个字段:

  • locked_by: VARCHAR(50) 存储锁定用户的ID
  • locked_until: TIMESTAMP 存储锁的过期时间

2. .NET Core 后端实现

  • 获取锁接口(用户开始操作时调用):
    [HttpPost("lock/{itemId}")]
    public async Task<IActionResult> LockItem(Guid itemId, string userId)
    {
        var item = await _dbContext.Items.FindAsync(itemId);
        if (item == null) return NotFound();
        
        // 校验是否被其他用户锁定且未过期
        if (item.LockedBy != userId && item.LockedUntil > DateTime.UtcNow)
        {
            return Conflict(new { message = "该数据项正在被其他用户操作,无法删除" });
        }
        
        // 锁定15分钟(可根据业务调整)
        item.LockedBy = userId;
        item.LockedUntil = DateTime.UtcNow.AddMinutes(15);
        await _dbContext.SaveChangesAsync();
        return Ok();
    }
    
  • 删除接口锁校验:
    [HttpDelete("{itemId}")]
    public async Task<IActionResult> DeleteItem(Guid itemId, string userId)
    {
        var item = await _dbContext.Items.FindAsync(itemId);
        if (item == null) return NotFound();
        
        // 非当前用户持有有效锁则禁止删除
        if (item.LockedBy != userId && item.LockedUntil > DateTime.UtcNow)
        {
            return Conflict(new { message = "该数据项正在被其他用户操作,无法删除" });
        }
        
        _dbContext.Items.Remove(item);
        await _dbContext.SaveChangesAsync();
        return NoContent();
    }
    
  • 解锁接口(操作完成/页面关闭时调用):
    [HttpPost("unlock/{itemId}")]
    public async Task<IActionResult> UnlockItem(Guid itemId, string userId)
    {
        var item = await _dbContext.Items.FindAsync(itemId);
        if (item == null) return NotFound();
        
        if (item.LockedBy == userId)
        {
            item.LockedBy = null;
            item.LockedUntil = null;
            await _dbContext.SaveChangesAsync();
        }
        return Ok();
    }
    

3. React前端实现

  • 用户进入操作页面时,先调用lock接口,成功后才展示编辑/操作组件;失败则提示用户
  • 监听页面卸载事件(beforeunload),自动调用unlock接口释放锁
  • 删除按钮点击前,直接调用删除接口,根据返回的Conflict状态码提示用户
  • 可选:每10分钟调用一次lock接口延长锁有效期,避免操作中途锁过期

方案二:Redis分布式锁(多实例后端场景)

如果后端是多实例部署,数据库锁可能存在一致性延迟,推荐结合Redis实现分布式锁:

1. 后端依赖

安装StackExchange.Redis包,配置Redis连接字符串

2. 核心逻辑

  • 获取锁方法:
    public async Task<bool> AcquireItemLock(string itemId, string userId, int expiryMinutes = 15)
    {
        var lockKey = $"item_lock:{itemId}";
        // 仅当锁不存在时设置成功,同时绑定过期时间
        return await _redisDatabase.StringSetAsync(lockKey, userId, TimeSpan.FromMinutes(expiryMinutes), When.NotExists);
    }
    
  • 删除接口校验:
    [HttpDelete("{itemId}")]
    public async Task<IActionResult> DeleteItem(Guid itemId, string userId)
    {
        var lockKey = $"item_lock:{itemId}";
        var lockedUserId = await _redisDatabase.StringGetAsync(lockKey);
        
        // 存在非当前用户的有效锁则禁止删除
        if (!lockedUserId.IsNullOrEmpty && lockedUserId != userId)
        {
            return Conflict(new { message = "该数据项正在被其他用户操作,无法删除" });
        }
        
        // 同时校验数据库状态(防止Redis锁过期但用户仍在操作)
        var item = await _dbContext.Items.FindAsync(itemId);
        if (item == null) return NotFound();
        
        _dbContext.Items.Remove(item);
        await _dbContext.SaveChangesAsync();
        // 删除锁
        await _redisDatabase.KeyDeleteAsync(lockKey);
        return NoContent();
    }
    
  • 前端逻辑与方案一一致,需在操作开始/结束时调用对应锁接口

方案三:乐观锁(轻量防并发场景)

如果不需要强锁定,仅需防止并发删除导致的数据异常,可使用乐观锁方案:

1. 数据库表调整

新增version字段(INT类型),每次数据更新时自动递增

2. 后端删除接口实现

[HttpDelete("{itemId}")]
public async Task<IActionResult> DeleteItem(Guid itemId, int currentVersion)
{
    var item = await _dbContext.Items.FindAsync(itemId);
    if (item == null) return NotFound();
    
    // 版本不匹配说明数据已被其他用户修改
    if (item.Version != currentVersion)
    {
        return Conflict(new { message = "该数据项已被其他用户修改,请刷新后重试" });
    }
    
    _dbContext.Items.Remove(item);
    await _dbContext.SaveChangesAsync();
    return NoContent();
}

3. 前端实现

  • 获取数据时保存version值
  • 删除操作时提交当前版本号,若返回冲突则提示用户刷新页面重新获取数据

关键注意事项

  • 锁过期时间需合理设置,避免用户意外退出导致数据项长期锁定
  • 前端必须处理页面意外关闭的场景,确保锁能被及时释放
  • 多实例部署时优先选择Redis分布式锁,保证锁的全局一致性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 03:01:07