乐观锁(Optimistic Locking)和悲观锁(Pessimistic Locking)是两种常用的并发控制策略,用于处理多个事务对同一数据的并发访问问题。它们的主要区别在于对数据锁定的方式和处理冲突的策略。
悲观锁(Pessimistic Locking)
定义: 悲观锁是一种假设在事务执行期间数据将被多个事务并发修改的策略。为了防止数据冲突,悲观锁在数据访问时采取了严格的锁定措施。
特点:
- 锁定数据:在事务开始时,对要操作的数据加锁。这样可以确保在事务完成之前,没有其他事务可以修改这些数据。
- 数据一致性:通过加锁,避免了数据被其他事务修改,确保数据在事务期间的一致性。
- 性能开销:由于加锁,可能导致其他事务等待锁释放,这可能会影响系统的并发性能。
示例: 在 MySQL 中,悲观锁通常通过 SELECT ... FOR UPDATE
或 LOCK TABLES
实现。例如:
BEGIN;
-- 悲观锁定数据
SELECT * FROM orders WHERE id = 1 FOR UPDATE;
-- 执行更新操作
UPDATE orders SET status = 'processed' WHERE id = 1;
COMMIT;
乐观锁(Optimistic Locking)
定义: 乐观锁是一种假设在事务执行期间数据不会被多个事务并发修改的策略。乐观锁不直接锁定数据,而是通过在事务提交时检查数据是否被其他事务修改来处理冲突。
特点:
- 版本检查:乐观锁通常使用版本号或时间戳来检测数据的变化。在每次修改数据时,都会检查数据的版本号或时间戳是否与事务开始时的一致。
- 冲突处理:如果版本号或时间戳不一致,说明数据已经被其他事务修改,事务会回滚或重新执行。
- 性能优势:由于乐观锁不需要在数据访问时加锁,通常可以获得更高的并发性能。但在高冲突环境下,可能会导致更多的重试和回滚。
示例: 在乐观锁中,通常会在数据表中增加一个版本号字段。例如:
-- 数据表结构
CREATE TABLE orders (
id INT PRIMARY KEY,
status VARCHAR(255),
version INT
);
-- 查询数据
SELECT status, version FROM orders WHERE id = 1;
-- 更新数据(假设读取的版本号是 1)
UPDATE orders
SET status = 'processed', version = version + 1
WHERE id = 1 AND version = 1;
在这个例子中,version
字段用于检测数据是否已被其他事务修改。如果 version
字段的值与事务开始时的值不一致,则说明数据已经被其他事务修改,更新操作会失败。
总结
-
悲观锁:
- 锁定:在事务执行期间,锁定数据,防止其他事务对数据进行修改。
- 冲突处理:通过锁定避免冲突,但可能导致性能瓶颈和锁等待。
- 适用场景:适用于数据冲突较多的场景,或对数据一致性要求非常严格的应用。
-
乐观锁:
- 锁定:不直接锁定数据,而是通过版本号或时间戳检查数据是否被修改。
- 冲突处理:通过检测版本号或时间戳来处理冲突,通常具有更高的并发性能。
- 适用场景:适用于数据冲突较少的场景,或对性能要求较高的应用。
选择使用哪种锁策略取决于具体应用的并发需求、性能要求和数据一致性要求。