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使用Union将两张表的数据合并显示

    MYSQL使用Union将两张表的数据合并显示

    使用union操作符会将多张表中相同的数据取值一次,如果想将表1和表2中的值完整的显示出来,可以使用union all,今天通过本文给大家分享MYSQL使用Union将两张表的数据合并显示功能,需要的朋友参考下吧
    2021-08-08
  • MySQL的语法及其使用指南

    MySQL的语法及其使用指南

    数据库的选取,创建,丢弃和变更 数据表和索引的创建,变更和丢弃从数据表检索信息
    2008-04-04
  • mysql中的判断函数和分支语句详解

    mysql中的判断函数和分支语句详解

    本文介绍了SQL中常用的判断函数和分支语句,判断函数包括if()、ifnull()以及nullif(),用于处理数据判断和空值处理,分支语句分为多条件判断和固定值判断,便于根据不同条件执行相应逻辑,这些功能在数据处理和查询中非常实用
    2024-10-10
  • MySQL双主(主主)架构配置方案

    MySQL双主(主主)架构配置方案

    这篇文章主要介绍了MySQL双主(主主)架构配置方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 快速解决mysql导出scv文件乱码、蹿行的问题

    快速解决mysql导出scv文件乱码、蹿行的问题

    这篇文章主要介绍了快速解决mysql导出scv文件乱码、蹿行的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • MySQL 不允许从远程访问的解决方法

    MySQL 不允许从远程访问的解决方法

    MySQL 不允许从远程访问的原因有很多除了下面的方法,还有需要看服务器安全设置禁止访问本机的3306端口。
    2010-03-03
  • 如何用mysql自带的定时器定时执行sql(每天0点执行与间隔分/时执行)

    如何用mysql自带的定时器定时执行sql(每天0点执行与间隔分/时执行)

    在开发过程中经常会遇到这样一个问题,每天或者每月必须定时去执行一条sql语句或更新或删除或执行特定的sql语句,下面这篇文章主要给大家介绍了关于如何用mysql自带的定时器定时执行sql(每天0点执行与间隔分/时执行)的相关资料,需要的朋友可以参考下
    2023-03-03
  • MySQL中like模糊查询的优化方法小结

    MySQL中like模糊查询的优化方法小结

    本文介绍了五种优化MySQL中like模糊查询的方法,主要包含后缀匹配走索引、反向索引、缩小搜索范围、使用缓存和借助全文搜索引擎这几种,感兴趣的可以了解一下
    2024-11-11
  • MySQL分表和分区的具体实现方法

    MySQL分表和分区的具体实现方法

    这篇文章主要介绍了MySQL分表和分区的具体实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-06-06
  • mysql表分区的方式和实现代码示例

    mysql表分区的方式和实现代码示例

    通俗地讲表分区是将一个大表,根据条件分割成若干个小表,下面这篇文章主要给大家介绍了关于mysql表分区的方式和实现代码,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-02-02

最新评论