悲观锁&乐观锁

!!! note 目录

悲观锁&乐观锁

一、悲观锁(行级锁)

  • 行级锁就像是保护数据库里每一行数据的小保险箱。
  • 当一个人要对某一行数据做操作时(比如修改或者读取),他需要先打开这个保险箱,这时别人就不能同时操作这一行数据,直到他完成操作并关闭保险箱。这样可以确保每一行数据都不会被多人同时改动,避免混乱和错误。

1.1 悲观锁示例

假设我们有一个 users 表,包含 idname 两列,现在我们想要更新其中一行的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 创建 users 表
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);

-- 向 users 表插入一些数据
INSERT INTO users (id, name) VALUES (1, 'Alice');
INSERT INTO users (id, name) VALUES (2, 'Bob');
INSERT INTO users (id, name) VALUES (3, 'Charlie');

-- 开始一个事务
START TRANSACTION;

-- 选择并锁定 id 为 1 的行,这里使用 FOR UPDATE 来获取行级锁
SELECT * FROM users WHERE id = 1 FOR UPDATE;

-- 更新 id 为 1 的用户的名称
UPDATE users SET name = 'Alice Smith' WHERE id = 1;

-- 提交事务
COMMIT;

在这个示例中,SELECT ... FOR UPDATE 语句用于获取对 id 为 1 的用户行的行级锁,以确保在事务结束前,其他事务不能修改该行数据。
使用行级锁可以确保数据的一致性和完整性,避免多个事务同时修改同一行数据可能导致的问题。

当一个事务获取了行级锁并且还没有释放该锁之前,其他事务是无法对被锁定的行进行修改的。
关键词:for update

二、乐观锁

  • 乐观锁是一种并发控制机制,常用于数据库管理系统中。

  • 在乐观锁中,假设多个事务之间的冲突是不常见的,因此它们尽可能地允许并发执行。

  • 它的核心思想是在事务提交之前不锁定数据,而是在事务提交时检查数据是否被其他事务修改过。
    如果检测到冲突,通常会回滚事务并重试。

  • 在实践中,乐观锁通常通过在数据表中添加一个版本号或时间戳字段来实现。

  • 每次更新数据时,版本号或时间戳会相应地增加。

  • 当事务提交时,系统会比较提交时的版本号或时间戳与事务开始时读取的版本号或时间戳是否一致,如果不一致,则意味着有其他事务修改了数据,事务需要进行回滚和重试。

乐观锁适用于读操作频繁、写操作相对较少的场景,以及对数据一致性要求不是特别严格的情况。它可以提高系统的并发性能,但需要开发人员注意处理并发冲突的情况,以及适当处理重试机制。