ASP.NET MVC中SQL Server单用户编辑时锁全表禁他人编辑方案咨询
针对全局排他编辑需求的可靠实现方案
你之前自己加状态标记表的方案可靠性差是必然的,核心问题是没有原子性保障、没有自动释放机制:要么两个用户同时读到"未锁定"状态同时拿到编辑权,要么遇到用户直接关浏览器、服务崩溃、请求异常中断的情况,标记位永远卡在"锁定中",直接把功能锁死。
你的业务要求是粗粒度的全局排他:只要有一个用户在编辑,其他所有人都不能编辑三张表的任何数据,完全不用自己造轮子,直接用SQL Server原生的应用锁能力就能解决,天生支持原子操作、异常自动释放,不会出现死标记问题。
具体实现步骤
- 锁获取逻辑:当用户点击"进入编辑"按钮发起请求时,开启数据库事务后,直接调用系统存储过程
sp_getapplock申请排他锁,锁超时设为0,拿不到锁立刻返回失败,核心SQL示例:
DECLARE @lockStatus INT; EXEC @lockStatus = sp_getapplock @Resource = 'GlobalEditLock_ThreeBusinessTables', -- 全局唯一的锁标识 @LockMode = 'Exclusive', -- 排他锁,同一时间只能有一个连接持有 @LockOwner = 'Transaction', -- 锁和当前事务绑定 @LockTimeout = 0; -- 锁等待时间设为0,抢不到直接返回 -- 返回值0/1代表加锁成功,其余值代表加锁失败 IF @lockStatus NOT IN (0,1) BEGIN RAISERROR('当前已有用户正在编辑数据,请稍后再试', 16, 1); ROLLBACK TRANSACTION; RETURN END
- 锁自动释放逻辑:完全不需要手动写重置标记的代码,以下任意场景触发时SQL Server会自动释放锁,不会残留死锁标记:
- 用户编辑完成提交/取消编辑,事务提交或回滚
- 用户直接关闭浏览器、断网,连接断开
- 服务重启、IIS进程回收、数据库连接异常中断
- 事务超时自动回滚
- 前端兜底配合:用户进入编辑页后,前端每30秒发一次心跳请求保活,如果后端连续2次没收到心跳(比如用户关了页面),主动终止对应持有锁的事务,锁会在1分钟内自动释放,不会长时间占用。
其他备选方案说明
- 如果你的MVC应用是单台IIS部署、没有多节点负载均衡,也可以用系统命名Mutex做进程级排他锁,实现更简单,但多实例部署时锁无法跨节点生效,优先选上面的数据库应用锁方案。
- 如果需要给用户更好的提示,比如显示"当前用户XXX正在编辑,已编辑X分钟",可以单独建一张纯展示用的锁状态表,用户拿锁成功时写入当前用户ID、拿锁时间即可,这张表不要参与锁的判断逻辑,哪怕数据残留也不影响核心锁的正确性,下次有用户拿锁成功直接覆盖记录就行。
不要尝试给自建的标记位表加过期时间、定时清理任务这类补丁,本质上都是在重复实现数据库已经提供的锁能力,还要自己处理并发原子性、异常场景的bug,稳定性远不如原生方案。
内容的提问来源于stack exchange,提问作者ajay
相关产品推荐
相关产品推荐

