mysql 悲观锁和乐观锁(—悲观锁)

适合悲观锁的使用场景:

悲观锁更适合在,写操作较多、并发冲突高、业务需要强一致性等场景下使用悲观锁。

如何使用悲观锁:

悲观锁主要通过以下两个 SQL语句实现:

1、SELECT...FOR UPDATE;

这个语句会在所查询中的数据行上设置排他锁(Exclusive Lock)。在数据被锁定期间,其他事务无法修改这些被选中的行数据,也无法对他们设置新的排它锁和共享锁。

eg: 当sql语句运行至2的时候,事务已被成功开启且对指定行数据加上了排他锁,这时候,在别的进程/窗口中再对id=1的数据进行update修改命令的话,会执行失败,因为数据已经被锁定了。这时候只有当前进程/窗口可以对该数据进行编辑操作,编辑完成后。执行至语句4,提交并关闭了事务后,其他窗口对id=1的update的编辑操作才能有效果。

START TRANSACTION;  // 1、开启事务

SELECT * FROM students WHERE id=1 FOR UPDATE;//2、查询指定行数据并加排它锁

UPDATE students set name='小明' where id=1; // 3、修改数据

COMMIT;    // 4、提交并关闭事务

2、SELECT...LOCK IN SHARE MODE;

这个 语句会在所查询的数据行上设置共享锁(Shared Lock)。在被锁定期间,其他事务可以读取这些行,但不能修改这些行, 也不能在这些数据行上这设置排他锁。

eg: 当sql语句运行至2的时候,事务已被成功开启且对指定行数据加上了共享锁,这时候,在别的进程/窗口中再对id=1的数据进行查询操作是能查询到对应的数据的,但若进行update修改命令的话,则会执行失败,因为数据已经被锁定了。这时候只有当前进程/窗口可以对该数据进行编辑操作,编辑完成后。执行至语句4,提交并关闭了事务后,其他窗口对id=1的update的编辑操作才能有效果。

START TRANSACTION;

SELECT * FROM students WHERE id=1 LOCK IN SHARE MODE;

UPDATE students set name='小明' where id=1;

COMMIT;


注意事项:

悲观锁的缺点:

        1、增加性能开销:对数据的读写进行加锁和解锁操作,会增加系统的开销,特别是高并发的环境下,锁的竞争会严重影响到系统性能。所以悲观锁机制对资源的锁定操作会影响系统性能。

        2、降低并发度:悲观锁在操作数据前会加锁,这导致了同一时间,只有一个事务能操作数据,其他事务只能等待,这严重降低了系统的并发性。

        3、死锁:当遇上多个事务相互等待对方释放锁时,就可能发生死锁。虽然数据库系统通常能够检测并解决死锁,但这回导致食物回滚,增加系统的开销。

        4、锁超时:如果一个食物长时间加锁而不释放,可能导致其他等待锁的事务超时,这不仅导致等待的事务失败,还可能印象到整个系统的稳定性。

<think>我们正在讨论MySQL中的悲观锁乐观锁。根据引用[1],按照锁的思想划分,可以分为乐观锁悲观锁。用户希望理解它们的定义、区别以及使用场景。 首先,定义: 1. 悲观锁:总是假设最坏的情况,每次操作数据时都认为别人会修改,所以每次操作时都会上锁,这样别人想操作这个数据就会阻塞直到它拿到锁。在MySQL中,典型的实现是通过`SELECT ... FOR UPDATE`语句加排他锁(X锁),或者使用`SELECT ... LOCK IN SHARE MODE`加共享锁(S锁)[^2]。 2. 乐观锁:相对乐观,认为操作数据时不会发生冲突,所以不上锁。而是在提交更新时,检查在此期间是否有别人修改过数据。通常使用版本号(version)或时间戳来实现。 区别: - 加锁方式:悲观锁在操作数据前加锁(数据库层面的锁);乐观锁则没有实际的加锁,而是通过版本号控制。 - 使用场景:悲观锁适用于写操作多的场景,保证数据安全;乐观锁适用于读操作多的场景,减少加锁的开销,提高吞吐量。 - 性能:悲观锁在高并发写时性能较差(因为等待锁);乐观锁在高并发写时可能冲突较多,导致重试,但读操作性能好。 使用场景: - 悲观锁:当业务逻辑中包含需要严格保证数据一致性的写操作,例如银行转账、库存扣减(避免更新丢失,如引用[3]描述的问题)等。 - 乐观锁:适用于读多写少,冲突较少的场景,例如一些并发量不是很大的更新操作,或者可以接受重试的场景。例如,在更新用户信息时,可以通过版本号控制,避免更新覆盖。 下面我们详细说明: ### 悲观锁MySQL中,悲观锁通常通过事务行级锁来实现。例如: ```sql BEGIN; SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- 加排他锁 -- 然后进行更新操作 UPDATE accounts SET balance = balance - 100 WHERE id = 1; COMMIT; ``` 这样,在事务提交前,其他事务不能修改id=1的记录,从而防止更新丢失。 ### 乐观锁 乐观锁不在数据库层面加锁,而是在表中增加一个版本号字段(如`version`)或时间戳。更新时检查版本号是否改变: ```sql -- 假设有一个version字段 UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = old_version; ``` 如果更新行数为0,则表示版本号已被修改,此次更新失败,需要重试或放弃。 ### 总结 - 悲观锁:强一致,牺牲性能;乐观锁:最终一致,性能较好。 - 选择哪种锁取决于应用场景:对数据冲突概率高的写操作用悲观锁;冲突概率低的用乐观锁。 §§相关问题§§ 1. 在MySQL中如何具体实现悲观锁? 2. 乐观锁的实现中,除了版本号还有哪些方法? 3. 悲观锁乐观锁在高并发场景下各自的优缺点是什么? 4. 如何避免乐观锁的ABA问题?</think>### MySQL悲观锁乐观锁详解 #### 悲观锁定义与原理 悲观锁(Pessimistic Locking)基于"先加锁再访问"的假设,认为并发操作很可能会发生冲突。在MySQL中实现方式包括: 1. **排他锁(X锁)**:通过`SELECT ... FOR UPDATE`语句实现,阻止其他事务读写锁定行[^1] ```sql BEGIN; SELECT * FROM accounts WHERE id=1 FOR UPDATE; -- 加排他锁 UPDATE accounts SET balance=balance-100 WHERE id=1; COMMIT; ``` 2. **共享锁(S锁)**:通过`SELECT ... LOCK IN SHARE MODE`实现,允许其他事务读但禁止写[^2] ```sql SELECT * FROM products WHERE id=5 LOCK IN SHARE MODE; ``` #### 乐观锁定义与原理 乐观锁(Optimistic Locking)基于"冲突概率低"的假设,在提交时检测冲突。核心实现方式: 1. **版本号机制**:表中增加`version`字段 ```sql UPDATE orders SET status='paid', version=version+1 WHERE id=100 AND version=5; -- 提交时检查版本 ``` 2. **时间戳机制**:使用`last_modified`时间戳字段 ```sql UPDATE inventory SET quantity=quantity-1, last_modified=NOW() WHERE product_id=10 AND last_modified='2023-06-15 10:00:00'; ``` #### 核心区别对比 | 特性 | 悲观锁 | 乐观锁 | |--------------|---------------------------|---------------------------| | 加锁时机 | 操作前加锁 | 提交时检查 | | 实现方式 | 数据库原生锁机制[^1] | 应用层版本控制 | | 并发性能 | 高并发下性能差 | 高并发下性能好 | | 冲突处理 | 阻塞等待 | 回滚/重试机制 | | 适用场景 | 短事务/强一致性要求 | 长事务/高并发读 | | 典型应用 | 银行转账[^3] | 商品库存更新 | #### 使用场景分析 **悲观锁适用场景**: 1. 资金交易系统(如转账操作,避免丢失更新[^3]) 2. 票务系统的座位锁定 3. 需要强一致性的库存扣减(如限量商品) **乐观锁适用场景**: 1. 高并发读多写少场景(如新闻点赞) 2. 业务逻辑允许重试的操作 3. 分布式系统跨数据库事务 4. 长事务操作(如复杂审批流程) #### 实战选择建议 - 当**冲突频率 > 20%** 时选择悲观锁 - 当**系统吞吐量要求高**时优先乐观锁 - 对**数据一致性要求极端严格**的用悲观锁 - **分布式环境**倾向乐观锁+重试机制 > 示例场景:电商库存扣减 > - 悲观锁方案:`FOR UPDATE`锁定商品行,避免超卖 > - 乐观锁方案:版本号更新,失败后前端提示"库存已变化"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值