数据库悲观锁和乐观锁介绍一下?
参考回答
悲观锁和乐观锁是两种常用的并发控制机制,主要用于解决多事务并发访问同一数据时的冲突问题。
- 悲观锁:通过加锁机制防止并发事务对数据的修改,适合冲突概率较高的场景。
- 乐观锁:不加锁,而是通过版本号或时间戳来检测冲突,适合冲突概率较低的场景。
详细讲解与拓展
1. 悲观锁
(1)定义
悲观锁假定冲突会频繁发生,在操作数据前先加锁,保证当前事务对数据的独占访问,其他事务需等待锁释放后才能访问。
(2)实现方式
在数据库中,悲观锁通常通过 数据库原生的锁机制 实现,例如行级锁、表级锁等。
– 共享锁(S 锁):允许多个事务同时读,但不允许写。
– 排他锁(X 锁):只允许一个事务读或写,其他事务被阻塞。
(3)悲观锁的典型场景
适合高冲突场景,例如:
– 银行转账:需要确保账户余额的正确性。
– 库存扣减:多个用户同时购买同一件商品时,需要确保库存不会超卖。
(4)实现示例
假设有一个商品表 Products
:
CREATE TABLE Products (
id INT PRIMARY KEY,
name VARCHAR(50),
stock INT
);
悲观锁示例:
-- 开启事务
START TRANSACTION;
-- 查询并加排他锁
SELECT stock FROM Products WHERE id = 1 FOR UPDATE;
-- 更新库存
UPDATE Products SET stock = stock - 1 WHERE id = 1;
-- 提交事务
COMMIT;
在 FOR UPDATE
的查询中,行锁会阻止其他事务同时修改这条记录。
2. 乐观锁
(1)定义
乐观锁假定冲突较少,允许多个事务同时操作数据,只有在提交时才检查冲突,如果检测到冲突则回滚并重试。
(2)实现方式
乐观锁通常通过版本号(Version)或时间戳(Timestamp)字段实现:
– 每次更新时检查版本号是否匹配,不匹配则说明发生了并发冲突。
(3)乐观锁的典型场景
适合低冲突场景,例如:
– 表单更新:用户同时编辑表单,提交时校验版本号。
– 统计系统:数据更新频率较低时,使用乐观锁避免频繁加锁。
(4)实现示例
假设同样的商品表 Products
添加了版本号字段:
ALTER TABLE Products ADD version INT DEFAULT 0;
乐观锁示例:
-- 查询当前库存和版本号
SELECT stock, version FROM Products WHERE id = 1;
-- 尝试更新(乐观锁通过版本号控制)
UPDATE Products
SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 0;
-- 检查是否更新成功
IF ROW_COUNT() = 0 THEN
-- 说明版本号不匹配,冲突发生
-- 回滚或重试操作
END IF;
在此示例中,如果 version
不匹配,更新失败,表示发生了并发修改。
3. 悲观锁与乐观锁的对比
特性 | 悲观锁 | 乐观锁 |
---|---|---|
适用场景 | 高冲突场景,如银行转账、库存扣减 | 低冲突场景,如统计分析、表单编辑 |
实现方式 | 数据库加锁(行锁、表锁) | 版本号或时间戳 |
性能开销 | 开销高(锁等待、锁定资源) | 开销低(无锁操作,但需检测冲突) |
并发性能 | 并发性能低 | 并发性能高 |
事务冲突处理 | 通过阻塞等待解决 | 通过检测冲突并回滚重试 |
典型问题 | 锁等待、死锁 | 多次冲突可能导致频繁重试 |
4. 选择悲观锁还是乐观锁?
1)选择悲观锁
- 数据竞争严重,冲突发生概率高。
- 数据对一致性要求高,不能容忍失败。
- 示例场景:
- 银行账户转账。
- 多用户实时操作库存。
2)选择乐观锁
- 数据竞争较低,冲突发生概率小。
- 对事务性能要求高,希望减少锁的开销。
- 示例场景:
- 用户更新个人资料。
- 数据分析中的统计更新。
总结
悲观锁通过加锁来确保数据的一致性,适合冲突频繁且对数据要求严格的场景;而乐观锁则通过版本控制来避免锁开销,适合低冲突、高性能需求的场景。选择哪种锁机制,需根据业务特性和并发冲突概率权衡使用。