在这里插入图片描述

🔒 一、悲观锁(Pessimistic Locking)

原理先加锁再操作,确保独占访问。
实现方式

BEGIN TRANSACTION;
SELECT * FROM table WHERE id = 1 FOR UPDATE; -- 加行级锁
UPDATE table SET column = value WHERE id = 1;
COMMIT;

适用场景:写操作频繁、冲突概率高的场景(如秒杀扣库存)。
优缺点

  • ✅ 强一致性,避免并发冲突;
  • ❌ 锁竞争可能导致线程阻塞,降低吞吐量;
  • ❌ 需控制事务粒度,避免长事务引发死锁。

🔄 二、乐观锁(Optimistic Locking)

原理无锁检查,冲突重试,基于版本号/时间戳校验。
实现方式

  1. 表中增加 version 字段;
  2. 更新时校验版本号:
UPDATE table SET column = new_value, version = version + 1 
WHERE id = 1 AND version = old_version;

若返回影响行数为0,则重试或放弃。
适用场景:读多写少(如配置更新、轻度并发扣减)。
优化

  • 结合重试机制(如指数退避算法);
  • 业务层设计幂等操作,避免重复提交副作用。

⏱️ 三、MVCC(多版本并发控制)

原理读旧写新,版本隔离,通过快照读避免阻塞。
实现方式

  • 数据库默认支持(如MySQL InnoDB的REPEATABLE READ隔离级别);
  • 写操作创建新版本,读操作访问旧快照。
    适用场景:读写混合的高并发场景(如余额查询与转账)。
    注意
  • 需合理设置隔离级别(如避免READ UNCOMMITTED);
  • 可能引发幻读问题,需配合间隙锁(Gap Lock)。

🌐 四、分布式锁(Distributed Lock)

原理全局互斥,跨节点协调,适用于分布式系统。
实现方式

方案 实现要点 适用场景
Redis锁 SET lock_key unique_val NX EX 30 + Lua脚本原子释放 高吞吐要求(如活动库存)
ZooKeeper锁 创建临时有序节点,监听前序节点删除事件 强一致性场景(如金融交易)
数据库锁 基于唯一约束插入锁记录(INSERT INTO lock_table(...) 无中间件的简单系统

关键设计

  • 锁自动续期(看门狗机制),避免业务未完成锁过期;
  • 锁标识(如UUID)防误删,确保释放者为持有者;
  • 容灾设计(如Redis集群模式、ZooKeeper多数存活)。

📊 五、方案选型建议

场景 推荐方案 理由
单机写冲突高 悲观锁 强一致性,实现简单
读多写少,冲突概率低 乐观锁 无锁竞争,吞吐量高
读写混合,事务隔离要求高 MVCC 读写互不阻塞,兼顾性能与一致性
分布式系统,跨节点协调 Redis/ZooKeeper锁 全局互斥,高可用

⚠️ 六、通用优化原则

  1. 减小锁粒度:锁单行而非全表(如WHERE id=1);
  2. 缩短持有时间:事务内仅保留必要操作,避免远程调用;
  3. 异步化处理:写操作入队列(如Kafka),消费端串行处理;
  4. 监控与兜底
    • 数据库锁等待监控(SHOW ENGINE INNODB STATUS);
    • 重试次数限制,防雪崩;
    • 数据校对任务,修复不一致。

🌰 七、实战案例:电商库存扣减

// 方案1:悲观锁(强一致)
try (Connection conn = dataSource.getConnection()) {
    conn.setAutoCommit(false);
    PreparedStatement ps = conn.prepareStatement("SELECT stock FROM sku WHERE sku_id=? FOR UPDATE");
    ps.setString(1, "SKU_001");
    ResultSet rs = ps.executeQuery();
    if (rs.next() && rs.getInt("stock") > 0) {
        // 扣减库存
        executeUpdate("UPDATE sku SET stock=stock-1 WHERE sku_id='SKU_001'");
    }
    conn.commit();
}

// 方案2:乐观锁(高并发)
int retry = 0;
while (retry++ < 3) {
    Sku sku = dao.selectById("SKU_001");
    if (sku.getStock() > 0) {
        int rows = dao.update("UPDATE sku SET stock=stock-1, version=version+1 
                              WHERE sku_id='SKU_001' AND version=" + sku.getVersion());
        if (rows > 0) break; // 更新成功退出
    }
}

💡 总结:安全修改同一行数据的核心是 权衡一致性与性能。单机场景优先用数据库锁(悲观/乐观),分布式环境需引入全局协调(Redis/ZK),最终通过异步队列和监控兜底保障稳定性。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐