Skip to content

062. Redis 分布式锁叠加乐观锁 ​

学习目标 ​

这一节把 Redis 分布式锁和乐观锁组合起来,优化并发更新。

学完后,你应该能理解:

  • 为什么单纯乐观锁会频繁重试;
  • 为什么单纯分布式锁仍然不够;
  • 分布式锁应该放在事务之前;
  • 为什么拿到锁后再开启数据库 session;
  • 这种组合如何减少 CPU 空转;
  • 指挥 AI 写并发代码时要明确哪些要求。

单纯乐观锁的问题 ​

乐观锁能保证数据正确。

但在高并发下,多个请求可能不断失败、重试、再失败。

这些重试会消耗后端 CPU、内存和请求时间。

如果业务逻辑里还要访问外部服务,成本会更高。

所以乐观锁虽然安全,但不一定足够高效。

用分布式锁降低重试 ​

可以在进入数据库事务前,先拿 Redis 分布式锁。

锁的 key 可以按业务资源设计。

例如给账户充值时,可以按账户 ID 加锁:

txt
lock:recharge:001

这样同一账户的并发充值会先在 Redis 层排队。

拿到锁后,再进入数据库事务。

为什么放在事务之前 ​

数据库 session 和事务都是重要资源。

如果请求还没拿到业务资源锁,就先占用数据库连接,会浪费数据库资源。

更合理的顺序是:

  1. 获取 Redis client;
  2. 申请分布式锁;
  3. 拿到锁后开启数据库 session;
  4. 开启事务;
  5. 执行业务更新;
  6. 提交事务;
  7. 释放 Redis 锁。

这样压力先落到 Redis,而不是 MySQL。

为什么还要乐观锁 ​

分布式锁有最大占用时间。

如果某个请求处理时间超过锁过期时间,锁会自动释放。

这时另一个请求可能拿到锁并开始更新同一条数据。

所以分布式锁不能单独保证数据正确。

最终更新时仍然要靠乐观锁判断 version。

分布式锁减少冲突。

乐观锁兜底保证正确。

对比单纯乐观锁 ​

单纯乐观锁下,多个请求会直接进入业务逻辑和事务。

冲突后再重试。

加上 Redis 分布式锁后,大部分请求会先在 Redis 层等待。

它们不会频繁进入数据库事务,也不会不停执行业务逻辑。

观察日志时,可以看到重试次数明显减少。

对比悲观锁 ​

悲观锁使用 MySQL 锁。

如果 SQL 没命中索引,可能锁表。

Redis 分布式锁不使用 MySQL 行锁。

它用 Redis key 控制同一业务资源的进入顺序。

这能避免一部分 MySQL 锁表风险。

但最终仍然要用乐观锁保证更新安全。

组合方案 ​

课程里的推荐组合是:

txt
Redis 分布式锁 + 乐观锁更新

它的目标是:

  • 减少 MySQL 锁等待;
  • 减少后端无意义重试;
  • 支持多实例后端;
  • 保证数据最终正确;
  • 控制数据库连接占用。

这比单纯让 AI 加一个 for update 更稳。

原子更新的位置 ​

并发更新有几类做法:

  • 原子更新:直接在 SQL 里完成字段计算;
  • 悲观锁:查询时加锁;
  • 乐观锁:更新时判断 version;
  • 分布式锁:Redis 层先做互斥。

原子更新适合简单数值计算。

复杂业务逻辑仍然需要乐观锁或组合方案。

指挥 AI 时要说清楚 ​

让 AI 写金额、库存、积分、审批金额这类代码时,不要只说“实现充值接口”。

要明确说明:

  • 这是并发更新场景;
  • 同一资源可能被多个请求同时更新;
  • 使用 Redis 分布式锁控制进入;
  • 更新时仍要使用乐观锁 version 兜底;
  • 更新失败要有限次数重试;
  • 超过重试次数返回明确错误。

这样 AI 更可能生成接近生产可用的代码。

这一节的重点 ​

这一节给出更完整的并发更新方案。

你需要记住:

  • 单纯乐观锁安全,但可能频繁重试;
  • 单纯分布式锁不能保证永远互斥;
  • 分布式锁要放在数据库事务之前;
  • 乐观锁负责最终正确性;
  • Redis 锁负责降低冲突和数据库压力;
  • 指挥 AI 时要把并发场景说清楚。

AI Agent 课程学习文档。