MySQL 核心操作:多表联合查询与数据库备份恢复
MySQL 核心操作:多表联合查询与数据库备份恢复
一、多表联合查询
在关系型数据库中,表与表通过主键 - 外键关联,多表联合查询可同时从 2 个及以上表中提取关联数据。MySQL 中多表查询主要分为交叉连接、内连接、外连接、分组查询、子查询5 类,以下详细说明
1. 交叉连接(CROSS JOIN):笛卡尔积查询
交叉连接是最基础的多表连接,本质是返回两张表的笛卡尔积(即表 1 的每一行与表 2 的每一行组合,结果行数 = 表 1 行数 × 表 2 行数),实际应用中需配合WHERE
筛选有效数据,否则结果无意义
核心特性
- 两种语法:显式(
CROSS JOIN
)和隐式(逗号分隔表),官方推荐显式语法 - 无
WHERE
时返回全量笛卡尔积,数据量极大(如 1000 行 ×1000 行 = 100 万行),需避免
语法格式
-- 显式语法(推荐)
SELECT <字段名> FROM <表1> CROSS JOIN <表2> [WHERE 筛选条件];-- 隐式语法(不推荐)
SELECT <字段名> FROM <表1>, <表2> [WHERE 筛选条件];
实例演示
以student
(学生表,10 行)和course
(课程表,5 行)为例:
全量笛卡尔积(无筛选):返回 10×5=50 行数据
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| shenyi |
| sy |
| sys |
+--------------------+
6 rows in set (0.01 sec)mysql> use shenyi;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changedmysql> show tables;
+------------------+
| Tables_in_shenyi |
+------------------+
| course |
| student |
| student123 |
+------------------+
3 rows in set (0.00 sec)
mysql> select * from student;
+----+-------------+------+
| id | name | age |
+----+-------------+------+
| 1 | tom | 20 |
| 2 | jerry | 23 |
| 3 | shenyi | 25 |
| 4 | sean | 28 |
| 5 | zhangshan | 26 |
| 7 | lisi | 50 |
| 8 | chenshuo | 10 |
| 9 | wangwu | 100 |
| 10 | qiuyi | 15 |
| 11 | qiuxiaotian | 20 |
+----+-------------+------+
10 rows in set (0.00 sec)mysql> select* from course;
+----+-------------+
| id | course_name |
+----+-------------+
| 1 | Java |
| 2 | MySQL |
| 3 | Python |
| 4 | Go |
| 5 | C++ |
+----+-------------+
5 rows in set (0.00 sec)
mysql> select * from course join student123;
+----+-------------+----+--------+------+-------+--------+-----------+
| id | course_name | id | name | age | sex | height | course_id |
+----+-------------+----+--------+------+-------+--------+-----------+
| 1 | Java | 1 | Dany | 25 | man | 160 | 1 |
| 2 | MySQL | 1 | Dany | 25 | man | 160 | 1 |
| 3 | Python | 1 | Dany | 25 | man | 160 | 1 |
| 4 | Go | 1 | Dany | 25 | man | 160 | 1 |
| 5 | C++ | 1 | Dany | 25 | man | 160 | 1 |
| 1 | Java | 2 | Green | 23 | man | 158 | 2 |
| 2 | MySQL | 2 | Green | 23 | man | 158 | 2 |
| 3 | Python | 2 | Green | 23 | man | 158 | 2 |
| 4 | Go | 2 | Green | 23 | man | 158 | 2 |
| 5 | C++ | 2 | Green | 23 | man | 158 | 2 |
| 1 | Java | 3 | Henry | 23 | woman | 185 | 1 |
| 2 | MySQL | 3 | Henry | 23 | woman | 185 | 1 |
| 3 | Python | 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Go | 3 | Henry | 23 | woman | 185 | 1 |
| 5 | C++ | 3 | Henry | 23 | woman | 185 | 1 |
| 1 | Java | 4 | Jane | 22 | man | 162 | 3 |
| 2 | MySQL | 4 | Jane | 22 | man | 162 | 3 |
| 3 | Python | 4 | Jane | 22 | man | 162 | 3 |
| 4 | Go | 4 | Jane | 22 | man | 162 | 3 |
| 5 | C++ | 4 | Jane | 22 | man | 162 | 3 |
| 1 | Java | 5 | Jim | 24 | woman | 175 | 2 |
| 2 | MySQL | 5 | Jim | 24 | woman | 175 | 2 |
| 3 | Python | 5 | Jim | 24 | woman | 175 | 2 |
| 4 | Go | 5 | Jim | 24 | woman | 175 | 2 |
| 5 | C++ | 5 | Jim | 24 | woman | 175 | 2 |
| 1 | Java | 6 | John | 21 | woman | 172 | 4 |
| 2 | MySQL | 6 | John | 21 | woman | 172 | 4 |
| 3 | Python | 6 | John | 21 | woman | 172 | 4 |
| 4 | Go | 6 | John | 21 | woman | 172 | 4 |
| 5 | C++ | 6 | John | 21 | woman | 172 | 4 |
| 1 | Java | 7 | Lily | 22 | man | 165 | 4 |
| 2 | MySQL | 7 | Lily | 22 | man | 165 | 4 |
| 3 | Python | 7 | Lily | 22 | man | 165 | 4 |
| 4 | Go | 7 | Lily | 22 | man | 165 | 4 |
| 5 | C++ | 7 | Lily | 22 | man | 165 | 4 |
| 1 | Java | 8 | Susan | 23 | man | 170 | 5 |
| 2 | MySQL | 8 | Susan | 23 | man | 170 | 5 |
| 3 | Python | 8 | Susan | 23 | man | 170 | 5 |
| 4 | Go | 8 | Susan | 23 | man | 170 | 5 |
| 5 | C++ | 8 | Susan | 23 | man | 170 | 5 |
| 1 | Java | 9 | Thomas | 22 | woman | 178 | 5 |
| 2 | MySQL | 9 | Thomas | 22 | woman | 178 | 5 |
| 3 | Python | 9 | Thomas | 22 | woman | 178 | 5 |
| 4 | Go | 9 | Thomas | 22 | woman | 178 | 5 |
| 5 | C++ | 9 | Thomas | 22 | woman | 178 | 5 |
| 1 | Java | 10 | Tom | 23 | woman | 165 | 5 |
| 2 | MySQL | 10 | Tom | 23 | woman | 165 | 5 |
| 3 | Python | 10 | Tom | 23 | woman | 165 | 5 |
| 4 | Go | 10 | Tom | 23 | woman | 165 | 5 |
| 5 | C++ | 10 | Tom | 23 | woman | 165 | 5 |
+----+-------------+----+--------+------+-------+--------+-----------+
50 rows in set (0.00 sec)
结果示例:每个学生与所有课程组合,如Dany
对应Java、MySQL、Python、Go、C++
5 条记录
筛选有效数据(按外键关联):通过course_id
(学生表外键)与id
(课程表主键)匹配,返回学生与对应课程的关联数据(10 行,与学生数一致)
mysql> select* from course;
+----+-------------+
| id | course_name |
+----+-------------+
| 1 | Java |
| 2 | MySQL |
| 3 | Python |
| 4 | Go |
| 5 | C++ |
+----+-------------+
5 rows in set (0.00 sec)mysql> select * from student123;
+----+--------+------+-------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+--------+------+-------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 2 | Green | 23 | man | 158 | 2 |
| 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Jane | 22 | man | 162 | 3 |
| 5 | Jim | 24 | woman | 175 | 2 |
| 6 | John | 21 | woman | 172 | 4 |
| 7 | Lily | 22 | man | 165 | 4 |
| 8 | Susan | 23 | man | 170 | 5 |
| 9 | Thomas | 22 | woman | 178 | 5 |
| 10 | Tom | 23 | woman | 165 | 5 |
+----+--------+------+-------+--------+-----------+
10 rows in set (0.00 sec)
mysql> select * from student123 cross join course where course.id=student123.course_id;
+----+--------+------+-------+--------+-----------+----+-------------+
| id | name | age | sex | height | course_id | id | course_name |
+----+--------+------+-------+--------+-----------+----+-------------+
| 1 | Dany | 25 | man | 160 | 1 | 1 | Java |
| 2 | Green | 23 | man | 158 | 2 | 2 | MySQL |
| 3 | Henry | 23 | woman | 185 | 1 | 1 | Java |
| 4 | Jane | 22 | man | 162 | 3 | 3 | Python |
| 5 | Jim | 24 | woman | 175 | 2 | 2 | MySQL |
| 6 | John | 21 | woman | 172 | 4 | 4 | Go |
| 7 | Lily | 22 | man | 165 | 4 | 4 | Go |
| 8 | Susan | 23 | man | 170 | 5 | 5 | C++ |
| 9 | Thomas | 22 | woman | 178 | 5 | 5 | C++ |
| 10 | Tom | 23 | woman | 165 | 5 | 5 | C++ |
+----+--------+------+-------+--------+-----------+----+-------------+
10 rows in set (0.00 sec)
2. 内连接(INNER JOIN):匹配有效关联数据
内连接是实际开发中最常用的连接方式,只返回两张表中满足连接条件的记录,自动过滤无关联的无效数据(避免笛卡尔积冗余)
核心特性
- 可省略
INNER
,仅用JOIN
- 必须通过
ON
子句指定连接条件(比WHERE
更规范,性能更优)
语法格式
SELECT <字段名> FROM <表1> [INNER] JOIN <表2> ON <连接条件>;
实例演示
查询学生姓名与对应课程名称(通过course_id = id
关联):
mysql> select s.id,s.name,c.course_name from student123 s join course c on
s.course_id=c.id;
+----+--------+-------------+
| id | name | course_name |
+----+--------+-------------+
| 1 | Dany | Java |
| 3 | Henry | Java |
| 2 | Green | MySQL |
| 5 | Jim | MySQL |
| 4 | Jane | Python |
| 6 | John | Go |
| 7 | Lily | Go |
| 8 | Susan | C++ |
| 9 | Thomas | C++ |
| 10 | Tom | C++ |
+----+--------+-------------+
10 rows in set (0.00 sec)
3. 外连接:保留基表所有数据(左 / 右连接)
内连接仅返回匹配数据,外连接则保留 “基表” 的所有记录,参考表中无匹配时字段值为NULL
。分为左外连接和右外连接,核心是 “基表” 的选择
3.1 左外连接(LEFT JOIN):保留左表所有数据
- 基表:
LEFT JOIN
左侧的表(表 1) - 结果:表 1 的所有记录 + 表 2 中匹配的记录;表 2 无匹配时,表 2 字段为
NULL
语法格式
SELECT <字段名> FROM <表1> LEFT [OUTER] JOIN <表2> ON <连接条件>;
实例演示
查询所有学生(包括无课程的学生)及其课程,学生表student123
新增lq
(course_id=NULL
,课程表无id=6
的课程):
mysql> select * from student123;
+----+--------+------+-------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+--------+------+-------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 2 | Green | 23 | man | 158 | 2 |
| 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Jane | 22 | man | 162 | 3 |
| 5 | Jim | 24 | woman | 175 | 2 |
| 6 | John | 21 | woman | 172 | 4 |
| 7 | Lily | 22 | man | 165 | 4 |
| 8 | Susan | 23 | man | 170 | 5 |
| 9 | Thomas | 22 | woman | 178 | 5 |
| 10 | Tom | 23 | woman | 165 | 5 |
+----+--------+------+-------+--------+-----------+
10 rows in set (0.01 sec)mysql> insert into student123(id,name,age,sex,height) values(11,'lq',25,'man',175);
Query OK, 1 row affected (0.00 sec)mysql> select * from student123;
+----+--------+------+-------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+--------+------+-------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 2 | Green | 23 | man | 158 | 2 |
| 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Jane | 22 | man | 162 | 3 |
| 5 | Jim | 24 | woman | 175 | 2 |
| 6 | John | 21 | woman | 172 | 4 |
| 7 | Lily | 22 | man | 165 | 4 |
| 8 | Susan | 23 | man | 170 | 5 |
| 9 | Thomas | 22 | woman | 178 | 5 |
| 10 | Tom | 23 | woman | 165 | 5 |
| 11 | lq | 25 | man | 175 | NULL |
+----+--------+------+-------+--------+-----------+
11 rows in set (0.00 sec)
mysql> select s.id,s.name,c.course_name from student123 s join course c on
s.course_id=c.id;
+----+--------+-------------+
| id | name | course_name |
+----+--------+-------------+
| 1 | Dany | Java |
| 3 | Henry | Java |
| 2 | Green | MySQL |
| 5 | Jim | MySQL |
| 4 | Jane | Python |
| 6 | John | Go |
| 7 | Lily | Go |
| 8 | Susan | C++ |
| 9 | Thomas | C++ |
| 10 | Tom | C++ |
+----+--------+-------------+
10 rows in set (0.00 sec)
mysql> select s.id,s.name,c.course_name from student123 s left join course
c on s.course_id=c.id;
+----+--------+-------------+
| id | name | course_name |
+----+--------+-------------+
| 1 | Dany | Java |
| 3 | Henry | Java |
| 2 | Green | MySQL |
| 5 | Jim | MySQL |
| 4 | Jane | Python |
| 6 | John | Go |
| 7 | Lily | Go |
| 8 | Susan | C++ |
| 9 | Thomas | C++ |
| 10 | Tom | C++ |
| 11 | lq | NULL |
+----+--------+-------------+
11 rows in set (0.00 sec)
结果示例:lq
的course_name
为NULL
,其余学生显示对应课程。
3.2 右外连接(RIGHT JOIN):保留右表所有数据
- 基表:
RIGHT JOIN
右侧的表(表 2) - 结果:表 2 的所有记录 + 表 1 中匹配的记录;表 1 无匹配时,表 1 字段为
NULL
语法格式
SELECT <字段名> FROM <表1> RIGHT [OUTER] JOIN <表2> ON <连接条件>;
实例演示
查询所有课程(包括无学生的课程)及其学生,课程表course
新增HTML
(id=6
,学生表无course_id=6
的学生):
mysql> insert into course values(6,'HTML');
Query OK, 1 row affected (0.00 sec)mysql> select s.id,s.name,c.course_name from student123 s right join course c on s.course_id=c.id;
+------+--------+-------------+
| id | name | course_name |
+------+--------+-------------+
| 1 | Dany | Java |
| 2 | Green | MySQL |
| 3 | Henry | Java |
| 4 | Jane | Python |
| 5 | Jim | MySQL |
| 6 | John | Go |
| 7 | Lily | Go |
| 8 | Susan | C++ |
| 9 | Thomas | C++ |
| 10 | Tom | C++ |
| NULL | NULL | HTML |
+------+--------+-------------+
11 rows in set (0.00 sec)
结果示例:HTML
的name
为NULL
,其余课程显示对应学生
4. 分组查询(GROUP BY):按字段聚合数据
GROUP BY
可按 1 个或多个字段对查询结果分组,常与聚合函数(COUNT
、SUM
、AVG
等)或GROUP_CONCAT
配合,实现数据统计
核心用法
用法 | 说明 |
---|---|
GROUP BY 字段 | 单独分组,仅显示每组第一条记录(需配合聚合函数才有用) |
GROUP_CONCAT(字段) | 显示每组内指定字段的所有值(用逗号分隔) |
聚合函数 +GROUP BY | 统计每组数据(如COUNT(*) 统计每组行数、AVG(age) 计算每组平均年龄) |
WITH ROLLUP | 在所有分组后追加一行 “总计”(聚合函数的总和) |
实例演示
以student123
(学生表)为例:
统计人数:
mysql> select count(*) from student123;
+----------+
| count(*) |
+----------+
| 11 |
+----------+
1 row in set (0.00 sec)mysql> select count(name) from student123;
+-------------+
| count(name) |
+-------------+
| 11 |
+-------------+
1 row in set (0.00 sec)mysql> select count(course_id) from student123;
+------------------+
| count(course_id) |
+------------------+
| 10 |
+------------------+
1 row in set (0.00 sec)
按性别分组,统计每组人数
mysql> select sex,count(*) from student123 group by sex;
+-------+----------+
| sex | count(*) |
+-------+----------+
| man | 6 |
| woman | 5 |
+-------+----------+
2 rows in set (0.00 sec)
对student123表中age字段的平均值计算
mysql> select avg(age) from student123;
+----------+
| avg(age) |
+----------+
| 23.0000 |
+----------+
1 row in set (0.00 sec)
对student123
表中age
字段的最大值和最小值查询
mysql> select max(age) from student123;
+----------+
| max(age) |
+----------+
| 25 |
+----------+
1 row in set (0.01 sec)mysql> select min(age) from student123;
+----------+
| min(age) |
+----------+
| 21 |
+----------+
1 row in set (0.00 sec)
按性别分组,显示每组学生姓名
mysql> select sex,group_concat(name) from student123 group by sex;
+-------+-------------------------------+
| sex | group_concat(name) |
+-------+-------------------------------+
| man | Dany,Green,Jane,Lily,Susan,lq |
| woman | Henry,Jim,John,Thomas,Tom |
+-------+-------------------------------+
2 rows in set (0.00 sec)
按年龄 + 性别分组,显示总计
mysql> select age,sex,count(*) from student123 group by age,sex;
+------+-------+----------+
| age | sex | count(*) |
+------+-------+----------+
| 21 | woman | 1 |
| 22 | man | 2 |
| 22 | woman | 1 |
| 23 | man | 2 |
| 23 | woman | 2 |
| 24 | woman | 1 |
| 25 | man | 2 |
+------+-------+----------+
7 rows in set (0.00 sec)
mysql> select age,sex,count(*) from student123 group by sex,age;
+------+-------+----------+
| age | sex | count(*) |
+------+-------+----------+
| 22 | man | 2 |
| 23 | man | 2 |
| 25 | man | 2 |
| 21 | woman | 1 |
| 22 | woman | 1 |
| 23 | woman | 2 |
| 24 | woman | 1 |
+------+-------+----------+
7 rows in set (0.00 sec)
分组聚合查询
mysql> select age,sex,group_concat(name) from student123 group by sex,age;
+------+-------+--------------------+
| age | sex | group_concat(name) |
+------+-------+--------------------+
| 22 | man | Jane,Lily |
| 23 | man | Green,Susan |
| 25 | man | Dany,lq |
| 21 | woman | John |
| 22 | woman | Thomas |
| 23 | woman | Henry,Tom |
| 24 | woman | Jim |
+------+-------+--------------------+
7 rows in set (0.01 sec)
分组聚合的基础上生成汇总行
mysql> select sex,group_concat(name) from student123 group by sex with rollup;
+-------+---------------------------------------------------------+
| sex | group_concat(name) |
+-------+---------------------------------------------------------+
| man | Dany,Green,Jane,Lily,Susan,lq |
| woman | Henry,Jim,John,Thomas,Tom |
| NULL | Dany,Green,Jane,Lily,Susan,lq,Henry,Jim,John,Thomas,Tom |
+-------+---------------------------------------------------------+
3 rows in set (0.00 sec)
5. 子查询:嵌套查询(内层结果作为外层条件)
子查询是 “查询中的查询”,将内层查询(子查询)的结果作为外层查询(父查询)的筛选条件,常用于复杂筛选。子查询需用括号包裹,支持多层嵌套
核心操作符
操作符 | 说明 |
---|---|
IN / NOT IN | 外层字段值在 / 不在子查询结果集中。 |
= / <> | 外层字段值等于 / 不等于子查询结果(子查询需返回单个值)。 |
EXISTS | 子查询结果集非空则返回TRUE (仅判断 “是否存在”,不关心具体值)。 |
实例演示
以 “查询学习 Python 课程的学生姓名” 为例:
mysql> select id,name,age,sex from student123 where course_id=((select id from course where course_name='Python');
+----+------+------+------+
| id | name | age | sex |
+----+------+------+------+
| 4 | Jane | 22 | man |
+----+------+------+------+
1 row in set (0.01 sec)
mysql> select * from student123;
+----+--------+------+-------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+--------+------+-------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 2 | Green | 23 | man | 158 | 2 |
| 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Jane | 22 | man | 162 | 3 |
| 5 | Jim | 24 | woman | 175 | 2 |
| 6 | John | 21 | woman | 172 | 4 |
| 7 | Lily | 22 | man | 165 | 4 |
| 8 | Susan | 23 | man | 170 | 5 |
| 9 | Thomas | 22 | woman | 178 | 5 |
| 10 | Tom | 23 | woman | 165 | 5 |
| 11 | lq | 25 | man | 175 | NULL |
+----+--------+------+-------+--------+-----------+
11 rows in set (0.00 sec)mysql> select * from course;
+----+-------------+
| id | course_name |
+----+-------------+
| 1 | Java |
| 2 | MySQL |
| 3 | Python |
| 4 | Go |
| 5 | C++ |
| 6 | HTML |
+----+-------------+
6 rows in set (0.00 sec)
筛选出 “选修了不存在的课程” 的学生(即学生的course_id
对应的值,在course
表中没有匹配的课程记录)
mysql> select name from student123 where course_id not in(select id from course);
Empty set (0.00 sec)
使用<>运算符,在 course 表和 student123表中查询出没有学习 Python 课程的学生姓名,SQL 语句和运行结果如下:
mysql> select name from student123 where course_id <> (selectid from course where course_name = 'Python');
+--------+
| name |
+--------+
| Dany |
| Green |
| Henry |
| Jim |
| John |
| Lily |
| Susan |
| Thomas |
| Tom |
+--------+
9 rows in set (0.00 sec)
查询 course 表中是否存在 id=1 的课程,如果存在,就查询出 student123 表中的记录,SQL 语句和运行结果如下:
mysql> select * from student123-> where exists(select course_name from course where id=1);
+----+--------+------+-------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+--------+------+-------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 2 | Green | 23 | man | 158 | 2 |
| 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Jane | 22 | man | 162 | 3 |
| 5 | Jim | 24 | woman | 175 | 2 |
| 6 | John | 21 | woman | 172 | 4 |
| 7 | Lily | 22 | man | 165 | 4 |
| 8 | Susan | 23 | man | 170 | 5 |
| 9 | Thomas | 22 | woman | 178 | 5 |
| 10 | Tom | 23 | woman | 165 | 5 |
| 11 | lq | 25 | man | 175 | NULL |
+----+--------+------+-------+--------+-----------+
11 rows in set (0.00 sec)
course 表中存在 id=1 的记录,因此 EXISTS 表达式返回 TRUE,外层查询语句接收 TRUE 之后对表 student123 进行查询,返回所有的记录
mysql> select * from student123-> where age>24 and exists(select course_name from coursewhere id=1);
+----+------+------+------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+------+------+------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 11 | lq | 25 | man | 175 | NULL |
+----+------+------+------+--------+-----------+
2 rows in set (0.00 sec)
结果显示,从 tb_students_info 表中查询出了一条记录,这条记录的 age 字段取值为 25。内层查询语句从 course 表中查询到记录,返回 TRUE。外层查询语句开始进行查询。根据查询条件,从 student123 表中查询 age 大于 24 的记录
子查询的功能也可以通过表连接完成,但是子查询会使 SQL 语句更容易阅读和编写。
一般来说,表连接(内连接和外连接等)都可以用子查询替换,但反过来却不一定,有的子查询不能用表连接来替换。子查询比较灵活、方便、形式多样,适合作为查询的筛选条件,而表连接更适合于查看连接表的数据
二、MySQL 数据库备份与恢复
数据库备份是保障数据安全的核心手段,MySQL 常用备份方案包括全量备份、增量备份、差异备份,配合mysqldump
工具和二进制日志实现完整的数据恢复
1. 常用备份方案对比
备份类型 | 核心特点 | 恢复效率 | 备份时间 | 适用场景 |
---|---|---|---|---|
全量备份 | 备份某一时间点的所有数据(如整个数据库、所有表) | 快(仅需全量文件) | 长 | 数据量小、需快速恢复场景 |
增量备份 | 仅备份上一次备份后新增 / 修改的数据(依赖二进制日志) | 慢(需全量 + 所有增量) | 短 | 数据量大、高频更新场景 |
差异备份 | 仅备份上一次全量备份后新增 / 修改的数据(依赖二进制日志) | 中(需全量 + 最后一次差异) | 中 | 数据量较大、更新频率中等 |
2. 备份工具:mysqldump(全量备份核心工具)
mysqldump
是 MySQL 官方提供的命令行工具,支持备份单个数据库、多个数据库、指定表,备份文件为 SQL 脚本(文本格式,可直接执行恢复)
备份整个数据库(全备):
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| shenyi |
| sy |
| sys |
+--------------------+
6 rows in set (0.00 sec)mysql> use shenyi;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changed
mysql> show tables;
+------------------+
| Tables_in_shenyi |
+------------------+
| course |
| student |
| student123 |
+------------------+
3 rows in set (0.00 sec)
[root@syf ~]# ls
anaconda-ks.cfg
[root@syf ~]# mysqldump -uroot -p --all-databases > all-202509171919.sql
Enter password:
[root@syf ~]# ls
all-202509171919.sql
anaconda-ks.cfg
mysql> show tables;
+------------------+
| Tables_in_shenyi |
+------------------+
| course |
| student |
| student123 |
+------------------+
3 rows in set (0.00 sec)mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)
[root@syf ~]# mysql -uroot -predhat
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.37 MySQL Community Server (GPL)Copyright (c) 2000, 2022, Oracle and/or its affiliates.Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> select * from shenyi.student;
ERROR 1146 (42S02): Table 'shenyi.student' doesn't exist
mysql> source /root/all-202509171919.sql;
....
mysql> use shenyi;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changed
mysql> show tables;
+------------------+
| Tables_in_shenyi |
+------------------+
| course |
| student |
| student123 |
+------------------+
3 rows in set (0.00 sec)mysql> select * from student;
+----+-------------+------+
| id | name | age |
+----+-------------+------+
| 1 | tom | 20 |
| 2 | jerry | 23 |
| 3 | shenyi | 25 |
| 4 | sean | 28 |
| 5 | zhangshan | 26 |
| 7 | lisi | 50 |
| 8 | chenshuo | 10 |
| 9 | wangwu | 100 |
| 10 | qiuyi | 15 |
| 11 | qiuxiaotian | 20 |
+----+-------------+------+
10 rows in set (0.00 sec)
备份shenyi库的student123表和student表
[root@syf ~]# mysqldump -uroot -p shenyi student123 student > table-20250917.sql
Enter password:
[root@syf ~]# ls
table-20250917.sql
mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)mysql> drop table student123;
Query OK, 0 rows affected (0.00 sec)mysql> show tables;
+------------------+
| Tables_in_shenyi |
+------------------+
| course |
+------------------+
1 row in set (0.00 sec)mysql> source /root/table-20250917.sql
.....
mysql> show tables;
+------------------+
| Tables_in_shenyi |
+------------------+
| course |
| student |
| student123 |
+------------------+
3 rows in set (0.00 sec)mysql> select * from student;
+----+-------------+------+
| id | name | age |
+----+-------------+------+
| 1 | tom | 20 |
| 2 | jerry | 23 |
| 3 | shenyi | 25 |
| 4 | sean | 28 |
| 5 | zhangshan | 26 |
| 7 | lisi | 50 |
| 8 | chenshuo | 10 |
| 9 | wangwu | 100 |
| 10 | qiuyi | 15 |
| 11 | qiuxiaotian | 20 |
+----+-------------+------+
10 rows in set (0.00 sec)mysql> select * from student123;
+----+--------+------+-------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+--------+------+-------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 2 | Green | 23 | man | 158 | 2 |
| 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Jane | 22 | man | 162 | 3 |
| 5 | Jim | 24 | woman | 175 | 2 |
| 6 | John | 21 | woman | 172 | 4 |
| 7 | Lily | 22 | man | 165 | 4 |
| 8 | Susan | 23 | man | 170 | 5 |
| 9 | Thomas | 22 | woman | 178 | 5 |
| 10 | Tom | 23 | woman | 165 | 5 |
| 11 | lq | 25 | man | 175 | NULL |
+----+--------+------+-------+--------+-----------+
11 rows in set (0.00 sec)
差异备份与恢复
mysql差异备份
开启MySQL服务器的二进制日志功能
[root@syf ~]# vim /etc/my.cnf
[mysqld]
basedir = /usr/local/mysql
datadir = /opt/data
socket = /tmp/mysql.sock
port = 3306
pid-file = /opt/data/mysql.pid
user = mysql
skip-name-resolveserver-id = 1 //设置服务器标识符
log-bin = mysql_bin //开启二进制日志功能
~
[root@syf ~]# systemctl restart mysqld
完全备份:
[root@syf ~]# mysqldump -uroot -predhat --single-transaction --flush-logs --master-data=2 --all-databases --delete-master-logs > all-20250917.sql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
[root@syf ~]# ll /root/all-20250917.sql anaconda-ks.cfg
-rw-------. 1 root root 2028 Jul 23 11:40 anaconda-ks.cfg
-rw-r--r-- 1 root root 880912 Sep 17 20:31 /root/all-20250917.sql
增加新内容:
mysql> insert into shenyi values(8,'hehe',20),(9,'xixi',50);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0mysql> select * from shenyi;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | tom | 20 |
| 2 | jerry | 23 |
| 3 | shenyi | 25 |
| 4 | sean | 28 |
| 5 | zhangshan | 26 |
| 6 | zhangshan | 20 |
| 7 | lisi | NULL |
| 8 | hehe | 20 |
| 9 | xixi | 50 |
+----+-----------+------+
9 rows in set (0.00 sec)mysql> update shenyi set age=40 where id=8;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from shenyi;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | tom | 20 |
| 2 | jerry | 23 |
| 3 | shenyi | 25 |
| 4 | sean | 28 |
| 5 | zhangshan | 26 |
| 6 | zhangshan | 20 |
| 7 | lisi | NULL |
| 8 | hehe | 40 |
| 9 | xixi | 50 |
+----+-----------+------+
9 rows in set (0.00 sec)
mysql差异备份恢复
模拟误删数据
[root@syf ~]# mysql -uroot -predhat -e 'drop database shenyi;'mysql: [Warning] Using a password on the command line interface can be insecure.
[root@syf ~]# mysql -uroot -predhat -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sy |
| sys |
+--------------------+
由上可以看到chenyu这个数据库已被删除
[root@syf ~]# ll /opt/data
total 123012
-rw-r----- 1 mysql mysql 56 Sep 15 20:46 auto.cnf
-rw------- 1 mysql mysql 1680 Sep 15 20:46 ca-key.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 ca.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 client-cert.pem
-rw------- 1 mysql mysql 1676 Sep 15 20:46 client-key.pem
-rw-r----- 1 mysql mysql 1097 Sep 17 20:19 ib_buffer_pool
-rw-r----- 1 mysql mysql 12582912 Sep 17 20:42 ibdata1
-rw-r----- 1 mysql mysql 50331648 Sep 17 20:43 ib_logfile0
-rw-r----- 1 mysql mysql 50331648 Sep 15 20:46 ib_logfile1
-rw-r----- 1 mysql mysql 12582912 Sep 17 20:31 ibtmp1
drwxr-x--- 2 mysql mysql 4096 Sep 17 19:49 mysql
-rw-r----- 1 mysql mysql 878 Sep 17 20:42 mysql_bin.000002 ////////
-rw-r----- 1 mysql mysql 19 Sep 17 20:31 mysql_bin.index
-rw-r----- 1 mysql mysql 6 Sep 17 20:19 mysql.pid
drwxr-x--- 2 mysql mysql 8192 Sep 15 20:46 performance_schema
-rw------- 1 mysql mysql 1676 Sep 15 20:46 private_key.pem
-rw-r--r-- 1 mysql mysql 452 Sep 15 20:46 public_key.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 server-cert.pem
-rw------- 1 mysql mysql 1680 Sep 15 20:46 server-key.pem
drwxr-x--- 2 mysql mysql 56 Sep 17 19:49 sy
-rw-r----- 1 mysql mysql 53098 Sep 17 20:19 syf.example.com.err
drwxr-x--- 2 mysql mysql 8192 Sep 15 20:46 sys
刷新创建新的二进制日志
[root@syf ~]# mysqladmin -uroot -predhat flush-logs
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
[root@syf ~]# ll /opt/data/
total 123016
-rw-r----- 1 mysql mysql 56 Sep 15 20:46 auto.cnf
-rw------- 1 mysql mysql 1680 Sep 15 20:46 ca-key.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 ca.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 client-cert.pem
-rw------- 1 mysql mysql 1676 Sep 15 20:46 client-key.pem
-rw-r----- 1 mysql mysql 1097 Sep 17 20:19 ib_buffer_pool
-rw-r----- 1 mysql mysql 12582912 Sep 17 20:45 ibdata1
-rw-r----- 1 mysql mysql 50331648 Sep 17 20:45 ib_logfile0
-rw-r----- 1 mysql mysql 50331648 Sep 15 20:46 ib_logfile1
-rw-r----- 1 mysql mysql 12582912 Sep 17 20:31 ibtmp1
drwxr-x--- 2 mysql mysql 4096 Sep 17 19:49 mysql
-rw-r----- 1 mysql mysql 925 Sep 17 20:45 mysql_bin.000002
-rw-r----- 1 mysql mysql 154 Sep 17 20:45 mysql_bin.000003
-rw-r----- 1 mysql mysql 38 Sep 17 20:45 mysql_bin.index
-rw-r----- 1 mysql mysql 6 Sep 17 20:19 mysql.pid
drwxr-x--- 2 mysql mysql 8192 Sep 15 20:46 performance_schema
-rw------- 1 mysql mysql 1676 Sep 15 20:46 private_key.pem
-rw-r--r-- 1 mysql mysql 452 Sep 15 20:46 public_key.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 server-cert.pem
-rw------- 1 mysql mysql 1680 Sep 15 20:46 server-key.pem
drwxr-x--- 2 mysql mysql 56 Sep 17 19:49 sy
-rw-r----- 1 mysql mysql 53098 Sep 17 20:19 syf.example.com.err
drwxr-x--- 2 mysql mysql 8192 Sep 15 20:46 sys
恢复完全备份
[root@syf ~]# mysql -uroot -predhat < all-20250917.sql
mysql: [Warning] Using a password on the command line interface can be insecure.
[root@syf ~]# mysql -uroot -predhat -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| shenyi |
| sy |
| sys |
+--------------------+
[root@syf ~]# mysql -uroot -predhat -e 'show tables from shenyi;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+------------------+
| Tables_in_shenyi |
+------------------+
| course |
| student |
| student123 |
+------------------+
[root@syf ~]# mysql -uroot -predhat -e 'select * from shenyi.student;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+-------------+------+
| id | name | age |
+----+-------------+------+
| 1 | tom | 20 |
| 2 | jerry | 23 |
| 3 | shenyi | 25 |
| 4 | sean | 28 |
| 5 | zhangshan | 26 |
| 7 | lisi | 50 |
| 8 | chenshuo | 10 |
| 9 | wangwu | 100 |
| 10 | qiuyi | 15 |
| 11 | qiuxiaotian | 20 |
+----+-------------+------+
[root@syf ~]# mysql -uroot -predhat -e 'select * from shenyi.student123;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+--------+------+-------+--------+-----------+
| id | name | age | sex | height | course_id |
+----+--------+------+-------+--------+-----------+
| 1 | Dany | 25 | man | 160 | 1 |
| 2 | Green | 23 | man | 158 | 2 |
| 3 | Henry | 23 | woman | 185 | 1 |
| 4 | Jane | 22 | man | 162 | 3 |
| 5 | Jim | 24 | woman | 175 | 2 |
| 6 | John | 21 | woman | 172 | 4 |
| 7 | Lily | 22 | man | 165 | 4 |
| 8 | Susan | 23 | man | 170 | 5 |
| 9 | Thomas | 22 | woman | 178 | 5 |
| 10 | Tom | 23 | woman | 165 | 5 |
| 11 | lq | 25 | man | 175 | NULL |
+----+--------+------+-------+--------+-----------+
恢复差异备份
[root@syf ~]# ll /opt/data/
total 124036
-rw-r----- 1 mysql mysql 56 Sep 15 20:46 auto.cnf
-rw------- 1 mysql mysql 1680 Sep 15 20:46 ca-key.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 ca.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 client-cert.pem
-rw------- 1 mysql mysql 1676 Sep 15 20:46 client-key.pem
-rw-r----- 1 mysql mysql 1097 Sep 17 20:19 ib_buffer_pool
-rw-r----- 1 mysql mysql 12582912 Sep 17 20:54 ibdata1
-rw-r----- 1 mysql mysql 50331648 Sep 17 20:54 ib_logfile0
-rw-r----- 1 mysql mysql 50331648 Sep 15 20:46 ib_logfile1
-rw-r----- 1 mysql mysql 12582912 Sep 17 20:31 ibtmp1
drwxr-x--- 2 mysql mysql 4096 Sep 17 20:52 mysql
-rw-r----- 1 mysql mysql 925 Sep 17 20:45 mysql_bin.000002
-rw-r----- 1 mysql mysql 861912 Sep 17 20:52 mysql_bin.000003
-rw-r----- 1 mysql mysql 38 Sep 17 20:45 mysql_bin.index
-rw-r----- 1 mysql mysql 6 Sep 17 20:19 mysql.pid
drwxr-x--- 2 mysql mysql 8192 Sep 15 20:46 performance_schema
-rw------- 1 mysql mysql 1676 Sep 15 20:46 private_key.pem
-rw-r--r-- 1 mysql mysql 452 Sep 15 20:46 public_key.pem
-rw-r--r-- 1 mysql mysql 1112 Sep 15 20:46 server-cert.pem
-rw------- 1 mysql mysql 1680 Sep 15 20:46 server-key.pem
drwxr-x--- 2 mysql mysql 138 Sep 17 20:52 shenyi
drwxr-x--- 2 mysql mysql 56 Sep 17 20:52 sy
-rw-r----- 1 mysql mysql 53098 Sep 17 20:19 syf.example.com.err
drwxr-x--- 2 mysql mysql 8192 Sep 15 20:46 sys
检查误删数据库的位置在什么地方
mysql> show binlog events in 'mysql_bin.000002';
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| mysql_bin.000002 | 4 | Format_desc | 1 | 123 | Server ver: 5.7.37-log, Binlog ver: 4 |
| mysql_bin.000002 | 123 | Previous_gtids | 1 | 154 | |
| mysql_bin.000002 | 154 | Anonymous_Gtid | 1 | 219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql_bin.000002 | 219 | Query | 1 | 289 | BEGIN |
| mysql_bin.000002 | 289 | Table_map | 1 | 340 | table_id: 143 (sy.shenyi) |
| mysql_bin.000002 | 340 | Write_rows | 1 | 403 | table_id: 143 flags: STMT_END_F |
| mysql_bin.000002 | 403 | Xid | 1 | 434 | COMMIT /* xid=524 */ |
| mysql_bin.000002 | 434 | Anonymous_Gtid | 1 | 499 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql_bin.000002 | 499 | Query | 1 | 569 | BEGIN |
| mysql_bin.000002 | 569 | Table_map | 1 | 620 | table_id: 143 (sy.shenyi) |
| mysql_bin.000002 | 620 | Update_rows | 1 | 684 | table_id: 143 flags: STMT_END_F |
| mysql_bin.000002 | 684 | Xid | 1 | 715 | COMMIT /* xid=526 */ |
| mysql_bin.000002 | 715 | Anonymous_Gtid | 1 | 780 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql_bin.000002 | 780 | Query | 1 | 878 | drop database shenyi |
| mysql_bin.000002 | 878 | Rotate | 1 | 925 | mysql_bin.000003;pos=4 |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
15 rows in set (0.01 sec)
使用mysqlbinlog恢复差异备份
[root@syf ~]# mysqlbinlog --stop-position=780 /opt/data/mysql_bin.000002 |mysql -uroot -predhat
mysql: [Warning] Using a password on the command line interface can be insecure.
[root@syf ~]# mysql -uroot -predhat -e 'select * from sy.shenyi;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | tom | 20 |
| 2 | jerry | 23 |
| 3 | shenyi | 25 |
| 4 | sean | 28 |
| 5 | zhangshan | 26 |
| 6 | zhangshan | 20 |
| 7 | lisi | NULL |
| 8 | hehe | 40 |
| 9 | xixi | 50 |
+----+-----------+------+