导读:本期聚焦于小伙伴创作的《为什么MySQL中的悲观锁会导致系统吞吐量下降,改用乐观锁版本号机制能解决吗》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《为什么MySQL中的悲观锁会导致系统吞吐量下降,改用乐观锁版本号机制能解决吗》有用,将其分享出去将是对创作者最好的鼓励。

在MySQL的并发控制场景中,锁机制是保障数据一致性的核心手段,其中悲观锁和乐观锁是两种最常用的方案。不少开发者在实际业务中发现,使用悲观锁后系统的吞吐量会出现明显下降,而切换到乐观锁版本号机制后性能往往能得到改善,这背后的原因值得深入探究。

为什么MySQL中的悲观锁会导致系统吞吐量下降,改用乐观锁版本号机制能解决吗

悲观锁导致系统吞吐量下降的核心原因

悲观锁的核心思想是“先加锁再操作”,它默认认为数据在并发场景下一定会出现冲突,因此在操作数据前就会先获取锁,阻塞其他事务对该数据的操作,直到当前事务提交或回滚释放锁。

这种机制在并发量较低、冲突概率高的场景下能保证数据一致性,但在高并发场景下会带来几个明显的性能问题:

  • 锁竞争开销大:大量事务同时请求同一行数据的锁时,未获取到锁的事务会进入等待队列,频繁的上下文切换和锁等待会消耗大量CPU和内存资源。
  • 锁持有时间长:如果事务中包含复杂的业务逻辑,锁的持有时间会被拉长,进一步加剧锁竞争,导致更多请求阻塞。
  • 可能出现死锁:多个事务互相持有对方需要的锁时,会触发死锁检测机制,数据库需要回滚部分事务来解除死锁,额外的回滚操作也会降低整体吞吐量。

悲观锁的典型使用示例

MySQL中悲观锁通常通过SELECT ... FOR UPDATE语句实现,以下是常见的使用代码:

-- 开启事务
START TRANSACTION;
-- 查询id为1的商品库存,并加悲观锁
SELECT stock FROM product WHERE id = 1 FOR UPDATE;
-- 业务逻辑:扣减库存
UPDATE product SET stock = stock - 1 WHERE id = 1;
-- 提交事务,释放锁
COMMIT;

上述代码中,事务执行SELECT ... FOR UPDATE后会锁定id为1的商品行,其他事务执行同样的查询或更新操作时会阻塞,直到当前事务提交。

乐观锁版本号机制的实现原理

乐观锁的核心思想是“先操作再校验”,它默认认为数据冲突的概率很低,因此操作过程中不会加锁,仅在提交更新时通过版本号判断数据是否被其他事务修改过。

版本号机制是乐观锁最常用的实现方式,具体逻辑如下:

  1. 在数据表中新增一个version字段,每次更新数据时版本号加1。
  2. 查询数据时同时获取当前的version值。
  3. 更新数据时,在WHERE条件中带上查询到的version值,同时把version加1。
  4. 如果更新语句返回的影响行数为1,说明更新成功;如果影响行数为0,说明数据已经被其他事务修改过,当前更新失败,需要重试或提示用户。

乐观锁版本号机制的实现示例

首先需要在商品表中新增version字段:

ALTER TABLE product ADD COLUMN version INT DEFAULT 0 NOT NULL;

乐观锁的更新逻辑代码如下:

-- 开启事务
START TRANSACTION;
-- 查询商品库存和当前版本号
SELECT stock, version FROM product WHERE id = 1;
-- 假设查询到的stock为10,version为5
-- 更新时校验版本号,同时版本号加1
UPDATE product SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 5;
-- 判断更新是否成功
-- 如果影响行数为1,说明更新成功,提交事务
COMMIT;
-- 如果影响行数为0,说明版本号不匹配,更新失败,回滚事务并重试
ROLLBACK;

在Java业务代码中,重试逻辑可以这样实现:

public boolean deductStockWithOptimisticLock(int productId) {
    int retryCount = 3;
    while (retryCount > 0) {
        // 查询商品库存和版本号
        Product product = productDao.selectById(productId);
        if (product.getStock() <= 0) {
            return false;
        }
        // 执行更新,返回影响行数
        int rows = productDao.updateStockWithVersion(productId, product.getStock() - 1, product.getVersion());
        if (rows > 0) {
            return true;
        }
        // 更新失败,重试次数减1
        retryCount--;
    }
    return false;
}

两种锁方案的适用场景对比

悲观锁和乐观锁没有绝对的好坏,需要根据业务场景的特性选择:

对比维度悲观锁乐观锁版本号机制
冲突概率适合冲突概率高的场景适合冲突概率低的场景
并发性能高并发下吞吐量低,锁竞争开销大高并发下吞吐量高,无锁等待
实现复杂度数据库原生支持,实现简单需要额外维护版本号,更新失败需要重试,实现稍复杂
数据一致性强一致性,操作期间数据不会被修改最终一致性,更新失败需要重试保证最终正确

如果业务是秒杀、库存扣减这类冲突概率极高的场景,悲观锁能避免大量重试带来的额外开销;如果是用户资料修改、订单状态更新这类冲突概率低的场景,乐观锁版本号机制能大幅提升系统吞吐量,减少请求阻塞。

需要注意的是,乐观锁的重试次数不能设置过高,否则重试本身也会消耗资源,通常设置3-5次重试即可,超过次数可以提示用户操作失败。

MySQL悲观锁乐观锁版本号机制系统吞吐量修改时间:2026-07-21 05:12:26

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。