1. 假定某商品库存为10,数据库使用mysql的默认隔离级别,请问扣减一个库存同时,有一个查询,查询到的数据库存是多少?
🧠 分析过程
MySQL 默认隔离级别:REPEATABLE READ
MySQL InnoDB 的默认隔离级别是 可重复读(REPEATABLE READ) ,其核心特征:
- 通过 MVCC(多版本并发控制) 实现;
- 一个事务中多次读取同一行数据,结果一致(除非自己修改);
- 在未提交事务中修改的数据 ,其他事务看不到;
- 避免了脏读。
🧾 举例分析
| 时间顺序 |
操作 |
说明 |
| t1 |
事务A开始 |
扣减库存事务开始 |
| t2 |
事务A执行 UPDATE product SET stock = stock - 1 WHERE id = 1; |
将库存改为 9,但未提交 |
| t3 |
事务B执行 SELECT stock FROM product WHERE id = 1; |
查询库存 |
| t4 |
事务A提交 |
扣减完成 |
🔍 t3 时刻:事务B 查询到的结果
因为 MySQL 在 REPEATABLE READ 隔离级别下,
事务B 看不到事务A未提交的修改(MVCC 保证),
所以:
✅ 事务B 查询到的库存 = 10
即使事务A已经执行了 UPDATE,但未提交,其他事务看到的仍是旧版本。
✅ 总结结论
| 项目 |
说明 |
| 数据库隔离级别 |
REPEATABLE READ(默认) |
| 库存初始值 |
10 |
| 事务A操作 |
UPDATE stock = 9(未提交) |
| 事务B操作 |
SELECT stock |
| 查询结果 |
10 |
| 原因 |
MVCC 保证读到的是已提交版本,避免脏读 |
💡 补充:如果换隔离级别会怎样
| 隔离级别 |
查询结果 |
原因 |
| READ UNCOMMITTED |
9 |
能读到未提交数据(脏读) |
| READ COMMITTED |
10 |
只能读到已提交数据 |
| REPEATABLE READ(默认) |
10 |
使用快照读,读到的是事务开始时的版本 |
| SERIALIZABLE |
10 |
加锁读,阻塞直到事务A提交或回滚 |
2.数据库s锁与x锁
🧩 一、S锁与X锁的基本定义
| 锁类型 |
全称 |
英文名称 |
作用 |
| S锁 |
共享锁 |
Shared Lock |
允许多个事务同时读取同一行,但不允许修改 |
| X锁 |
排他锁 |
Exclusive Lock |
只允许一个事务对该行进行读写,其他事务不能再加任何锁 |
🧠 二、加锁后的行为差异
| 操作类型 |
需要的锁类型 |
是否允许并发 |
SELECT ... LOCK IN SHARE MODE |
S锁 |
✅ 允许其他事务也加S锁(共享读) ❌ 不允许X锁(写) |
SELECT ... FOR UPDATE |
X锁 |
❌ 其他事务不能加任何锁(读写都阻塞) |
UPDATE / DELETE / INSERT |
X锁 |
❌ 独占该行 |
普通 SELECT(无锁读) |
不加锁(MVCC快照读) |
✅ 完全并发,读不阻塞写 |
🔐 三、S锁与X锁的兼容性矩阵
| 当前锁类型 |
申请锁类型 |
是否兼容 |
| S锁 |
S锁 |
✅(可以同时读取) |
| S锁 |
X锁 |
❌(阻塞) |
| X锁 |
S锁 |
❌(阻塞) |
| X锁 |
X锁 |
❌(阻塞) |
👉 结论:
📊 五、总结对比表
| 项目 |
S锁(共享锁) |
X锁(排他锁) |
| 允许读 |
✅ |
✅(自己可以) |
| 允许写 |
❌ |
✅(自己可以) |
| 可共存 |
✅ 多个S锁可共存 |
❌ 独占 |
| 与MVCC关系 |
显式加锁读 |
显式写操作 |
| 常见SQL |
SELECT ... LOCK IN SHARE MODE |
UPDATE、DELETE、SELECT ... FOR UPDATE |
💡 六、实际应用场景
| 场景 |
推荐锁类型 |
原因 |
| 需要读取后再判断是否更新 |
SELECT ... FOR UPDATE |
确保数据不会被其他事务修改(加X锁) |
| 仅需读取但希望防止被删除或修改 |
SELECT ... LOCK IN SHARE MODE |
加S锁防止数据被改动 |
| 只想查(读一致性) |
普通 SELECT |
使用MVCC,性能最佳 |