MySQL 悲观锁与乐观锁的实现

在数据库并发控制中,悲观锁和乐观锁是两种主要的锁机制。它们各自适用于不同的场景,了解它们的区别及实现方法对开发人员来说至关重要。

悲观锁

悲观锁的思想是在进行数据操作前,总是假设会发生冲突,因此在操作之前就对数据加锁,以防止其他事务修改数据。MySQL中,可以通过SELECT ... FOR UPDATE语句来实现悲观锁。

悲观锁示例

以下是一个使用悲观锁的代码示例:

BEGIN;

-- 悲观锁:对某条记录加锁
SELECT * FROM accounts WHERE account_id = 1 FOR UPDATE;

-- 执行一些操作
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;

COMMIT;

在以上示例中,首先启动了一个事务,然后使用FOR UPDATEaccount_id为1的记录加锁。在此期间,其他事务不能对这条记录进行修改,直到当前事务提交。

乐观锁

乐观锁的思想是,假设不会发生冲突,因此在操作时不加锁,而是在提交时检查是否有其他事务修改过数据。如果有冲突,则回滚,需要重新尝试。乐观锁通常通过版本字段来实现。

乐观锁示例

以下是一个使用乐观锁的代码示例:

BEGIN;

-- 获取当前记录及其版本号
SELECT balance, version FROM accounts WHERE account_id = 1;

-- 假设进行一些操作
UPDATE accounts SET balance = balance - 100, version = version + 1 
WHERE account_id = 1 AND version = current_version;

IF ROW_COUNT() = 0 THEN
    -- 版本不匹配,可能有其他事务修改过数据
    ROLLBACK;
ELSE
    COMMIT;
END IF;

在这个示例中,首先获取了记录和其版本号。然后在更新时,条件中要求版本匹配。如果没有行被更新,说明数据已经被其他事务修改,则需要回滚。

类图与ER图

理解悲观锁与乐观锁的使用场景,可以通过类图和ER图进行辅助说明。

类图

classDiagram
    class Account {
        +int account_id
        +double balance
        +int version
        +void mustLock()
        +void optimisticLock()
    }

在类图中,Account类包含了账户ID、余额及版本信息,并定义了两个方法:mustLock()用于悲观锁,optimisticLock()用于乐观锁。

ER图

erDiagram
    ACCOUNTS {
        int account_id PK
        double balance
        int version
    }

ER图描述了ACCOUNTS表的结构,包含账户ID、余额和版本信息。account_id是主键。

小结

悲观锁和乐观锁各有其适用的场景。悲观锁适合于写操作频繁的环境,因为它防止了脏读和不可重复读。而乐观锁则适合读操作多的环境,因其对性能的影响较小。选择合适的锁策略能够有效提升数据库的并发性能和系统的稳定性。希望通过本文的讲解,能让你对这两种锁有更深入的了解,并能够根据需求做出合适的选择。