MySQL事务与锁:并发控制的艺术

引言 “并发是计算机科学中最难的问题之一,因为它涉及时间、顺序和不确定性。” —— Leslie Lamport 在前两篇文章中,我们了解了MySQL如何通过索引实现快速查询,如何通过WAL日志保证数据持久化。但还有一个核心问题没有解决: 如何在高并发场景下保证数据一致性? 想象这样的场景: 双11零点,100万用户同时抢购一件库存只有10个的商品 每个用户都执行: 1. 读取库存 → 10 2. 判断库存足够 → 是 3. 扣减库存 → 库存 - 1 4. 创建订单 结果:卖出了100万件,但库存只扣了10个 💥 这就是并发控制的核心难题:如何让多个并发事务互不干扰,同时保证数据一致性? 今天,我们从第一性原理出发,深度剖析MySQL的并发控制机制: 无控制 → 锁机制 → MVCC → 隔离级别 → 死锁处理 混乱 串行化 读写分离 灵活平衡 自动恢复 ❌ ⚠️ ✅ ✅ ✅ 我们还将手写MVCC核心逻辑,彻底理解MySQL如何实现读写不阻塞。 一、问题的起点:并发导致的数据混乱 让我们从一个最经典的并发问题开始:电商库存扣减。 1.1 场景:秒杀商品超卖问题 需求: 商品:iPhone 16 Pro Max(库存10件) 活动:双11零点秒杀,原价9999元,秒杀价1元 预期:10个用户抢到,其余用户提示"已抢完" 无并发控制的实现: /** * 秒杀服务(无并发控制) */ @Service public class SeckillService { @Autowired private ProductMapper productMapper; @Autowired private OrderMapper orderMapper; /** * 秒杀下单(存在并发问题) */ public boolean seckill(Long productId, Long userId) { // 1. 读取库存 Product product = productMapper.selectById(productId); int stock = product.getStock(); // 2. 判断库存是否足够 if (stock <= 0) { return false; // 库存不足 } // 3. 扣减库存 product.setStock(stock - 1); productMapper.updateById(product); // 4. 创建订单 Order order = new Order(); order.setUserId(userId); order.setProductId(productId); order.setAmount(1.00); // 秒杀价1元 orderMapper.insert(order); return true; } } 并发测试: ...

2025-11-03 · maneng

MySQL事务与锁:并发控制的艺术

引言 “并发是计算机科学中最难的问题之一,因为它涉及时间、顺序和不确定性。” —— Leslie Lamport 在前两篇文章中,我们了解了MySQL如何通过索引实现快速查询,如何通过WAL日志保证数据持久化。但还有一个核心问题没有解决: 如何在高并发场景下保证数据一致性? 想象这样的场景: 双11零点,100万用户同时抢购一件库存只有10个的商品 每个用户都执行: 1. 读取库存 → 10 2. 判断库存足够 → 是 3. 扣减库存 → 库存 - 1 4. 创建订单 结果:卖出了100万件,但库存只扣了10个 💥 这就是并发控制的核心难题:如何让多个并发事务互不干扰,同时保证数据一致性? 今天,我们从第一性原理出发,深度剖析MySQL的并发控制机制: 无控制 → 锁机制 → MVCC → 隔离级别 → 死锁处理 混乱 串行化 读写分离 灵活平衡 自动恢复 ❌ ⚠️ ✅ ✅ ✅ 我们还将手写MVCC核心逻辑,彻底理解MySQL如何实现读写不阻塞。 一、问题的起点:并发导致的数据混乱 让我们从一个最经典的并发问题开始:电商库存扣减。 1.1 场景:秒杀商品超卖问题 需求: 商品:iPhone 16 Pro Max(库存10件) 活动:双11零点秒杀,原价9999元,秒杀价1元 预期:10个用户抢到,其余用户提示"已抢完" 无并发控制的实现: /** * 秒杀服务(无并发控制) */ @Service public class SeckillService { @Autowired private ProductMapper productMapper; @Autowired private OrderMapper orderMapper; /** * 秒杀下单(存在并发问题) */ public boolean seckill(Long productId, Long userId) { // 1. 读取库存 Product product = productMapper.selectById(productId); int stock = product.getStock(); // 2. 判断库存是否足够 if (stock <= 0) { return false; // 库存不足 } // 3. 扣减库存 product.setStock(stock - 1); productMapper.updateById(product); // 4. 创建订单 Order order = new Order(); order.setUserId(userId); order.setProductId(productId); order.setAmount(1.00); // 秒杀价1元 orderMapper.insert(order); return true; } } 并发测试: ...

2025-11-03 · maneng

MVCC多版本并发控制:原理与实现

什么是MVCC? MVCC(Multi-Version Concurrency Control,多版本并发控制) 是InnoDB实现高并发的核心机制。 核心思想: 每行数据有多个版本 读操作读取快照版本(不加锁) 写操作创建新版本(加锁) 读写不冲突,提高并发性能 适用隔离级别: ✅ READ COMMITTED ✅ REPEATABLE READ ❌ READ UNCOMMITTED(无需MVCC) ❌ SERIALIZABLE(完全加锁) MVCC的实现机制 1. 隐藏字段 InnoDB为每行数据添加三个隐藏字段: 字段名 长度 说明 DB_TRX_ID 6字节 最后修改该行的事务ID DB_ROLL_PTR 7字节 回滚指针,指向undo log DB_ROW_ID 6字节 隐藏主键(无主键时自动生成) -- 实际存储的行数据(用户不可见) ┌────┬──────┬─────────┬────────────┬─────────────┬────────────┐ │ id │ name │ balance │ DB_TRX_ID │ DB_ROLL_PTR │ DB_ROW_ID │ ├────┼──────┼─────────┼────────────┼─────────────┼────────────┤ │ 1 │ A │ 1000 │ 100 │ 0x7FA8... │ 1 │ └────┴──────┴─────────┴────────────┴─────────────┴────────────┘ 2. undo log版本链 每次修改数据,旧版本保存在undo log,形成版本链。 -- 初始数据 INSERT INTO account (id, name, balance) VALUES (1, 'A', 1000); -- DB_TRX_ID = 100 -- 事务101:修改余额 UPDATE account SET balance = 900 WHERE id = 1; -- DB_TRX_ID = 101,旧版本保存到undo log -- 事务102:再次修改 UPDATE account SET balance = 800 WHERE id = 1; -- DB_TRX_ID = 102,旧版本保存到undo log 版本链结构: ...

2025-01-14 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...