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 课程学习文档。