前言
在最近字节的面试中,面试管问了一个问题:你所知道的,MySQL都支持哪些锁?当时回答的不是特别好,因此写下这篇文章做个记录。方便自己复盘以及帮助各正在面试的小伙伴们。
乐观锁
乐观锁是一种并发控制机制,它假设在事务提交之前没有其他事务会修改相同的数据
。对数据的操作是保持乐观态度的,因此被称为乐观锁。乐观锁通过记录版本号或者时间戳来判断数据是否被修改。回顾以前学过的知识点,在Java的CAS操作机制中也用到了乐观锁的思想来保证数据的可靠性。
那乐观锁的思想如何运用到数据库中呢,我们通过栗子来学习乐观锁。比如在我之前的电商网站课设中,使用products表来管理商品库存。每个商品记录有一个version字段用于乐观锁控制。
代码语言:sql复制-- 初始库存,为了方便理解添加几条数据
INSERT INTO products (product_id, stock, version) VALUES (1, 100, 0);
-- 事务A
START TRANSACTION;
-- 读取商品库存和版本号
SELECT stock, version FROM products WHERE product_id = 1;
-- 假设此时读取到 stock=100, version=0
-- 稍后进行库存减少操作(此时其他事务未修改)
UPDATE products
SET stock = stock - 1, version = version 1
WHERE product_id = 1 AND version = 0;
-- 检查是否更新成功
IF ROW_COUNT() = 1 THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
-- 事务B(并发执行)
START TRANSACTION;
-- 读取商品库存和版本号(在同一时间,也读取到 stock=100, version=0)
SELECT stock, version FROM products WHERE product_id = 1;
-- 尝试减少库存(但此时version已不匹配,因为事务A它已经提交了)
UPDATE products
SET stock = stock - 1, version = version 1
WHERE product_id = 1 AND version = 0;
-- 由于version不匹配,更新失败,事务B回滚
IF ROW_COUNT() = 0 THEN
ROLLBACK;
END IF;
因此,从这个小栗子中可以看出,乐观锁通常是通过记录版本号或者时间戳来判断数据是否被修改的。
悲观锁
顾名思义,悲观锁在数据是否被修改上对数据持有的态度就并不那么乐观了。悲观锁假设在事务期间会发生冲突,它在操作期间持有锁来避免冲突,和乐观锁恰恰相反。
我们往订单处理系统中添加几条数据,使用orders表来管理订单状态。
代码语言:sql复制-- 初始订单状态
INSERT INTO orders (order_id, status) VALUES (1, 'pending');
-- 事务A
START TRANSACTION;
-- 查询订单状态并加锁 这里的FOR UPDATE表示枷锁
SELECT order_id, status FROM orders WHERE order_id = 1 FOR UPDATE;
-- 执行一些业务逻辑,如检查库存、支付验证等 里略过
-- 更新订单状态为处理中
UPDATE orders SET status = 'processing' WHERE order_id = 1;
COMMIT;
-- 如果在事务A执行期间,事务B尝试更新同一订单的状态,它需要等待事务A提交或回滚后才能继续。
由此可见,悲观锁的实现方式是通过SQL语句中的SELECT ... FOR UPDATE
(添加独占锁)或LOCK IN SHARE MODE
(共享锁)来加锁。
行级锁
行锁的定义:行级锁是对单个行加锁,确保数据行的独占访问
。
行锁和悲观锁的独占锁有着异曲同工之妙,因为SELECT ... FOR UPDATE
在InnoDB中实际上是通过行级锁来实现的。
表级锁
定义:表级锁是对整个表加锁,其他连接无法修改或读取此表的数据。在InnoDB中主要用于元数据操作。
虽然InnoDB主要使用行级锁,但在执行如ALTER TABLE
这样的DDL操作时,会隐式地对表加锁。
-- 假设需要修改orders表的结构
ALTER TABLE orders ADD COLUMN delivery_date DATE;
-- 在执行此操作时,orders表被锁定,其他事务无法访问。
意向锁
意向锁是表级锁的一种,它主要用于表示事务将来对表中的行加锁的意向。
意向锁是内部机制,通常情况下是不需要用户直接操作的。它们在InnoDB内部用于协调行级锁和表级锁之间的冲突。
间隙锁
间隙锁是锁定一个范围的键,但不包括这些键的实际值,用于防止幻读。我们经常背的八股就是可重复度的隔离级别下...请往下看