在 MySQL 中,乐观锁和悲观锁是两种处理并发控制的方法,它们分别适用于不同的场景,并以不同的方式来保证数据的一致性和完整性。
悲观锁(Pessimistic Locking)
概念:
悲观锁假设会发生并发冲突,因此在操作数据时会直接将数据锁定,防止其他事务对相同数据进行修改,直到当前事务完成。这种方式通常通过数据库的行级锁定机制实现。
使用场景:
- 当预期会有大量的并发写操作时。
- 对于关键业务数据的更新,要求高度的数据一致性。
实现方式:
- 在 MySQL 的 InnoDB 存储引擎中,可以通过
SELECT ... FOR UPDATE
或者SELECT ... LOCK IN SHARE MODE
来实现悲观锁。 SELECT ... FOR UPDATE
:会对查询到的记录加排他锁,阻止其他事务对该记录进行读取或更新操作,直到当前事务提交或者回滚。SELECT ... LOCK IN SHARE MODE
:会对查询到的记录加共享锁,允许其他事务读取这些记录但阻止他们进行更新,直到当前事务结束。
示例:
-- 使用 FOR UPDATE 锁定选定的行
BEGIN;
SELECT * FROM accounts WHERE id = 123 FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = 123;
COMMIT;
乐观锁(Optimistic Locking)
概念:
乐观锁则假定不会发生并发冲突,在读取数据时不加锁,而是在提交更新时检查是否有其他事务已经修改了该数据。如果检测到冲突,则拒绝本次更新并提示用户重试操作。
使用场景:
- 当系统中的读操作远远多于写操作时。
- 对于那些可以接受偶尔的冲突重试的情况。
实现方式:
- 乐观锁通常通过版本号(Version Number)或时间戳(Timestamp)机制实现。每次更新数据时都会检查版本号是否一致,如果不一致则说明数据已被其他事务修改过。
- 版本号字段需要在表结构中预先定义好,并且每次更新数据时都要同时更新这个版本号。
示例:
假设有一个表包含一个 version
字段用于实现乐观锁:
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(255),
version INT DEFAULT 0
);
-- 更新时检查版本号
UPDATE products
SET price = 200, version = version + 1
WHERE id = 1 AND version = 1; -- 假设当前版本为1
在这个例子中,只有当 id=1
的产品版本号仍为 1 时,更新才会成功;否则,表示有其他事务已修改了这条记录,更新失败。
总结
- 悲观锁适合于写操作频繁的场景,能够确保数据的一致性,但可能导致较高的锁竞争,降低系统的并发性能。
- 乐观锁更适合于读多写少的环境,它减少了锁的竞争,提高了并发度,但在高冲突率的情况下可能导致更多的重试次数。
选择哪种锁策略取决于具体的业务需求、系统的并发特性以及对数据一致性的要求。
THE END