乐观锁、悲观锁和分布式锁,你用对了吗?

乐观锁、悲观锁和分布式锁的使用

后台开发最怕什么?数据被改乱。 把数据库想成公共厨房,锅就是数据。 三个人同时炒菜,锅翻了,菜就糊了。 锁就是排队机制,让锅一次只给一个人用。

下面讲讲我们3种最常用的锁:乐观锁、悲观锁、分布式锁。

1. 乐观锁

使用场景

适合冲突少的场景。比如用户修改自己的资料、库存更新不频繁的情况。

实际案例

我们有个商品表,结构大概是这样的:

sql 复制代码
CREATE TABLE product (
  id INT PRIMARY KEY,
  name VARCHAR(50),
  stock INT,
  version INT DEFAULT 0
);

注意那个version字段,这就是乐观锁的关键。

当用户下单时,我们会这样更新库存:

sql 复制代码
UPDATE product 
SET stock = stock - 1, version = version + 1 
WHERE id = 1001 AND version = 2;

只有当当前版本是2的时候,才允许扣减库存,并把版本号加1。

如果两个请求同时来,第一个执行完后版本变成3,第二个请求因为version=2不成立,更新就会失败。这时候程序就知道:"哦,有人抢先改了,我得重新查一下库存再处理。"

小结

  • 优点:性能好,不阻塞
  • 缺点:冲突多了会反复重试
  • 适合:读多写少、并发不高的场景

2. 悲观锁:先上锁,再操作

使用场景

适合冲突频繁的场景。比如秒杀、抢票这种大家一窝蜂涌进来的情况。

实际案例

还是上面那个商品表,但这次我们用悲观锁:

sql 复制代码
BEGIN;

-- 先锁定这条记录,别人不能动
SELECT * FROM product WHERE id = 1001 FOR UPDATE;

-- 查到库存是5,减1
UPDATE product SET stock = 4 WHERE id = 1001;

COMMIT;

FOR UPDATE这个语句很关键,它会把这行数据锁住,直到事务结束。其他人想执行同样的查询,就得排队等着。

这样就能保证同一时间只有一个请求能修改库存,彻底避免超卖。

小结

  • 优点:简单直接,不会超卖
  • 缺点:性能差,容易阻塞
  • 适合:高并发、强一致性的场景

3. 分布式锁:跨机器的协调

使用场景

当你有多个服务实例(比如部署了3台服务器),单机锁不管用了,就得用分布式锁。

实际案例

我们系统做了集群,3台服务器一起处理订单。这时候用数据库锁或synchronized都没用------锁不住别的机器上的进程。

我们用了 Redis 来实现分布式锁:

java 复制代码
// 伪代码
String lockKey = "lock:product_1001";

// 尝试加锁,3秒自动过期
Boolean locked = redis.set(lockKey, "1", "NX", "EX", 3);

if (locked) {
    try {
        // 执行扣库存逻辑
        deductStock(1001);
    } finally {
        // 释放锁
        redis.del(lockKey);
    }
} else {
    // 加锁失败,说明别人正在处理
    System.out.println("商品太火爆,再试一次!");
}

这里用到了 Redis 的 SET key value NX EX 命令:

  • NX:只有key不存在时才设置
  • EX:3秒后自动过期(防止程序崩溃导致锁永远不释放)

小结

  • 优点:能跨服务协调
  • 缺点:依赖Redis,实现要小心死锁
  • 适合:分布式系统、集群环境

什么时候用哪种锁?

我总结了个简单判断方法:

场景 推荐用锁
单机应用,偶尔更新 乐观锁
单机应用,高频修改 悲观锁
多台服务器部署 分布式锁
读多写少 乐观锁
写操作特别频繁 悲观锁 or 分布式锁

最后一点心得

锁不是越多越好,而是够用就好。 先想清楚"锅会不会翻",再决定"怎么排队"。 技术就是解决问题的工具!用顺手了,也就没那么可怕了。

本文首发于微信公众号「刘大华的开发笔记」我是大华,专注分享前后端开发的实战笔记。 关注我,少走弯路,一起进步!

相关推荐
zl9798994 小时前
RabbitMQ-下载安装与Web页面
linux·分布式·rabbitmq
向着光芒的女孩5 小时前
【IDEA】关不了的Proxy Authentication弹框探索过程
java·ide·intellij-idea
Filotimo_5 小时前
Spring Boot 整合 JdbcTemplate(持久层)
java·spring boot·后端
李慕婉学姐6 小时前
【开题答辩过程】以《“饭否”食材搭配指南小程序的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring·小程序
abments7 小时前
pgsql timestamp without time zone > character varying解决方案
java
sanggou7 小时前
大数据量查询处理方案 - 内存优化与高效展示
java
没有bug.的程序员7 小时前
Java 字节码:看懂 JVM 的“机器语言“
java·jvm·python·spring·微服务
-大头.7 小时前
深入理解 Java 内存区域与 JVM 运行机制
java·jvm
没有bug.的程序员7 小时前
JVM 整体架构:一套虚拟机的心脏与血管
java·jvm·spring boot·spring cloud·架构
晨枫阳8 小时前
不同语言的元组对比
java·前端·javascript