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的字符串函数使用说明

    符串或串(String)是由零个或多个字符组成的有限序列。一般记为 s=’dreamflys’(n>=0)。它是编程语言中表示文本的数据类型
    2012-01-01
  • 清理MySQL Binlog二进制日志的三种方式

    清理MySQL Binlog二进制日志的三种方式

    Binlog日志非常重要,但是占用的磁盘空间也很大,我们也需要定期的去清理二进制日志,在MySQL数据库中,提供了自动清理Binlog日志的参数,本文给大家介绍了清理MySQL Binlog二进制日志的三种方式,文中通过代码讲解非常详细,需要的朋友可以参考下
    2024-01-01
  • Mysql中的select ...for update

    Mysql中的select ...for update

    这篇文章主要介绍了Mysql中的select ...for update用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • MySQL数据库INNODB表损坏修复处理过程分享

    MySQL数据库INNODB表损坏修复处理过程分享

    突然收到MySQL报警,从库的数据库挂了,一直在不停的重启,打开错误日志,发现有张表坏了。innodb表损坏不能通过repair table 等修复myisam的命令操作。现在记录下解决过程
    2013-08-08
  • SQL窗口函数OVER用法实例整理

    SQL窗口函数OVER用法实例整理

    做SQL题时碰到了over()函数不太理解,所以整理了下,下面这篇文章主要给大家介绍了关于SQL窗口函数OVER用法的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • 常见MySQL隐式类型转换方式

    常见MySQL隐式类型转换方式

    这篇文章主要介绍了常见MySQL隐式类型转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2026-03-03
  • MySQL 5.6主从报错的实战记录

    MySQL 5.6主从报错的实战记录

    这篇文章主要给大家介绍了关于MySQL 5.6主从报错的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • MySQL用户授权管理及白名单的实现

    MySQL用户授权管理及白名单的实现

    MySQL作为一种常用的关系型数据库管理系统,在权限管理和用户认证方面提供了丰富的功能和方案,本文主要介绍了MySQL用户授权管理及白名单的实现,感兴趣的可以了解一下
    2023-09-09
  • MySQL将select结果执行update的实例教程

    MySQL将select结果执行update的实例教程

    这篇文章主要给大家介绍了关于MySQL将select结果执行update的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • MySQL虚拟列的具体使用

    MySQL虚拟列的具体使用

    虚拟列是一种特殊类型的表列,它不像普通列直接存储数据,而是根据其他列中的数据动态生成,本文主要介绍了MySQL虚拟列的具体使用,感兴趣的可以了解一下
    2024-09-09

最新评论