面试题:MySQL 的乐观锁和悲观锁是什么?

在 MySQL 中,乐观锁悲观锁是两种常见的并发控制机制,用于解决多个事务同时操作相同数据时可能引发的冲突问题。它们的主要区别在于对数据冲突的处理方式。


1. 悲观锁(Pessimistic Locking)

1.1 概念

  • 悲观锁假设数据冲突很可能会发生,因此在访问数据时直接加锁,确保其他事务无法修改数据,直到当前事务完成。
  • 适用于写操作较多的场景。

1.2 实现方式

在 MySQL 中,悲观锁通常通过以下方式实现:

  • SELECT ... FOR UPDATE:对查询结果加排他锁(X 锁),其他事务无法修改或加锁。
  • SELECT ... LOCK IN SHARE MODE:对查询结果加共享锁(S 锁),其他事务可以读取但不能修改。

1.3 示例

-- 事务 1
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 加排他锁
UPDATE users SET balance = balance - 100 WHERE id = 1;
COMMIT;

-- 事务 2
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 等待事务 1 释放锁
UPDATE users SET balance = balance + 100 WHERE id = 1;
COMMIT;

1.4 优缺点

  • 优点
    • 保证数据一致性,避免冲突。
  • 缺点
    • 加锁会降低并发性能。
    • 可能导致死锁。

2. 乐观锁(Optimistic Locking)

2.1 概念

  • 乐观锁假设数据冲突不太可能发生,因此在访问数据时不加锁,而是在提交事务时检查数据是否被其他事务修改过。
  • 适用于读操作较多的场景。

2.2 实现方式

乐观锁通常通过以下方式实现:

  • 版本号(Version):在表中增加一个版本号字段,每次更新数据时版本号加 1。
  • 时间戳(Timestamp):在表中增加一个时间戳字段,每次更新数据时更新时间戳。

2.3 示例

-- 表结构
CREATE TABLE users (
    id INT PRIMARY KEY,
    balance DECIMAL(10, 2),
    version INT DEFAULT 0
);

-- 事务 1
START TRANSACTION;
SELECT balance, version FROM users WHERE id = 1; -- 读取当前余额和版本号
-- 假设读取到的 balance = 1000, version = 1
UPDATE users SET balance = balance - 100, version = version + 1 
WHERE id = 1 AND version = 1; -- 检查版本号
COMMIT;

-- 事务 2
START TRANSACTION;
SELECT balance, version FROM users WHERE id = 1; -- 读取当前余额和版本号
-- 假设读取到的 balance = 1000, version = 1
UPDATE users SET balance = balance + 100, version = version + 1 
WHERE id = 1 AND version = 1; -- 检查版本号
COMMIT;
  • 如果事务 1 先提交,事务 2 的 UPDATE 会失败(因为版本号已变为 2),此时事务 2 需要重试。

2.4 优缺点

  • 优点
    • 不加锁,提高并发性能。
    • 适用于读多写少的场景。
  • 缺点
    • 需要处理冲突(如重试机制)。
    • 实现复杂度较高。

3. 乐观锁 vs 悲观锁

特性乐观锁悲观锁
加锁时机提交时检查冲突访问数据时加锁
实现方式版本号或时间戳SELECT ... FOR UPDATE
适用场景读多写少写多读少
性能高并发性能低并发性能
冲突处理需要处理冲突(如重试)直接加锁,避免冲突
复杂度实现较复杂实现简单

4. 选择建议

  • 使用悲观锁的场景
    • 数据冲突概率较高。
    • 写操作较多。
    • 需要强一致性保证。
  • 使用乐观锁的场景
    • 数据冲突概率较低。
    • 读操作较多。
    • 需要高并发性能。

5. 总结

  • 悲观锁:通过加锁避免冲突,适合写多读少的场景,但可能降低并发性能。
  • 乐观锁:通过版本号或时间戳检测冲突,适合读多写少的场景,但需要处理冲突。

根据实际业务场景选择合适的锁机制,可以有效提升数据库的并发性能和数据一致性。

THE END
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容