
事务与锁是数据库保证数据一致性和处理高并发的核心技术。可以避免脏数据、处理并发冲突,并设计出高效安全的数据库应用。
-- 开启事务(两种方式)
START TRANSACTION; -- 或 BEGIN;
-- 插入订单记录
INSERT INTO orders (user_id, amount) VALUES (1, 100);
-- 更新账户余额
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- 提交事务(数据永久生效)
COMMIT;
-- 若发生错误则回滚
ROLLBACK;
注意:MySQL的默认隔离级别是 REPEATABLE READ,可通过以下命令查看:
SELECT @@transaction_isolation; -- 输出:REPEATABLE-READ
场景:两个会话同时操作同一条数据,观察不同隔离级别的表现。
-- 会话1:事务A修改数据但不提交
START TRANSACTION;
UPDATE products SET stock = 5 WHERE id = 1; -- 原库存10
-- 会话2:事务B在不同隔离级别下的读取结果
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT stock FROM products WHERE id = 1; -- 可能读到5(脏读)
COMMIT;
隔离级别对照表:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
READ UNCOMMITTED | ✔️ | ✔️ | ✔️ |
READ COMMITTED | ✖️ | ✔️ | ✔️ |
REPEATABLE READ (默认) | ✖️ | ✖️ | ✔️ |
SERIALIZABLE | ✖️ | ✖️ | ✖️ |
悲观锁方案:在查询时直接加锁,阻止其他事务修改。
-- 用户下单时锁定商品库存
START TRANSACTION;
SELECT stock FROM products WHERE id = 1 FOR UPDATE; -- 加排他锁
-- 检查库存并扣减
IF stock >= 1 THEN
UPDATE products SET stock = stock - 1 WHERE id = 1;
END IF;
COMMIT;
乐观锁方案:通过版本号检测冲突,适合低竞争场景。
-- 商品表添加版本号字段
ALTER TABLE products ADD version INT DEFAULT 0;
-- 更新时校验版本号
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 当前版本;
-- 会话1
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; -- 锁住用户1
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; -- 等待会话2释放锁
-- 会话2
START TRANSACTION;
UPDATE accounts SET balance = balance - 200 WHERE user_id = 2; -- 锁住用户2
UPDATE accounts SET balance = balance + 200 WHERE user_id = 1; -- 等待会话1释放锁
-- 死锁发生!MySQL会自动回滚其中一个事务
SHOW ENGINE INNODB STATUS; -- 查看死锁日志
避免死锁建议:
SET innodb_lock_wait_timeout = 30;SELECT ... FOR UPDATE会发生什么?
A. 第一个用户加锁成功,第二个用户等待
B. 两个用户同时加锁成功
C. 直接触发死锁答案:是啥
建议在本地数据库中复现所有案例,观察不同参数下的行为差异。