0

0

mysql如何使用悲观锁保证数据安全

P粉602998670

P粉602998670

发布时间:2025-09-22 09:58:01

|

881人浏览过

|

来源于php中文网

原创

答案:MySQL悲观锁通过SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE在事务中锁定数据,防止并发修改,确保数据一致性;适用于库存扣减、资金转账等高一致性要求场景,但需注意死锁预防和性能优化。

mysql如何使用悲观锁保证数据安全

在MySQL中,要使用悲观锁来保证数据安全,核心思路是在对数据进行操作前,就预先锁定它,确保在当前事务完成之前,其他任何事务都无法修改或读取到未提交的数据。这就像你走进图书馆,拿起一本书,在开始阅读之前就给它贴上“已借阅”的标签,别人就不能再拿走它了。这种“先占为王”的策略,是防止并发操作导致数据不一致的有效手段。

解决方案

在MySQL(特别是InnoDB存储引擎)中,我们主要通过

SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE
这两种语句来实现悲观锁。

SELECT ... FOR UPDATE
是排他锁(Exclusive Lock),它会锁定查询到的行,阻止其他事务对这些行进行读取(如果是
LOCK IN SHARE MODE
)或修改(无论是
FOR UPDATE
还是普通修改)。这意味着,一旦一个事务对某行数据使用了
FOR UPDATE
,其他事务就必须等待,直到这个事务提交(
COMMIT
)或回滚(
ROLLBACK
),锁才会被释放。这在需要修改数据,比如扣减库存、资金转账等场景中至关重要。

SELECT ... LOCK IN SHARE MODE
是共享锁(Shared Lock),它允许其他事务同时获取共享锁来读取这些行,但会阻止任何事务获取排他锁(即
FOR UPDATE
或进行修改操作)。这适用于读多写少,但需要保证读取数据一致性的场景。

通常,在涉及数据修改并需要严格并发控制时,我们更多地会用到

FOR UPDATE

一个简单的库存扣减示例:

假设我们有一个

products
表,其中包含
id
stock
字段。

START TRANSACTION;

-- 查询并锁定商品ID为1的库存,防止其他事务同时修改
SELECT stock FROM products WHERE id = 1 FOR UPDATE;

-- 假设当前库存是10,要扣减1
UPDATE products SET stock = stock - 1 WHERE id = 1;

-- 模拟业务逻辑处理...

-- 如果一切顺利,提交事务,释放锁
COMMIT;

在这个例子中,当第一个事务执行

SELECT ... FOR UPDATE
时,
id = 1
的行就被锁定了。如果此时有另一个事务也尝试对
id = 1
的行执行
SELECT ... FOR UPDATE
UPDATE
操作,它就会被阻塞,直到第一个事务完成。这确保了库存扣减的原子性和一致性。

需要注意的是,悲观锁必须在事务中才能生效。如果不在事务中,

FOR UPDATE
LOCK IN SHARE MODE
语句执行后会立即释放锁,起不到应有的作用。

MySQL悲观锁与乐观锁有何不同,我该如何选择?

在我看来,这是数据库并发控制中最常遇到的哲学问题:你是选择“相信”冲突不会发生(乐观锁),还是“假定”冲突一定会发生(悲观锁)?

悲观锁,就像前面提到的,它在操作数据之前就“悲观”地认为会有其他事务来捣乱,所以提前把数据锁住,确保自己独占。它的优点是数据一致性非常强,几乎可以百分百保证。但缺点也很明显:性能开销大,因为锁会阻塞其他事务;如果锁粒度过大或持有时间过长,很容易造成死锁或降低系统并发能力。

乐观锁则相反,它“乐观”地认为冲突不会经常发生。它不会在操作前加锁,而是在提交更新时,通过某种机制(比如版本号或时间戳)来检查数据是否在读取后被其他事务修改过。如果发现冲突,就回滚事务并重试。它的优点是并发性高,没有锁的开销,适用于读多写少的场景。但缺点是,如果冲突频繁,会导致大量事务回滚重试,反而降低性能,并且实现起来需要应用程序层面的支持,比悲观锁更复杂一些。

企业网站系统清新版
企业网站系统清新版

企业网站系统,方便您建立网站;强大的网站管理系统,只要会打字,就可随时管理网站内容! 轻松拥有一个属于自己的商务网站,全方位展示你的产品,在线订购,轻松管理。 系统采用全后台管理方式,后台功能齐全,使用维护方便。 后台主要功能如下: 1、系统管理:管理员管理,可以新增管理员及修改管理员密码;数据库备份,为保证您的数据安全本系统采用了数据库备份功能;上传文件管理,管理你增加产品时上传的图片及其他文件

下载

如何选择? 这真的没有标准答案,更多是根据具体业务场景和对系统性能、数据一致性要求的权衡。

  • 高并发、低冲突的场景(比如商品浏览量计数、社交媒体点赞): 乐观锁通常是更好的选择。它能提供更好的吞吐量。
  • 低并发、高冲突的场景(比如银行转账、库存扣减、秒杀系统): 悲观锁可能更合适,或者至少在核心业务逻辑中使用。在这些场景下,数据的一致性和准确性是压倒一切的,宁愿牺牲一些并发性也要保证数据正确。
  • 混合场景: 甚至可以混合使用。例如,在用户下单时,库存扣减用悲观锁,而订单状态的更新可能用乐观锁(通过版本号)。

我个人的经验是,在设计系统时,如果对某个核心数据操作的并发冲突有疑虑,或者数据一致性要求极高,我会倾向于先考虑悲观锁,并尽量缩小锁的范围和持有时间。如果后续发现性能瓶颈,再考虑优化为乐观锁或结合其他并发控制策略。

使用MySQL悲观锁时,如何避免死锁和提升性能?

悲观锁虽然能保证数据安全,但它带来的副作用也不容忽视,尤其是死锁和性能问题。我见过不少因为不恰当使用悲观锁而导致系统雪崩的案例。所以,如何“用好”它,比“会不会用”更重要。

避免死锁: 死锁通常发生在两个或多个事务互相等待对方释放资源时。在悲观锁的语境下,就是事务A锁住了资源X,想获取资源Y;同时事务B锁住了资源Y,想获取资源X。它们就这么僵持住了。

  1. 保持一致的加锁顺序: 这是避免死锁最有效的方法之一。如果所有事务都以相同的顺序获取锁,那么死锁的概率会大大降低。比如,如果一个事务需要同时锁定订单和商品,那么就规定总是先锁订单,再锁商品。
  2. 缩短事务,减少锁持有时间: 事务越短,锁被持有的时间就越短,其他事务等待的时间就越少,死锁的机会也就越小。尽量只在事务中包含必要的数据库操作。
  3. 使用
    innodb_lock_wait_timeout
    MySQL的InnoDB引擎有一个参数
    innodb_lock_wait_timeout
    ,可以设置事务等待锁的超时时间。如果一个事务等待锁的时间超过这个值,MySQL会自动回滚这个事务。虽然不能完全避免死锁,但可以避免事务无限期等待,提供一种“止损”机制。
  4. MySQL 8.0+ 的
    NOWAIT
    SKIP LOCKED
    这两个选项非常实用。
    • SELECT ... FOR UPDATE NOWAIT
      : 如果查询的行已经被其他事务锁定,则立即返回错误,而不是等待。这允许应用层处理冲突,比如提示用户稍后再试。
    • SELECT ... FOR UPDATE SKIP LOCKED
      : 如果查询的行被锁定,则跳过这些行,只返回未锁定的行。这在某些批量处理场景中非常有用,可以处理部分数据而不被阻塞。

提升性能:

  1. 缩小锁的范围(行级锁): InnoDB默认是行级锁,这是好事。确保你的查询条件能够命中索引,这样MySQL就能精确地锁定所需的行,而不是锁定整个表或大范围的索引。如果
    WHERE
    条件没有命中索引,可能会升级为表锁,那性能就彻底完蛋了。
  2. 只锁定必要的数据: 不要为了安全而锁定整个表。只锁定你真正需要修改或保护的那些行。
  3. 合理设置事务隔离级别: 不同的隔离级别对锁的行为有影响。例如,
    READ COMMITTED
    隔离级别下,非锁定读不会使用共享锁,可以减少一些锁冲突。但也要注意,这可能会引入其他一致性问题,需要仔细权衡。
  4. 监控锁情况: 使用
    SHOW ENGINE INNODB STATUS
    命令可以查看当前的锁等待、死锁等信息,帮助你分析和定位问题。定期监控这些指标,是优化悲观锁性能的关键。

MySQL悲观锁的实际应用场景有哪些,它真的安全吗?

悲观锁在很多对数据一致性要求极高的核心业务场景中扮演着不可或缺的角色。它就像一个严谨的守卫,确保在关键时刻,数据不会被并发的“闯入者”破坏。

实际应用场景:

  1. 库存扣减: 这是最经典的场景。电商平台在用户下单时,需要扣减商品库存。如果多个用户同时购买同一件商品,没有锁的保护,可能会导致库存超卖,产生严重的业务问题。
    SELECT ... FOR UPDATE
    可以确保在扣减库存时,当前库存是准确的,并且其他并发请求需要等待。
  2. 资金转账: 银行系统中的资金划拨,从一个账户扣款,向另一个账户加款。这涉及到两个账户的余额修改,必须保证原子性。如果A账户扣款成功,B账户加款失败,那资金就凭空消失了。通过锁定两个账户的行,可以避免并发转账导致的数据不一致。
  3. 订单状态更新: 订单从“待支付”到“已支付”,再到“已发货”等状态流转。如果多个操作(比如用户支付和客服修改订单)同时修改订单状态,可能导致状态混乱。悲观锁可以确保订单状态更新的唯一性和正确性。
  4. 秒杀系统: 在高并发的秒杀活动中,抢购商品的逻辑往往需要悲观锁来确保商品库存的正确扣减,以及防止一人多抢等情况。
  5. 唯一性资源分配: 比如优惠券、序列号等一次性资源的分配,悲观锁可以确保每个资源只被一个用户成功获取。

它真的安全吗?

是的,从数据库层面来看,悲观锁在正确使用的情况下,能够非常有效地保证数据在并发操作下的安全性和一致性。 它通过强制串行化对共享资源的访问,从根本上杜绝了脏读、不可重复读和幻读等并发问题(至少对于被锁定的数据而言),确保了事务的隔离性。

然而,需要强调的是,“安全”是一个多维度的概念。悲观锁保证的是数据库层面的数据并发安全,它并不能解决所有安全问题:

  • 业务逻辑错误: 如果你的业务逻辑本身有bug,比如扣减库存时计算错误,悲观锁是无法阻止这种错误的。
  • 应用层问题: 悲观锁是数据库提供的机制,如果应用层没有正确地开启和管理事务,或者在事务中做了太多不相干的、长时间的操作,依然可能导致问题。
  • 系统故障: 如果数据库服务器突然崩溃,正在进行的事务可能会丢失,锁也随之释放,这需要通过持久化和恢复机制来保障数据安全。
  • 死锁问题: 虽然悲观锁保证了数据一致性,但如果使用不当导致死锁,反而会影响系统的可用性。

所以,悲观锁是一个强大的工具,但它不是万能药。它的安全性建立在正确理解、正确实现和正确管理的基础之上。在使用悲观锁时,我们必须同时关注业务逻辑的严谨性、事务管理的合理性以及系统性能的监控,才能真正构建一个健壮且安全的应用。

相关专题

更多
mysql修改数据表名
mysql修改数据表名

MySQL修改数据表:1、首先查看数据库中所有的表,代码为:‘SHOW TABLES;’;2、修改表名,代码为:‘ALTER TABLE 旧表名 RENAME [TO] 新表名;’。php中文网还提供MySQL的相关下载、相关课程等内容,供大家免费下载使用。

652

2023.06.20

MySQL创建存储过程
MySQL创建存储过程

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句调用存储过程智能用输出变量返回值。函数可以从语句外调用(通过引用函数名),也能返回标量值。存储过程也可以调用其他存储过程。php中文网还提供MySQL创建存储过程的相关下载、相关课程等内容,供大家免费下载使用。

244

2023.06.21

mongodb和mysql的区别
mongodb和mysql的区别

mongodb和mysql的区别:1、数据模型;2、查询语言;3、扩展性和性能;4、可靠性。本专题为大家提供mongodb和mysql的区别的相关的文章、下载、课程内容,供大家免费下载体验。

280

2023.07.18

mysql密码忘了怎么查看
mysql密码忘了怎么查看

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql密码忘了怎么办呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

513

2023.07.19

mysql创建数据库
mysql创建数据库

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql怎么创建数据库呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

250

2023.07.25

mysql默认事务隔离级别
mysql默认事务隔离级别

MySQL是一种广泛使用的关系型数据库管理系统,它支持事务处理。事务是一组数据库操作,它们作为一个逻辑单元被一起执行。为了保证事务的一致性和隔离性,MySQL提供了不同的事务隔离级别。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

384

2023.08.08

sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

522

2023.08.11

mysql忘记密码
mysql忘记密码

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。那么忘记mysql密码我们该怎么解决呢?php中文网给大家带来了相关的教程以及其他关于mysql的文章,欢迎大家前来学习阅读。

594

2023.08.14

桌面文件位置介绍
桌面文件位置介绍

本专题整合了桌面文件相关教程,阅读专题下面的文章了解更多内容。

0

2025.12.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 1.5万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 777人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号