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查询优化:从执行计划到性能调优

引言 “过早优化是万恶之源。但当性能问题真正出现时,优化就是救命稻草。” —— Donald Knuth 在前三篇文章中,我们学习了索引、事务、锁的原理。但光有理论还不够,如何定位和优化慢查询? 想象这样的场景: 凌晨3点,你被一通电话吵醒: "数据库快挂了,所有查询都超时!" 你打开监控,发现: - CPU 100% - 慢查询日志爆满 - 某个SQL执行了10秒还没返回 如何快速定位问题?如何优化这个慢查询? 这就是查询优化的核心价值:让慢查询变快,让系统起死回生。 今天,我们从第一性原理出发,深度剖析MySQL的查询优化: SQL执行流程: 客户端 → 连接器 → 解析器 → 优化器 → 执行器 → 存储引擎 ↓ ↓ ↓ ↓ ↓ 权限检查 语法解析 生成计划 执行查询 返回数据 性能优化: 慢查询 → EXPLAIN → 找到瓶颈 → 优化索引 → 改写SQL → 性能飞跃 10秒 分析 全表扫描 建索引 覆盖索引 10ms 我们还将通过10个真实案例,将慢查询从10秒优化到10ms,性能提升1000倍。 一、SQL执行流程:从SQL到结果集 理解查询优化,首先要理解SQL是如何执行的。 1.1 MySQL的架构:两层结构 ┌─────────────────────────────────────────────────────────────┐ │ MySQL Server层 │ ├─────────────────────────────────────────────────────────────┤ │ 连接器 解析器 优化器 执行器 │ │ ↓ ↓ ↓ ↓ │ │ 权限验证 语法解析 生成计划 执行查询 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 存储引擎层 │ ├─────────────────────────────────────────────────────────────┤ │ InnoDB MyISAM Memory Archive │ │ ↓ ↓ ↓ ↓ │ │ 事务支持 不支持 内存存储 压缩存储 │ └─────────────────────────────────────────────────────────────┘ Server层与存储引擎的职责分工: ...

2025-11-03 · maneng

MySQL查询优化:从执行计划到性能调优

引言 “过早优化是万恶之源。但当性能问题真正出现时,优化就是救命稻草。” —— Donald Knuth 在前三篇文章中,我们学习了索引、事务、锁的原理。但光有理论还不够,如何定位和优化慢查询? 想象这样的场景: 凌晨3点,你被一通电话吵醒: "数据库快挂了,所有查询都超时!" 你打开监控,发现: - CPU 100% - 慢查询日志爆满 - 某个SQL执行了10秒还没返回 如何快速定位问题?如何优化这个慢查询? 这就是查询优化的核心价值:让慢查询变快,让系统起死回生。 今天,我们从第一性原理出发,深度剖析MySQL的查询优化: SQL执行流程: 客户端 → 连接器 → 解析器 → 优化器 → 执行器 → 存储引擎 ↓ ↓ ↓ ↓ ↓ 权限检查 语法解析 生成计划 执行查询 返回数据 性能优化: 慢查询 → EXPLAIN → 找到瓶颈 → 优化索引 → 改写SQL → 性能飞跃 10秒 分析 全表扫描 建索引 覆盖索引 10ms 我们还将通过10个真实案例,将慢查询从10秒优化到10ms,性能提升1000倍。 一、SQL执行流程:从SQL到结果集 理解查询优化,首先要理解SQL是如何执行的。 1.1 MySQL的架构:两层结构 ┌─────────────────────────────────────────────────────────────┐ │ MySQL Server层 │ ├─────────────────────────────────────────────────────────────┤ │ 连接器 解析器 优化器 执行器 │ │ ↓ ↓ ↓ ↓ │ │ 权限验证 语法解析 生成计划 执行查询 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 存储引擎层 │ ├─────────────────────────────────────────────────────────────┤ │ InnoDB MyISAM Memory Archive │ │ ↓ ↓ ↓ ↓ │ │ 事务支持 不支持 内存存储 压缩存储 │ └─────────────────────────────────────────────────────────────┘ Server层与存储引擎的职责分工: ...

2025-11-03 · maneng

MySQL高可用架构:主从复制与分库分表

引言 单机MySQL的三大瓶颈: 1. 存储瓶颈:单机磁盘容量有限(TB级) 2. 并发瓶颈:单机QPS上限1-2万 3. 可用性瓶颈:单点故障,服务不可用 高可用架构的演进路径: 单机 → 主从复制 → 读写分离 → 分库分表 → 分布式数据库 ↓ ↓ ↓ ↓ ↓ 1台 1主N从 读扩展 存储扩展 NewSQL 一、主从复制:高可用的基石 1.1 主从复制原理 核心组件: 主库(Master) ↓ binlog(二进制日志) 从库(Slave) ↓ relay log(中继日志) ↓ 重放SQL 数据一致 复制流程: -- 主库操作 INSERT INTO users (name, age) VALUES ('Zhang San', 25); -- 主库写binlog -- binlog内容: -- Event_type: Query -- Query: INSERT INTO users (name, age) VALUES ('Zhang San', 25) -- 从库IO线程: -- 1. 连接主库,请求binlog -- 2. 主库binlog dump线程发送binlog -- 3. 从库IO线程写入relay log -- 从库SQL线程: -- 1. 读取relay log -- 2. 解析SQL语句 -- 3. 执行SQL:INSERT INTO users... -- 4. 完成数据同步 三个关键线程: ...

2025-11-03 · maneng

MySQL高可用架构:主从复制与分库分表

引言 单机MySQL的三大瓶颈: 1. 存储瓶颈:单机磁盘容量有限(TB级) 2. 并发瓶颈:单机QPS上限1-2万 3. 可用性瓶颈:单点故障,服务不可用 高可用架构的演进路径: 单机 → 主从复制 → 读写分离 → 分库分表 → 分布式数据库 ↓ ↓ ↓ ↓ ↓ 1台 1主N从 读扩展 存储扩展 NewSQL 一、主从复制:高可用的基石 1.1 主从复制原理 核心组件: 主库(Master) ↓ binlog(二进制日志) 从库(Slave) ↓ relay log(中继日志) ↓ 重放SQL 数据一致 复制流程: -- 主库操作 INSERT INTO users (name, age) VALUES ('Zhang San', 25); -- 主库写binlog -- binlog内容: -- Event_type: Query -- Query: INSERT INTO users (name, age) VALUES ('Zhang San', 25) -- 从库IO线程: -- 1. 连接主库,请求binlog -- 2. 主库binlog dump线程发送binlog -- 3. 从库IO线程写入relay log -- 从库SQL线程: -- 1. 读取relay log -- 2. 解析SQL语句 -- 3. 执行SQL:INSERT INTO users... -- 4. 完成数据同步 三个关键线程: ...

2025-11-03 · maneng

MySQL核心原理:关键技术深度解析

引言 通过前5篇文章,我们已经掌握了MySQL的核心技术。本文将深入MySQL内部,理解关键组件的工作原理。 一、InnoDB核心组件 1.1 Buffer Pool:内存缓存池 作用:缓存数据页和索引页,减少磁盘IO Buffer Pool结构: ┌─────────────────────────────────────┐ │ Buffer Pool (默认128MB) │ ├─────────────────────────────────────┤ │ 数据页缓存 (75%) │ │ ├─ Page 1: users表数据 │ │ ├─ Page 2: orders表数据 │ │ └─ ... │ ├─────────────────────────────────────┤ │ 索引页缓存 (20%) │ │ ├─ Index Page 1: uk_username │ │ ├─ Index Page 2: idx_created_time │ │ └─ ... │ ├─────────────────────────────────────┤ │ Undo页缓存 (5%) │ │ └─ 历史版本数据 │ └─────────────────────────────────────┘ 淘汰算法: LRU (Least Recently Used) 热数据: 靠近链表头部 冷数据: 靠近链表尾部 新数据: 先放入中间位置(避免扫描污染) 关键参数: ...

2025-11-03 · maneng

MySQL核心原理:关键技术深度解析

引言 通过前5篇文章,我们已经掌握了MySQL的核心技术。本文将深入MySQL内部,理解关键组件的工作原理。 一、InnoDB核心组件 1.1 Buffer Pool:内存缓存池 作用:缓存数据页和索引页,减少磁盘IO Buffer Pool结构: ┌─────────────────────────────────────┐ │ Buffer Pool (默认128MB) │ ├─────────────────────────────────────┤ │ 数据页缓存 (75%) │ │ ├─ Page 1: users表数据 │ │ ├─ Page 2: orders表数据 │ │ └─ ... │ ├─────────────────────────────────────┤ │ 索引页缓存 (20%) │ │ ├─ Index Page 1: uk_username │ │ ├─ Index Page 2: idx_created_time │ │ └─ ... │ ├─────────────────────────────────────┤ │ Undo页缓存 (5%) │ │ └─ 历史版本数据 │ └─────────────────────────────────────┘ 淘汰算法: LRU (Least Recently Used) 热数据: 靠近链表头部 冷数据: 靠近链表尾部 新数据: 先放入中间位置(避免扫描污染) 关键参数: ...

2025-11-03 · maneng

MySQL核心原理:关键技术深度解析

引言 通过前5篇文章,我们已经掌握了MySQL的核心技术。本文将深入MySQL内部,理解关键组件的工作原理。 一、InnoDB核心组件 1.1 Buffer Pool:内存缓存池 作用:缓存数据页和索引页,减少磁盘IO Buffer Pool结构: ┌─────────────────────────────────────┐ │ Buffer Pool (默认128MB) │ ├─────────────────────────────────────┤ │ 数据页缓存 (75%) │ │ ├─ Page 1: users表数据 │ │ ├─ Page 2: orders表数据 │ │ └─ ... │ ├─────────────────────────────────────┤ │ 索引页缓存 (20%) │ │ ├─ Index Page 1: uk_username │ │ ├─ Index Page 2: idx_created_time │ │ └─ ... │ ├─────────────────────────────────────┤ │ Undo页缓存 (5%) │ │ └─ 历史版本数据 │ └─────────────────────────────────────┘ 淘汰算法: LRU (Least Recently Used) 热数据: 靠近链表头部 冷数据: 靠近链表尾部 新数据: 先放入中间位置(避免扫描污染) 关键参数: ...

2025-11-03 · maneng

MySQL第一性原理:为什么我们需要数据库?

引言 “如果你不能简单地解释一件事,说明你还没有真正理解它。” —— 理查德·费曼 作为一名后端开发者,你一定写过这样的代码: @Autowired private UserRepository userRepository; public User findUser(Long id) { return userRepository.findById(id); } 这行代码背后,数据库帮你做了什么? 持久化存储:数据写入磁盘,重启不丢失 快速检索:1亿条数据中找到目标行,只需10微秒 并发控制:1万个并发请求,数据不会错乱 事务保证:转账操作要么全成功,要么全失败 故障恢复:系统崩溃后,数据可以完整恢复 但你有没有想过: 为什么不用文件存储数据?(txt、csv、json) 为什么不用内存存储数据?(HashMap、Redis) 为什么一定要用MySQL这样的关系型数据库? 今天,我们从第一性原理出发,通过对比文件、内存、MySQL三种存储方案,深度剖析数据库解决的核心问题。 本文不会教你怎么写SQL,而是回答为什么需要数据库。 一、引子:用户注册功能的三种实现 让我们从一个最简单的需求开始:用户注册功能。 需求: 用户注册时,存储用户名和密码 检查用户名是否已存在 用户登录时,验证用户名和密码 按注册时间范围查询用户 性能要求: 支持100万用户 注册和登录响应时间 < 100ms 支持1000并发 我们将用三种方式实现,看看它们的差异。 1.1 场景A:文件存储(users.txt) 实现思路:将用户数据存储在文本文件中,每行一个用户,逗号分隔字段。 /** * 用户服务 - 文件存储实现 */ public class FileUserService { private static final String FILE_PATH = "users.txt"; /** * 注册用户(追加到文件末尾) */ public void register(String username, String password) { // 1. 检查用户名是否已存在 if (exists(username)) { throw new RuntimeException("用户名已存在"); } // 2. 追加到文件 try (FileWriter fw = new FileWriter(FILE_PATH, true)) { String line = username + "," + hashPassword(password) + "," + System.currentTimeMillis() + "\n"; fw.write(line); } catch (IOException e) { throw new RuntimeException("注册失败", e); } } /** * 检查用户名是否存在(全文件扫描) */ public boolean exists(String username) { try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); if (parts[0].equals(username)) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } /** * 登录验证(全文件扫描) */ public boolean login(String username, String password) { String hashedPassword = hashPassword(password); try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); if (parts[0].equals(username) && parts[1].equals(hashedPassword)) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } /** * 按注册时间范围查询(全文件扫描+过滤) */ public List<User> findByRegisteredDateRange(long startTime, long endTime) { List<User> result = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); long registeredTime = Long.parseLong(parts[2]); if (registeredTime >= startTime && registeredTime <= endTime) { result.add(new User(parts[0], parts[1], registeredTime)); } } } catch (IOException e) { e.printStackTrace(); } return result; } private String hashPassword(String password) { // 简化版,实际应该用BCrypt return Integer.toString(password.hashCode()); } } 文件内容示例: ...

2025-11-03 · maneng

InnoDB架构综合实战:从原理到优化

InnoDB架构回顾 InnoDB架构(5层) ┌────────────────────────────────────┐ │ 1. 内存结构 │ │ ├─ Buffer Pool(最大) │ │ ├─ Change Buffer │ │ ├─ Adaptive Hash Index │ │ └─ Log Buffer │ ├────────────────────────────────────┤ │ 2. 后台线程 │ │ ├─ Master Thread │ │ ├─ IO Thread │ │ ├─ Page Cleaner Thread │ │ └─ Purge Thread │ ├────────────────────────────────────┤ │ 3. 磁盘结构 │ │ ├─ 表空间(Tablespace) │ │ ├─ redo log(持久性) │ │ ├─ undo log(原子性+MVCC) │ │ └─ binlog(复制) │ ├────────────────────────────────────┤ │ 4. 存储结构 │ │ └─ 表空间→段→区(1MB)→页(16KB)→行 │ ├────────────────────────────────────┤ │ 5. 锁机制 │ │ ├─ 表锁、行锁 │ │ ├─ Gap Lock、Next-Key Lock │ │ └─ MVCC │ └────────────────────────────────────┘ 实战案例1:Buffer Pool命中率优化 问题 生产环境查询慢,QPS从5000降到2000。 ...

2025-01-15 · maneng

如约数科科技工作室

浙ICP备2025203501号

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