MySQL 从入门到精通:一个老 DBA 的实战心法
从 CRUD 到高可用,这篇带你走完数据库工程师的进阶之路
文章目录
- [MySQL 从入门到精通:一个老 DBA 的实战心法](#MySQL 从入门到精通:一个老 DBA 的实战心法)
-
- [第一阶段:入门 ------ 先学会"把数据存进去、查出来"](#第一阶段:入门 —— 先学会“把数据存进去、查出来”)
-
- [1.1 DDL 和 DML:建表与操作](#1.1 DDL 和 DML:建表与操作)
- [1.2 JOIN:关系型思维的敲门砖](#1.2 JOIN:关系型思维的敲门砖)
- [第二阶段:进阶 ------ 让查询"跑得快、跑得稳"](#第二阶段:进阶 —— 让查询“跑得快、跑得稳”)
-
- [2.1 数据库设计:范式化不是玄学](#2.1 数据库设计:范式化不是玄学)
- [2.2 索引:性能优化的第一课](#2.2 索引:性能优化的第一课)
- [2.3 事务与 ACID:数据不丢、不错、不乱](#2.3 事务与 ACID:数据不丢、不错、不乱)
- [第三阶段:精通 ------ 架构师视角的性能调优与高可用](#第三阶段:精通 —— 架构师视角的性能调优与高可用)
-
- [3.1 EXPLAIN:你的慢查询显微镜](#3.1 EXPLAIN:你的慢查询显微镜)
- [3.2 主从复制与读写分离](#3.2 主从复制与读写分离)
- [3.3 分库分表:当单机再也装不下](#3.3 分库分表:当单机再也装不下)
- [3.4 进阶特性:JSON 和 CTE](#3.4 进阶特性:JSON 和 CTE)
- [总结:MySQL 学习路线图](#总结:MySQL 学习路线图)
做后端开发这些年,我越来越觉得:SQL 谁都会写,但能写出高并发下不崩、数据量上来后不慢、出问题能快速定位的,才是真本事。
MySQL 作为全世界最流行的开源关系数据库,几乎每个后端工程师都用过。但"会用"和"精通"之间,隔着一整个职业生涯的踩坑经验。这篇文章我想从自己的实战经历出发,把 MySQL 从基础语法、索引优化、事务隔离,到主从复制、分库分表、高可用架构,完整串一遍。希望帮你少走我当年走过的弯路。
第一阶段:入门 ------ 先学会"把数据存进去、查出来"
刚接触 MySQL 时,别想太多,先把 CRUD 跑通。这个阶段的核心是:能独立建表、能写对查询、能理解数据怎么关联。
1.1 DDL 和 DML:建表与操作
- DDL(数据定义语言) :
CREATE DATABASE、CREATE TABLE、ALTER TABLE、DROP TABLE。
这里最容易踩坑的是数据类型选错 。比如用VARCHAR存手机号没问题,但用INT存手机号会溢出;用DATETIME还是TIMESTAMP?前者范围大,后者带时区,看需求。 - DML(数据操作语言) :
SELECT、INSERT、UPDATE、DELETE。
这阶段把WHERE条件(AND、OR、LIKE、BETWEEN)和LIMIT分页练熟就够了。
1.2 JOIN:关系型思维的敲门砖
很多新手写 JOIN 全靠蒙,其实你只要想清楚一个问题:我要保留哪张表的所有行?
INNER JOIN:只保留两个表都匹配的行。比如"有订单的客户"。LEFT JOIN:保留左表所有行,右表没匹配的填NULL。比如"所有客户,以及他们是否下过订单"。RIGHT JOIN:同上,保留右表全部。FULL JOIN(MySQL 8.0+ 支持):两边都保留。
我的经验 :95% 的场景用 INNER JOIN 和 LEFT JOIN 就够了。写之前先在脑子里过一遍业务,别写出笛卡尔积再把服务器搞崩。
第二阶段:进阶 ------ 让查询"跑得快、跑得稳"
能跑通只是第一步。线上出现慢查询时,你才真正开始学 MySQL。
2.1 数据库设计:范式化不是玄学
很多人觉得范式是理论,其实它就是一句话:别让同一份数据在多个地方存。
- 第一范式(1NF):每个字段都是原子值,不能是列表。比如"爱好"字段不能存"篮球,足球",应该拆成多行或用关联表。
- 第二范式(2NF):满足 1NF,且非主键字段必须完全依赖于主键。比如订单明细表里,商品名称只依赖商品 ID,不依赖订单 ID,就应该拆出去。
- 第三范式(3NF):满足 2NF,且消除传递依赖。比如订单表里存了客户 ID,又存了客户姓名,姓名依赖 ID,应该只留 ID。
实战建议:刚开始可以按 3NF 设计,等遇到性能瓶颈再适当反范式化(比如冗余一些字段减少 JOIN)。别一上来就反范式,后面维护会想哭。
2.2 索引:性能优化的第一课
索引是 MySQL 性能的核心,但也是最容易被误用的。我见过有人给每一列都建索引,结果写入慢成狗。
索引的本质:像书的目录。没有索引就要全表扫描(Full Table Scan),有索引就能快速定位。
B+ 树 :MySQL 默认用 B+ 树做索引。叶子节点存数据,非叶子节点只存键值,所以树很矮,查询快。一个重要的推论 :如果你用 LIKE '%abc'(前缀模糊),索引基本废了;用 LIKE 'abc%' 则还能用。
复合索引与最左前缀原则
建一个复合索引 INDEX(a, b, c),相当于按 a 排序,相同 a 再按 b 排序,依此类推。所以:
WHERE a = 1→ 能用索引WHERE a = 1 AND b = 2→ 能用WHERE b = 2→ 用不了,因为没从最左开始WHERE a = 1 AND c = 3→ 只能用到a这一部分
踩坑提醒 :不要在查询条件里对索引列用函数,比如 WHERE YEAR(date_col) = 2023,这样索引直接失效。改成 WHERE date_col BETWEEN '2023-01-01' AND '2023-12-31'。
下面这张图是 MySQL 执行一条查询的完整流程,理解它有助于你后续用 EXPLAIN 分析慢查询。
客户端发起查询
连接层
解析器
优化器
生成执行计划
存储引擎 InnoDB
索引查找 / 数据读取
返回结果集
2.3 事务与 ACID:数据不丢、不错、不乱
转账、下单这种多步操作,必须用事务。事务的四大特性:
- 原子性:要么全成功,要么全失败。中间出错就回滚。
- 一致性:事务前后,数据约束(比如总额不变)必须满足。
- 隔离性:多个事务同时跑,互不干扰(或按隔离级别干扰)。
- 持久性:提交后,就算断电,数据也在。
隔离级别(从低到高):
| 级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ |
| READ COMMITTED | ❌ | ✅ | ✅ |
| REPEATABLE READ(MySQL 默认) | ❌ | ❌ | ❌(InnoDB 通过 MVCC 解决) |
| SERIALIZABLE | ❌ | ❌ | ❌ |
我的建议 :别乱改隔离级别。MySQL 默认的 REPEATABLE READ 能应对绝大多数场景。只有在你明确需要"读已提交"来提升并发时,才考虑改成 READ COMMITTED。
第三阶段:精通 ------ 架构师视角的性能调优与高可用
到了这个级别,你不再纠结于一条 SQL 怎么写,而是要考虑:系统在高并发、大数据量下怎么保持稳定、可扩展。
3.1 EXPLAIN:你的慢查询显微镜
遇到慢查询,第一件事不是改代码,而是跑 EXPLAIN。
怎么看结果?
type列:从好到坏依次是const>eq_ref>ref>range>index>ALL。看到ALL意味着全表扫描,必须加索引。key列:实际用到的索引。如果为NULL,说明没用到。rows列:预估要扫描的行数。越小越好。Extra列:Using filesort或Using temporary往往意味着性能杀手,需要优化。
实战技巧 :有时优化器选择的执行计划不是最优的,你可以用 STRAIGHT_JOIN 强制表连接顺序,或者用 FORCE INDEX 指定索引。但一般先看统计信息是不是过期了,跑一下 ANALYZE TABLE。
3.2 主从复制与读写分离
当单机扛不住读流量时,就该上主从复制了。
- 主库(Master) :负责写入(
INSERT/UPDATE/DELETE)。 - 从库(Slave) :负责读取(
SELECT)。通过binlog从主库同步数据。
从库 主库 客户端 从库 主库 客户端 写入操作 执行事务,写 binlog 传输 binlog 事件 重放事件,更新数据 读取操作 返回结果
常见坑:
- 复制延迟:主库写入快,从库同步慢,导致读到的数据是旧的。解决方案:监控延迟,对实时性要求高的查询强制走主库。
- 主从切换:主库挂了要切从库,需要一套高可用方案(如 MHA、Orchestrator、Group Replication)。
3.3 分库分表:当单机再也装不下
数据量达到几十亿、写入 QPS 超过单机极限时,就得水平拆分(Sharding)。
- 概念:按某个维度(比如用户 ID 哈希)把数据分布到多个数据库实例上。
- 常用中间件:ShardingSphere、MyCAT、Vitess。
挑战:
- 跨分片事务:原本在一个数据库里轻松搞定的 ACID,跨了分片就变成分布式事务,非常复杂。很多场景不得不放弃强一致性,改用最终一致性。
- 跨分片 JOIN:基本不能用了。设计时要提前把数据冗余好,或者通过应用层聚合。
- 分片键选择:选错了会导致数据倾斜(某个分片特别大)。比如按时间分片,新数据都往最后一个分片写,老分片闲着,这就是典型的错误。
我的建议:不到万不得已,别上分库分表。优先考虑:
- 优化索引和 SQL
- 加缓存(Redis)
- 升级硬件(SSD、内存)
- 读写分离
- 实在不行,再上分库分表
3.4 进阶特性:JSON 和 CTE
- JSON 类型 :MySQL 5.7+ 支持原生 JSON。适合存储不固定结构的配置、扩展属性。可以用
JSON_EXTRACT查询,甚至建虚拟列索引。但别滥用------能用标准列就别用 JSON,否则查询性能差、维护难。 - CTE(公共表表达式) :用
WITH定义的临时结果集,能让复杂查询清晰很多。尤其适合递归查询,比如组织架构树、物料清单。
sql
-- 递归查询某个部门下的所有子部门
WITH RECURSIVE dept_tree AS (
SELECT id, name, parent_id FROM departments WHERE id = 1
UNION ALL
SELECT d.id, d.name, d.parent_id
FROM departments d
INNER JOIN dept_tree dt ON d.parent_id = dt.id
)
SELECT * FROM dept_tree;
总结:MySQL 学习路线图
| 阶段 | 核心目标 | 关键技术点 | 可交付成果 |
|---|---|---|---|
| 初级 | 能写 CRUD,懂表结构 | DDL/DML、基础 JOIN、数据类型 | 一个功能正常的业务模块 |
| 中级 | 优化性能,保证数据正确 | 索引原理(B+树、最左前缀)、3NF、事务隔离级别、EXPLAIN |
慢查询优化方案、高并发下的稳定事务 |
| 高级 | 架构设计,高可用扩展 | 主从复制、分库分表、JSON/CTE、高可用切换 | 支撑千万级用户的高可用数据库集群 |
数据库这条路,说到底就是不断和"性能瓶颈"死磕。遇到慢查询,别慌,先用 EXPLAIN 看执行计划,再对症下药------加索引、改 SQL、调参数、上缓存、做拆分。每一步都有代价,但每一步都能让你对 MySQL 的理解更深一层。
希望这篇文章能帮你从"会写 SQL"走到"能设计高可用数据库架构"。如果你在生产环境中遇到过什么奇葩问题,欢迎留言,一起交流。