状态标记(业务锁)实现方案

单据数据污染解决方案-状态标记(业务锁)

单据数据污染解决方案-状态标记(业务锁)

感觉本篇对你有帮助可以关注一下我的微信公众号(深入浅出谈java),会不定期更新知识和面试资料、技巧!!!

如何防止单据数据被多人操作,造成数据污染?

确保同一时间只有一个用户可以编辑单据,或者至少能检测到并发修改,避免数据冲突。比如当用户尝试编辑一个被锁定的单据时,提示"当前单据正被其他用户编辑"。同时,在服务端处理并发请求时,确保更新状态的原子性,避免竞态条件。

这时候,我应该考虑常见的并发控制方法。比如乐观锁和悲观锁,这两种方法在开发中经常用到。

本文主要针对的是 状态标记(业务锁)

解决方案

常见的解决方案包括:

  1. 乐观锁:通过版本号或时间戳检测冲突。
  2. 悲观锁:在事务中使用行级锁。
  3. 状态标记:结合超时机制防止死锁。
  4. 前端提示和实时检测。
  5. 操作日志和版本比对。
  6. 分布式锁应对多实例环境。
  7. 权限控制减少冲突可能性。

需要根据具体场景选择合适的方案,或者组合使用多种方法。例如,使用乐观锁处理大部分情况,结合前端提示和状态标记来提升用户体验。或者在高并发场景下使用悲观锁,同时设置合理的超时时间。

其他解决办法可阅读:单据污染解决方案 - 古渡蓝按 - 博客园

流程图

核心逻辑

状态标记。在单据表中增加一个状态字段,比如"编辑中",当用户开始编辑时,将状态设为不可编辑,保存后再恢复。这种方法需要前端和后端配合,比如用户点击编辑时,后端检查状态是否为可编辑,如果是,就设为编辑中,防止其他人编辑。但可能存在用户忘记保存或者异常退出的情况,导致状态一直处于编辑中,所以可能需要一个超时机制,自动释放状态。

状态标记(业务锁)核心逻辑

通过业务字段标记单据的编辑状态,确保同一时间只有一个用户能操作单据。
关键点

  1. 使用数据库字段(如 status)标记单据是否被锁定。
  2. 用户尝试编辑时,检查并抢占锁;编辑完成后释放锁。
  3. 超时机制:防止用户未提交导致锁永久占用。

具体实现步骤

1. 数据库设计

例如:在单据表中添加字段

字段名 类型 说明
status int 0-可编辑,1-编辑中
lock_by varchar 锁定者(用户ID)
lock_time datetime 锁定时间(用于超时释放)

2. 用户操作流程

步骤 用户行为 前端 服务端逻辑 数据库操作
1 用户A点击"编辑"按钮 发送/lock请求,携带单据ID 检查当前单据状态: 若status=0,更新为status=1,记录lock_bylock_time UPDATE table SET status=1, lock_by='A', lock_time=NOW() WHERE id=123 AND status=0
2 用户A编辑表单 表单展示,禁用其他用户操作入口 保持锁定状态
3 用户A提交保存 发送保存请求,携带修改数据 更新单据数据,并释放锁(status=0 UPDATE table SET ..., status=0 WHERE id=123 AND lock_by='A'
4 用户B尝试编辑同一单据 发送/lock请求 检查状态:status=1,返回"单据已

超时释放锁

  • 后台定时任务 :每隔N分钟扫描lock_time超时的单据,自动释放锁:

    sql 复制代码
    UPDATE table SET status=0 
    WHERE status=1 AND lock_time < NOW() - INTERVAL '5 MINUTE';

伪代码

1. 服务端锁定逻辑(伪代码)

python 复制代码
def lock_order(order_id, user_id):
    # 原子操作:尝试锁定
    updated = execute_sql(
        "UPDATE orders SET status=1, lock_by=%s, lock_time=NOW() "
        "WHERE id=%s AND status=0", 
        [user_id, order_id]
    )
    if updated == 0:
        # 获取当前锁定者信息
        locked_by = execute_sql(
            "SELECT lock_by FROM orders WHERE id=%s", [order_id]
        )
        return {"success": False, "message": f"单据已被{locked_by}占用"}
    return {"success": True}

2. 前端提示逻辑(伪代码)

javascript 复制代码
// 用户点击编辑按钮
async function handleEdit(orderId) {
  const res = await fetch('/lock', { method: 'POST', body: { orderId } });
  if (!res.success) {
    alert(res.message); // 如"单据已被张三锁定"
  } else {
    // 进入编辑模式
  }
}

注意事项

  1. 原子性:锁定操作需通过单条SQL保证原子性(避免竞态条件)。
  2. 锁释放
    • 用户提交后必须释放锁。
    • 异常场景(如用户关闭页面)依赖后台超时释放。
  3. 用户体验
    • 前端可轮询检查锁状态(如每30秒查询一次)。
    • 提示具体锁定者(如"单据正被张三编辑")。

通过状态标记机制,可低成本实现多人操作冲突控制,适合中小型系统或业务流程明确的场景。

最后文章有啥不对,欢迎大佬在评论区指点!!!

如果感觉对你有帮助就点赞推荐或者关注一下吧!!!