MySQL 从入门到删库跑路,保姆级教程!

你是小阿巴,刚入行的程序员。

这天,你接到一个私活:帮学校做个学生管理系统,要能管理学生信息、记录成绩、统计数据。

你一听,这不简单吗?用 Java 写个程序,把数据存到 Map 里就搞定了。

public class StudentManagementSystem {
   // 使用 Map 存储学生信息,key 为学号,value 为学生信息字符串
   private Map<Integer, StringstudentMap new HashMap<>();
   // 添加学生
   public void addStudent(int studentNo, String name, double score, int classId) {
       String studentInfo name "," score "," classId;
       studentMap.put(studentNo, studentInfo);
  }
   // 查询学生
   public String getStudent(int studentNo) {
       return studentMap.get(studentNo);
  }
   // 查询所有学生
   public void listAllStudents() {
       for (Map.Entry<Integer, Stringentry : studentMap.entrySet()) {
           System.out.println("学号:" entry.getKey() +
                   ",信息:" entry.getValue());
      }
  }
}

 

结果一周后,甲方指着你的鼻子骂道:狗阿巴,我录入的 500 个学生信息怎么全没了?!

你一查,原来昨晚服务器重启了,导致内存里的数据全部丢失!

你汗流浃背了:看来我得把数据保存到硬盘上……

于是你连夜改代码,把数据存到了文本文件里,这下数据就不会丢失了。

但接下来,甲方提出了各种不同的查询数据需求,每个需求你都得写一堆代码逻辑,让你越来越头大。

于是你找到号称 “后端之狗” 的鱼皮求助:有没有更好的办法管理数据啊?

鱼皮笑了笑:当然要用数据库啦!

你一脸懵:数据库?那是啥?

 

 

第一阶段:认识数据库

鱼皮:数据库就像一个超级 Excel 表格,可以存储管理海量数据、快速灵活地查询和筛选数据、在多个服务器间共享数据、并且能够精确控制数据的读写权限。

你恍然大悟:啊,所以我应该用数据库来管理学生信息。🤡

鱼皮:没错,绝大多数项目的数据都存在数据库里,因此数据库是后端程序员的必备技能。

数据库主要分 2 大类:

1)关系型数据库,比如 MySQL、Oracle、PostgreSQL,适合存储相互之间有关联的数据,比如学生和班级、订单和商品。

2)非关系型数据库,比如 Redis(主要用于缓存和高速读写)、MongoDB(文档型数据库),它们在数据结构和使用场景上更灵活。

 

你:这么多数据库,我该学哪个呢?

鱼皮:建议新手从 MySQL 开始,因为它是主流的、容易入门的、开源的关系型数据库。

你:那还等什么,MySQL,启动!

鱼皮:别急,在学之前,得先了解几个数据库的基本概念。

1)数据库管理系统(DBMS):顾名思义就是用来管理数据库的系统,比如 MySQL。它把数据存储在硬盘上,断电也不会丢失。

2)数据库:一个 MySQL 系统可以管理多个数据库,比如每个项目一个库(学生系统一个库、订单系统一个库),互不干扰。

3)表:一个数据库可以有多张数据表,用来存储某一类数据。

4)记录:表由一行行记录组成,每一行就是一条数据

5)字段:每一列对应一个字段,具有名称和类型。比如姓名字段是文本类型、年龄字段是数字类型。

6)关联

这是关系型数据库的核心,表和表之间是有联系的,主要有 3 种关系。

1 对 1:比如学生表和学生档案表,一个学生对应一份档案

1 对多:比如班级表和学生表,一个班级有多个学生

多对多:比如学生表和课程表,一个学生可以选多门课,一门课也可以被多个学生选

这些表和表之间的关联形成了完整的业务系统。

你想了想:对哦,也就是说我可以根据学生关联查询到所属的班级和选修的课程。

那怎么操作数据库呢?

鱼皮:这就要用到 SQL 了。

SQL 是专门用来操作数据库的 结构化查询语言(Structured Query Language)。

你可以用 SQL 实现各种查询,比如:

1)查询所有学生:SELECT * FROM student;

2)只查询属于某个班级的学生:SELECT * FROM student WHERE class_id = 1;

3)分组统计每个班级中所有学生的平均成绩:SELECT class_id, AVG(score) FROM student GROUP BY class_id;

4)还可以同时查询学生表和班级表,并把结果关联在一起:SELECT s.name, c.class_name FROM student s JOIN class c ON s.class_id = c.id;

此外,不同的数据库管理系统(比如 MySQL、Oracle、SQL Server)有自己的 “方言”,操作这些数据库的 SQL 会有一点点小差别。

你:天呐,相当于我要多学一门语言,而且还要学方言!

鱼皮:别担心,SQL 的核心语法都是通用的。

而且你现在只需要知道 SQL 是用来操作数据库的就够了,

有时间再到我开发的 免费 SQL 学习网站 边练边学,而且后续我还会出视频专门讲 SQL 哦。

 

你:关注了关注了~

鱼皮:那接下来咱们就开始实战学习吧,先把 MySQL 装上并且操作一波。

 

第二阶段:实战应用

基础操作

机智如你,直接打开 官网 下载了 MySQL 数据库,并且成功安装运行。

 

但是怎么操作 MySQL 呢?

鱼皮:可以使用官方提供的 命令行工具,先输入命令,输入默认设置的用户名和密码,就能连上数据库并进行操作了。

你:root 是什么?

鱼皮:root 是数据库的超级管理员账号,拥有所有权限。

不过命令行看着不直观,我建议你装个 MySQL 可视化工具(比如 Navicat、DataGrip),能像使用 Excel 一样管理数据库,数据一目了然。

你:哇,确实方便多了!

鱼皮:接下来我带你实际操作一遍,用数据库来管理学生信息。

1)首先连接数据库

点击左上角创建连接,选择数据库的类别,然后输入数据库配置信息,点击确认,就连接成功了。

 

2)然后来创建一个数据库

右键点击数据库连接,选择 “新建数据库”,命名为 school,点击确认,就创建成功了。

对应的 SQL 语句是 CREATE DATABASE school;

 

3)接下来我们要创建表

鱼皮:想一想,如果要设计一个存储学生信息的表,需要哪些字段?

你:学号、姓名、成绩、班级。

鱼皮:没错,每个字段还要指定类型。

  • 学号用 int 整数类型

  • 姓名用 varchar 文本类型

  • 成绩用 decimal 小数类型(保留 2 位小数)

  • 班级用 int 整数类型(表示班级编号)

除了类型,创建表时还要设置一些 约束,比如:

  • 主键:每条数据的唯一标识,一般建议每个表单独加一列 id 字段,作为主键

  • 非空:某些字段不能为空,比如姓名不能为空

  • 唯一:值不能重复,比如每个学号都是唯一的

  • 外键:建立表之间的关联关系,比如学生表的 class_id 可以设为外键,关联到班级表的 id,保证数据的完整性。但实际开发中外键存在性能问题,用的没那么多。

设计好表结构之后,点击保存,数据表就创建成功了。

对应的 SQL 语句类似这样:

CREATE TABLE student (
  id INT PRIMARY KEY AUTO_INCREMENT,  # 主键,自动递增
  student_no INT UNIQUE NOT NULL,     # 学号,唯一且非空
  name VARCHAR(50NOT NULL,          # 姓名,非空
  score DECIMAL(5,2),                 # 成绩,最多 5 位数字,2 位小数
  class_id INT                        # 班级编号
);

 

4)然后我们就可以操作数据表了。主要有 4 类核心操作 —— 增删改查。

先插入几条学生数据:

INSERT INTO student (student_no, name, score, class_id
VALUES (2024001'小阿巴'95.51);

然后查询所有学生数据:

SELECT FROM student;

修改某条数据,把你的成绩改成满分:

UPDATE student SET score 100 WHERE student_no 2024001;

SELECT FROM student;

最后删除某条数据:

DELETE FROM student WHERE student_no 2024001;

SELECT FROM student;

鱼皮:注意,删除掉的数据就再也找不回来了!

因此实际项目中建议使用 逻辑删除,就是加个 is_deleted 字段来标记数据是否失效,而不是真的删除数据,这样即使误删也能恢复。

 

客户端操作

你很是兴奋:用可视化工具真方便啊,但是我怎么用代码操作数据库呢?

鱼皮:主流编程语言都有操作数据库的 SDK,比如使用 Java 的 JDBC,需要先加载对应数据库的驱动、然后获取连接、编写 SQL 语句并执行、最后收集结果并关闭连接。

你挠了挠头:我就查询 1 次数据库,都要写这么多代码么?而且我根本不会写 SQL 语句啊!

鱼皮笑道:没关系,实际开发中我们会使用 ORM 对象关系映射框架,可以把数据库的表映射成 Java 对象,像操作对象一样操作数据库。

比如 Java 的 MyBatis 框架,可以让你用更简洁的方式执行 SQL:

// MyBatis 方式:只需要写 SQL,框架自动处理连接和结果映射
Student student studentMapper.selectByStudentNo(2024001);
System.out.println(student.getName());

还有它的增强版 MyBatis Plus 和 MyBatis Flex,提供了更多开箱即用的功能,连 SQL 都不用写!直接调用现成的方法,几行代码就能实现增删改查,告别 SQL Boy!

你感叹道:这才是人写的代码啊!优雅,真是优雅~

鱼皮提醒道:但是,框架不是万能的,有些复杂查询的 SQL 还要自己手写,不过现在有 AI 了,写个 SQL 还不是手拿把掐的?

你:明白了,我这就用框架重写学生管理系统。

 

第三阶段:数据库特性

索引

一个月后,你重写的学生管理系统正式上线,不仅得到了甲方的好评,而且很快火遍了全国高校。

你内心暗爽:数据库也不过如此嘛~

但你没想到,随着学生数据量的暴涨和表结构的扩展,你的数据库查询速度也越来越慢,光是查询某个班级的学生竟然都要等好几秒!

你有些惊讶:我这 SQL 也不复杂啊,怎么查询这么慢?

鱼皮:加个索引试试?

你:索引?那是啥?

鱼皮:索引就是数据库的目录。你现在查数据,数据库得一行一行全表扫描,数据量越大速度越慢。加了索引后,数据库可以通过索引快速定位到数据,就像通过书的目录快速翻到某一页。

你恍然大悟:对啊,老师经常按班级查询学生,不妨给班级字段添加索引。

CREATE INDEX idx_class_id ON student(class_id);

添加索引后,你再次测试查询,竟然只要几十毫秒!

你激动得跳了起来:索引太香了,我要给所有字段都加索引~

鱼皮摆摆手:别,索引可不是越多越好!

你愣住了:为啥?

鱼皮:索引虽然能加快查询,但也有代价。

  • 一方面会增加写入数据(增删改)的开销。因为每次写入数据,索引也要更新。

  • 此外,索引本身也是数据,要占用更多的存储空间。

所以,要在经常用来查询、并且数据区分度高的字段上合理添加索引。比如班级 ID、学号这种字段适合加索引,但性别字段(只有男女两个值)就不适合。

 

事务

又过了几天,甲方又提新需求了。

甲方:小阿巴,有老师反馈题目判错了,需要批量把所有学生的成绩都加 5 分。

你信心满满:简单,不就是个批量更新操作么?

你写了一段代码,循环更新所有学生的成绩。

没想到,越自信 Bug 越多,功能刚刚上线,就收到了投诉:怎么有些学生的成绩改了,有些没改?

你查看了下日志,发现前面学生的成绩更新成功了,但由于网络波动,导致后面的学生都没更新。

你急坏了:咋办,这样就不公平了啊!

鱼皮看了看你的代码:这是典型的数据一致性问题,你需要用 事务 来解决。

你:啥是事务?

鱼皮:事务就是把多个操作捆绑在一起,要么全部成功,要么全部失败。

就像你给别人投币,你扣除硬币和对方增加硬币必须同时成功,不能让硬币凭空消失对吧?

你:对对对,那批量改成绩也是,要么所有学生都加 5 分,要么都不加。

鱼皮:没错,数据库事务有 4 个特性,简称 ACID:

  • 原子性(Atomicity):要么全做,要么全不做。

  • 一致性(Consistency):数据从一个正确状态到另一个正确状态。比如转账时,A 扣钱和 B 加钱的总和不变。

  • 隔离性(Isolation):多个事务互不干扰。

  • 持久性(Durability):事务完成后,数据永久保存。

你:那怎么使用事务呢?

鱼皮:如果直接用原始的 JDBC,需要手动控制事务,比较麻烦。

但如果在 Spring 框架中,只需要加一个 @Transactional 注解就搞定了:

Spring 会自动帮你管理事务。如果中间任何一步出错,抛出了指定的异常,整个事务会自动回滚,将数据恢复到原来的状态,保证要么所有学生都加分成功,要么都不加。

你:原来这么简单啊,我这就用事务重写代码!

 

其他

鱼皮:MySQL 还有一些其他特性,比如视图可以用来简化复杂查询、存储过程可以批量执行 SQL、触发器可以自动触发操作。

1)视图:视图是一个虚拟表,把复杂查询封装起来重复使用。比如你经常要统计每个班级的平均成绩,每次都写一长串 SQL 太麻烦,可以创建一个视图,以后直接查视图就行。

2)存储过程:可以批量执行 SQL。比如每天定时同步数据,写个存储过程,定时调用就行。

3)触发器:可以自动触发操作。比如每次添加用户,自动生成一条操作日志,不用手动写代码。

你感叹道:原来数据库还有这么多功能,我学不完了啊……

鱼皮笑了:别担心,这些在实际开发中用得不多,先简单了解就好。

 

第四阶段:生产环境实践

几年后,你已经成为小有名气的 “学生管理系统大师”,还成立了自己的工作室。

没事儿就对着新来的实习生阿坤吹牛皮:你阿巴哥我啊,精通数据库,索引、事务耍的贼溜儿~

然而某天上午,学校的运维同学打来电话:阿巴阿巴,系统出问题了,所有操作都一直 转圈、超时! 好像是数据库卡死了!

你大惊:什么?!我都加索引了,还能卡死?

emmm…… 对了,我想到了!

赶紧重启数据库,重启解决所有问题!嘿嘿嘿哈哈哈哈~

结果重启没多久,数据库又卡死了!

你彻底懵了:呜呜呜,怎么办啊!

这时,你身旁的阿坤突然鸡叫起来:我来!

 

为什么系统会崩溃?

阿坤:我们先看看为什么系统会崩溃?

开启 MySQL 的慢查询日志功能,有两种方式。

1)通过 SQL 命令(临时开启)

# 开启慢查询日志功能
SET GLOBAL slow_query_log 1;

# 设置慢查询阈值(超过几秒算是慢查询)
SET GLOBAL long_query_time 5;

# 修改日志文件位置(可选)
SET GLOBAL slow_query_log_file '/path/to/slow.log';

2)修改配置文件(永久生效)

# 在 [mysqld] 部分设置参数
slow_query_log 1
long_query_time 5
slow_query_log_file = /path/to/slow.log

 

你看,有些 SQL 语句执行了几十秒!

使用 Explain 命令分析下这些查询,原来没有正确使用索引,导致了全表扫描,把数据库拖垮了。

这些慢查询就是罪魁祸首,它们会 长时间霸占数据库连接和 CPU 资源。当大量用户同时执行慢查询,数据库连接池很快被耗尽,新的请求因为无法获取连接、全部阻塞,导致数据库 “卡死”。

你惭愧地低下头:我知道了,生产环境一定要开启慢查询日志,及时发现慢 SQL 并优化。

 

数据丢失了怎么办?

屋漏偏逢连夜雨,你很快又收到了投诉,说是有的学生数据丢失了!

你很是疑惑:MySQL 不是有日志(Redo Log 和 Binlog)来保证数据持久性吗?怎么会丢数据呢?

经过排查,原来是服务器的硬盘被一个学生不小心踹坏了!

你难受得像持矢了一样:真是人在家中坐,Bug 天上来。

唉,我怎么把数据找回来啊?

阿坤:别怕,幸好鱼皮哥之前设置了备份。一方面定期做 全量备份,比如利用 mysqldump 工具每天凌晨备份一次并发送到其他服务器上;再配合 增量备份,利用 Binlog(二进制日志)记录每次的数据修改操作,这样即使数据库崩了,也能恢复到最近的状态。

 

怎么保证系统不会再崩?

你松了一口气:那怎么保证数据库不会再宕机呢?

阿坤:可以搭建 MySQL 高可用集群,典型的是 一主多从 架构。

主库(Master)专门负责写操作(增删改),并通过 Binlog 记录每一次数据变更操作。

从库(Slave) 拉取主库的 Binlog 到本地文件中,然后回放数据变更操作,实现数据同步。我们可以利用从库来承担绝大部分的读操作,这叫 读写分离,能大大提升并发能力。

当主库出现故障时,利用 MHA 等工具,自动将一个从库提升为新的主库,并调整其他从库的指向,实现故障的自动切换。

 

数据量大了怎么办?

你很是惊讶:之前完全没听说过这些啊……

阿坤用看流浪狗的眼神看了你一眼:阿巴哥哥,如果数据量特别大,一个库存不下怎么办?

你哑口无言:阿巴阿巴……

阿坤笑了:当然是 分库分表 啦!比如

1)水平拆分:把同一张表的数据分散到多个表或多个库中。比如根据学生 ID 的尾号,奇数的存到表 1,偶数的存到表 2。

2)垂直拆分:按业务功能来拆分库,比如把学生基本信息、成绩信息、选课信息分别存到不同的库中。

你彻底服了:妙啊,这样就能存储海量数据了!

 

其他实践

这时鱼皮走过来拍了拍阿坤的肩膀:小伙子年轻有为啊!

小阿巴,MySQL 生产环境实践的知识点还有很多。比如:

1)权限管理:不要所有人都用 root 账号,风险太大,要给不同的人分配不同的权限。

2)云数据库服务:提供了现成的 MySQL 集群架构、监控系统、自动备份、数据迁移等,比自己运维省心多了。

3)调优技巧:硬件优化、数据库配置优化、库表设计优化、SQL 优化、连接池优化等等。

4)常见问题:还有死锁排查、大表的在线变更、数据迁移、主从延迟处理等等,遇到了再去解决。

你羞愧地抬不起头:我以为自己已经掌握了数据库,原来只是学了个皮毛……

 

第五阶段:深入底层原理

于是,你主动找到阿坤:我想深入学习 MySQL,不能只停留在会用的层面,请问怎么学习底层原理啊?

阿坤有些惊讶:咦?你不 背八股文 的么?刷刷题就好了呀!

你震惊了:现在的实习生,竟然恐怖如斯!

 

鱼皮:阿坤你别逗他了,其实我们可以带着问题学习。比如 MySQL 是如何实现高效查询的

你想了想:加索引?

鱼皮:对,但这只是使用层面。底层实现有很多技术,比如高效的存储引擎(InnoDB)、优秀的索引结构(B+ 树)、缓冲池机制、查询优化器等等。

比如我考考你,下面两个 SQL 语句哪个执行更快?

# SQL 1: 使用 OR
SELECT FROM student WHERE class_id OR class_id 2;

# SQL 2: 使用 IN
SELECT FROM student WHERE class_id IN (12);

你:额…… 第 2 个?因为它更简短。

鱼皮:哼哼,答案是 几乎一样快!因为 MySQL 的查询优化器非常智能,它会分析语句、将它们处理成相同的逻辑结构,再去执行。

这就是 MySQL 能高效查询的原因之一,带着这些问题去阅读相关文章,或者直接像阿坤说的刷一刷 MySQL 面试题,就能快速学会很多核心知识点。

img

如果想系统学习,可以看看《MySQL 是怎样运行的》、《高性能 MySQL》这几本书。

img

要记住,学习底层原理不只是为了应付面试,而是为了更好地使用 MySQL,遇到问题时能够快速定位和解决。

你:好的,我这就去学!

 

结尾

若干年后,你已经成为了大厂的数据库专家。不仅能熟练设计库表、优化性能,搭个 MySQL 集群也是手拿把掐的。

!img

你也像鱼皮当时一样,耐心地给新人分享学习数据库的经验:数据库是实战型技术,一定要多动手实践。

img

再次遇到鱼皮是在一条昏暗的小巷,此时的他年过 35,灰头土脸。你什么都没说,只是给他点了个赞,不打扰,是你的温柔。

img

更详细的 MySQL 数据库学习路线,可以在编程导航免费阅读哦。

 

更多编程学习资源

posted @ 2025-12-02 11:33  程序员鱼皮  阅读(0)  评论(0)    收藏  举报