MySQL中的乐观锁和悲观锁的区别及说明

 更新时间:2025年05月09日 10:06:51   作者:篱笆院的狗  
这篇文章主要介绍了MySQL中的乐观锁和悲观锁的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

在 MySQL 中,乐观锁悲观锁是两种不同的并发控制机制,用于解决多用户/事务同时操作数据时的冲突问题。

它们的核心理念和实现方式有显著区别:

1. 悲观锁(Pessimistic Locking)

核心思想

假设数据会被频繁修改,因此提前对数据进行加锁,防止其他事务访问,直到当前事务完成操作并释放锁。

实现方式

显式加锁:通过 SQL 语句主动申请锁。

  • 排他锁(X Lock)
SELECT ... FOR UPDATE

(在事务中锁定选中的行,阻止其他事务修改或加锁)

  • 共享锁(S Lock)
SELECT ... LOCK IN SHARE MODE

(允许其他事务读,但阻止写操作)

隐式加锁:数据库自动管理(如 MySQL 的 InnoDB 引擎默认使用行级锁)。

适用场景

  • 写多读少的场景(如频繁更新的数据)。
  • 需要强一致性且冲突概率较高时(如金融交易)。

示例

START TRANSACTION;
-- 锁定用户账户余额(排他锁)
SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE;
-- 执行扣款操作
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
COMMIT;

优点:

  • 保证数据操作的原子性和一致性。
  • 适合高竞争场景(冲突概率高时效率更高)。

缺点:

  • 可能导致死锁(需应用层处理)。
  • 降低并发性能(长时间持有锁会阻塞其他操作)。

2. 乐观锁(Optimistic Locking)

核心思想

假设数据冲突较少发生,因此不加锁,而是在更新时检查数据是否被修改过。若被修改过,则拒绝操作或重试。

实现方式

  • 版本号(Version):在表中增加 version 字段,更新时验证版本号。
UPDATE table 
SET 
  column = new_value, 
  version = version + 1 
WHERE 
  id = 1 AND version = old_version;
  • 时间戳(Timestamp):类似版本号,但使用时间戳标记数据修改时间。
  • CAS(Compare-And-Swap):在应用层比较数据一致性后再提交。

适用场景

  • 读多写少的场景(如商品库存充足时的秒杀)。
  • 冲突概率较低时(如用户点赞操作)。

示例

-- 初始查询(获取当前版本号)
SELECT balance, version FROM accounts WHERE user_id = 1;

-- 更新时检查版本号
UPDATE accounts 
SET 
  balance = balance - 100, 
  version = version + 1 
WHERE 
  user_id = 1 AND version = 1; -- 假设旧版本号是1

-- 如果受影响行数=0,说明版本已过期,需重试或报错

优点:

  • 无锁竞争,并发性能高。
  • 避免死锁问题。

缺点:

  • 冲突发生时需处理重试逻辑(如循环重试或返回错误)。
  • 无法保证强一致性(最终一致性)。

3. 对比总结

特性悲观锁乐观锁
加锁时机操作前加锁操作后验证
实现复杂度依赖数据库机制需应用层配合(如版本号)
并发性能较低(锁竞争)较高(无锁)
适用场景高竞争、强一致性低竞争、最终一致性
典型问题死锁、性能瓶颈版本冲突、重试逻辑

4. MySQL 中的实际选择

  • InnoDB 引擎:支持行级锁,适合悲观锁(需显式使用 FOR UPDATE)。
  • MyISAM 引擎:仅支持表级锁,悲观锁性能较差,通常不推荐。
  • 乐观锁:需在应用层实现(如通过版本号字段),与存储引擎无关。

根据业务场景选择:

  • 金融交易等强一致性场景 → 悲观锁
  • 高并发读多写少场景 → 乐观锁

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 年底了,你的mysql密码安全吗

    年底了,你的mysql密码安全吗

    这篇文章主要介绍了MySQL密码安全的相关知识,帮助大家更好的使用数据库,感兴趣的朋友可以了解下
    2021-01-01
  • mysql子查询(单行子查询,多行子查询,多列子查询)

    mysql子查询(单行子查询,多行子查询,多列子查询)

    本文主要介绍了mysql子查询(单行子查询,多行子查询,多列子查询),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • mysql中secure_file_priv=不生效问题及解决

    mysql中secure_file_priv=不生效问题及解决

    这篇文章主要介绍了mysql中secure_file_priv=不生效问题及解决方案,以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家,
    2024-01-01
  • MySQL高效可靠处理持久化数据的教程指南

    MySQL高效可靠处理持久化数据的教程指南

    这篇文章主要给大家详细介绍了 MySQL 如何高效可靠处理持久化数据,文中有详细的流程步骤和代码示例,对我们的学习有一定的帮助,需要的朋友可以参考下
    2023-07-07
  • MySQL下的RAND()优化案例分析

    MySQL下的RAND()优化案例分析

    这篇文章主要介绍了MySQL下的RAND()优化案例,包括对JOIN查询和子查询的优化,需要的朋友可以参考下
    2015-05-05
  • MYSQL 的10大经典优化案例场景实战

    MYSQL 的10大经典优化案例场景实战

    在应用开发的早期,数据量少,开发人员开发功能时更重视功能上的实现,随着生产数据的增长,很多SQL语句开始暴露出性能问题,对生产的影响也越来越大,有时可能这些有问题的SQL就是整个系统性能的瓶颈。本文我们就聊聊MYSQL的优化
    2021-09-09
  • mysql导入导出命令解析

    mysql导入导出命令解析

    这篇文章主要介绍了mysql导入导出命令解析,有需要的朋友可以参考下
    2015-08-08
  • mysql中like % %模糊查询的实现

    mysql中like % %模糊查询的实现

    这篇文章主要介绍了mysql中like % %模糊查询的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • MySQL怎么实现原子性的流程详解(以UPDATE为例)

    MySQL怎么实现原子性的流程详解(以UPDATE为例)

    原子性是事务的其中一个特性,指的是要么全部执行完,要么全都不执行,这篇文章主要介绍了MySQL怎么实现原子性(以UPDATE为例)的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-08-08
  • mysql5.x升级到mysql5.7后导入之前数据库date出错的快速解决方法

    mysql5.x升级到mysql5.7后导入之前数据库date出错的快速解决方法

    这篇文章主要介绍了mysql5.x升级到mysql5.7后导入之前数据库date出错的快速解决方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-09-09

最新评论