返回

悲观锁和乐观锁:MySQL 中的并发控制机制

闲谈

MySQL 中的悲观锁与乐观锁

在 MySQL 数据库中,并发控制是至关重要的,它确保了多个用户同时访问数据时的正确性和一致性。其中,悲观锁和乐观锁是两种不同的并发控制机制,它们提供了不同的方法来处理并发访问。

悲观锁

悲观锁的理念是“假设最坏的情况”,即在执行事务之前就对数据进行锁定。这确保了事务在执行期间不会被其他并发事务修改。

在 MySQL 中,悲观锁通过 SELECT ... FOR UPDATE 语句实现。当一个事务使用 FOR UPDATE 子句查询数据时,它会自动在所查询的数据行上获取排他锁。这阻止了其他事务同时更新或删除这些行,直到当前事务提交或回滚。

优点:

  • 防止脏读和不可重复读: 通过在事务开始时锁定数据,悲观锁可以防止其他事务读取未提交的数据(脏读)或多次读取相同数据并获得不同的结果(不可重复读)。
  • 保证数据完整性: 悲观锁确保了事务在执行期间不受并发操作的影响,从而保证了数据的一致性和完整性。

缺点:

  • 可能导致死锁: 当两个或多个事务同时锁定同一行数据时,可能会发生死锁。
  • 降低并发性: 悲观锁会限制并发性,因为一个事务在锁定数据期间,其他事务无法访问这些数据。

乐观锁

乐观锁的理念是“假设最好的情况”,即在执行事务之前不锁定数据。而是,在事务提交时,它检查数据自事务开始以来是否被修改。如果数据已被修改,则事务将回滚。

在 MySQL 中,乐观锁通常通过 WHERE 子句中的版本号或时间戳实现。事务在开始时获取数据的版本号或时间戳,并在提交时将其与数据库中当前的版本号或时间戳进行比较。如果版本号或时间戳不匹配,则事务将回滚。

优点:

  • 提高并发性: 乐观锁允许并发事务在不互相阻塞的情况下同时访问数据,从而提高了并发性。
  • 避免死锁: 乐观锁不会产生死锁,因为它不使用锁定。

缺点:

  • 可能导致脏读和不可重复读: 由于乐观锁不锁定数据,因此它不能防止脏读或不可重复读。
  • 可能导致更新冲突: 当多个事务同时尝试更新同一行数据时,可能会发生更新冲突。

选择悲观锁还是乐观锁

选择悲观锁还是乐观锁取决于应用程序的具体需求。

  • 对于需要强数据一致性的应用程序, 悲观锁是一个更好的选择。
  • 对于需要高并发性的应用程序, 乐观锁是一个更好的选择。

在 MySQL 中,还可以通过设置隔离级别来调整悲观锁和乐观锁的行为。

示例:

悲观锁

BEGIN TRANSACTION;
SELECT * FROM table_name FOR UPDATE;
-- 执行更新操作
COMMIT;

乐观锁

SELECT * FROM table_name WHERE version = 1;
-- 执行更新操作
IF @@ROWCOUNT > 0 THEN
  -- 数据未被修改,提交事务
  COMMIT;
ELSE
  -- 数据已被修改,回滚事务
  ROLLBACK;
END IF;