MySQL 常见面试题完全指南
本文档整理了常见的 MySQL 面试题,按从浅到深的顺序讲解,特别适合新手准备面试或系统复习。每个问题都给出:
- 问题
- 思路
- 标准/推荐回答
- 必要时配合简单示例
你可以:
- 先只看“问题+简答”,自己在脑子里回答一遍;
- 然后对照“解析”补充自己的理解;
- 对不熟的地方回到对应专题文档(如 基本操作、索引、事务 等)复习。
目录
- 基础概念与常规操作
- 数据类型与表设计
- SQL 与 CRUD
- 索引相关面试题
- 事务与隔离级别
- 锁机制
- 日志(binlog、redo log、undo log)
- 性能优化与慢查询
- 设计题与综合题
- 杂项与开放题
1. 基础概念与常规操作
1.1 什么是 MySQL?和“数据库”“数据库管理系统”有什么区别?
考点:基础概念、术语区分。
示例回答:
- 数据库(Database):有结构地存放数据的仓库,可以有很多张表。
- 数据库管理系统(DBMS):管理数据库的“软件”,负责存储、查询、备份、安全等。
- MySQL:一个开源的 关系型数据库管理系统(RDBMS),用 SQL 操作数据,数据以表的形式组织。
可以加一句:MySQL 常用于 Web 应用后端,配合 PHP、Java、Python 等语言,是非常常见的数据库选型。
1.2 什么是表?什么是行和列?
考点:能否把“表结构”说清楚。
示例回答:
- 表(Table):数据库中存放数据的基本结构,可以理解为一张带表头的表格。
- 列(Column/字段):表中某一“属性”,如学生表里的姓名、年龄、班级。
- 行(Row/记录):表中的一条具体数据记录,例如“张三,18,高一1班”就是一行。
补充一句:表的结构由列名和数据类型决定,数据则是一行一行的记录。
1.3 MySQL 中常见的约束有哪些?分别有什么作用?
考点:约束类型、作用场景。
关键点:
- PRIMARY KEY:主键,唯一标识一行,不能重复、不能为 NULL。
- UNIQUE:唯一约束,列值不能重复(可为 NULL)。
- NOT NULL:非空,列值必须有,不允许 NULL。
- DEFAULT:默认值,插入时未指定该列就用默认值。
- CHECK(8.0.16+):检查约束,列值必须满足条件,如年龄 0~150。
- FOREIGN KEY:外键,保证该列的取值必须在另一张表的主键或唯一键中存在。
可以举例学生-班级、订单-用户来说明外键约束的作用。
1.4 如何查看当前有哪些数据库?如何选择要使用的数据库?
考点:基本命令是否熟练。
关键命令:
- 查看所有数据库:
SHOW DATABASES;
- 选择(切换)数据库:
USE 库名;
- 查看当前正在使用的数据库:
SELECT DATABASE();
1.5 MySQL 中如何查看某个表的结构?如何查看当前库下所有表?
命令:
- 查看所有表:
SHOW TABLES;
- 查看表结构:
DESC 表名;
-- 或
DESCRIBE 表名;
2. 数据类型与表设计
2.1 MySQL 中常见的数据类型有哪些?如何选择?
考点:能否说出主流类型并举例。
简要回答:
- 数值:
- INT / BIGINT:整数,如 id、数量。
- DECIMAL(M,D):精确小数,如金额、分数。
- FLOAT / DOUBLE:近似小数,如比例、统计值。
- 字符串:
- VARCHAR(n):可变长字符串,适合大部分文本,如姓名、标题。
- CHAR(n):定长字符串,如固定长度编码。
- TEXT / LONGTEXT:长文本,如文章内容。
- 日期时间:
- DATE:日期。
- TIME:时间。
- DATETIME / TIMESTAMP:日期 + 时间。
可以补一句:金额一般用 DECIMAL,而不是 FLOAT/DOUBLE,以避免精度问题。
2.2 VARCHAR 和 CHAR 有什么区别?什么时候用 VARCHAR,什么时候用 CHAR?
考点:存储与性能差异。
回答要点:
- CHAR(n):定长字符串:
- 不足长度时用空格填充;
- 适合长度固定、变化不大的字段,如性别(CHAR(1))、国家代码、固定长度的编码。
- VARCHAR(n):变长字符串:
- 按实际长度存储,多一个长度字节;
- 适合大部分长度不固定的文本,如姓名、地址、标题。
一般建议:绝大多数情况使用 VARCHAR,只有真的是定长字段(且长度不大)才考虑 CHAR。
2.3 为什么建议每张表都有一个自增主键 id?
考点:主键设计意识。
回答示例:
- 自增整数主键(INT/BIGINT)简单、稳定,便于:
- 在程序中做唯一标识;
- 做外键引用;
- 做聚簇索引时减少页分裂(顺序插入)。
- 业务字段(如身份证号、手机号)做主键:
- 可能会变化;
- 字段较长作为聚簇索引会影响性能和存储。
可以提到:逻辑上“主键应该稳定、不随业务变化”,自增 id 是典型实现。
3. SQL 与 CRUD
3.1 写出一条插入语句、一条更新语句、一条删除语句和一条简单查询语句
考点:基础 CRUD 是否熟悉。
示例:
-- 插入
INSERT INTO students (name, gender, age, class_name)
VALUES ('张三', '男', 18, '高一1班');
-- 更新
UPDATE students
SET age = 19
WHERE id = 1;
-- 删除
DELETE FROM students
WHERE id = 1;
-- 查询
SELECT id, name, age
FROM students
WHERE class_name = '高一1班';
面试时可以顺便强调:UPDATE/DELETE 一定要加 WHERE,否则会改/删全表。
3.2 WHERE 和 HAVING 有什么区别?
考点:分组与过滤的执行顺序。
回答要点:
- WHERE:
- 在 分组(GROUP BY)之前执行;
- 用来过滤“行”,不允许使用聚合函数(如 COUNT、AVG 等)。
- HAVING:
- 在 分组(GROUP BY)之后执行;
- 用来过滤“组”,常用聚合函数(如 HAVING COUNT(*) > 2)。
可以举例:
-- 先用 WHERE 过滤,只统计高一1班
SELECT class_name, COUNT(*) AS 人数
FROM students
WHERE class_name = '高一1班'
GROUP BY class_name
HAVING COUNT(*) > 2; -- 再按组的人数筛选
3.3 INNER JOIN、LEFT JOIN、RIGHT JOIN 的区别?
考点:连接类型、结果集差异。
回答要点:
- INNER JOIN(内连接):
- 只保留左右表都能匹配上的行;
- 不匹配的行不会出现在结果中。
- LEFT JOIN(左连接):
- 以左表为基准,左表所有行都保留;
- 右表匹配上的行会拼上,匹配不上时右表列为 NULL。
- RIGHT JOIN(右连接):
- 以右表为基准,右表所有行都保留;
- 左表匹配不上时左表列为 NULL;
- 一般可以通过调整书写顺序 + LEFT JOIN 代替。
建议配一个学生-班级的小例子来解释。
4. 索引相关面试题
4.1 什么是索引?有什么优点和缺点?
考点:索引本质、利弊。
回答要点:
- 索引是建立在表的某些列上的数据结构(一般是 B+ 树),用来加快数据的查找和排序。
- 优点:
- 大幅提高查询速度,尤其是 WHERE、ORDER BY、GROUP BY、JOIN 中使用的列。
- 降低 I/O,减少全表扫描。
- 缺点:
- 需要额外的存储空间;
- 写操作(INSERT/UPDATE/DELETE)需要维护索引结构,写入速度会变慢;
- 过多或设计不合理的索引会适得其反。
4.2 MySQL 中有哪些类型的索引?
考点:概念区分。
回答要点:
- 按用途:
- 主键索引(PRIMARY KEY):主键列自动创建,唯一且非空。
- 唯一索引(UNIQUE):列值不能重复。
- 普通索引(INDEX/KEY):只加速查询,不限制列值。
- 全文索引(FULLTEXT):用于文本搜索(如匹配单词),InnoDB 从 5.6 开始支持。
- 按列数:
- 单列索引:只包含一列。
- 复合索引(联合索引):包含多列,如 INDEX (col1, col2)。
可补一句:大多数情况下我们说“建索引”就是建 B+ 树索引,具体实现取决于存储引擎(如 InnoDB)。
4.3 什么是联合(复合)索引?什么是“最左前缀原则”?
考点:复合索引使用规则。
回答要点:
- 复合索引:在多列上一起建立一个索引,例如 INDEX (class_name, age)。
- 最左前缀原则:
- 复合索引会从最左边的列开始起作用;
- 能用到索引的条件必须包含索引的前缀列:
(class_name, age)可加速:WHERE class_name = ?WHERE class_name = ? AND age = ?
- 但对
WHERE age = ?单独使用时往往用不上这个联合索引。
面试时可以画一小段:
想象索引是按 class_name 再按 age 排序的一本“电话簿”,只写 age 就没法利用前面的顺序了。
4.4 什么情况下适合建索引?什么情况下不适合建索引?
考点:索引设计意识。
适合建索引:
- 经常出现在 WHERE 条件、ORDER BY、GROUP BY、JOIN 条件中的列。
- 区分度较高的列(不同值较多,如用户 id、订单号)。
- 外键列(用于关联查询)。
不适合建索引:
- 频繁更新的列(特别是值变化很大又经常修改的列),索引维护成本高。
- 区分度很低的列(如性别、布尔值),一般不单独建索引。
- 表非常小(几百行),全表扫描也很快。
- 仅参与计算但不参与筛选/排序的列。
可以总结一句:多查少写的列更适合建索引;少查多写的列尽量少建索引。
4.5 覆盖索引是什么?
考点:优化方向。
简要回答:
- 覆盖索引:查询所需的列都在索引里,不需要回表(再访问数据页),直接从索引就能返回结果。
- 例如索引
(name, age),查询SELECT name, age FROM students WHERE name = '张三';时,如果条件和返回列都在这个索引中,就可以只访问索引,减少 I/O。
可以补一句:覆盖索引能显著减少磁盘访问,是常见的优化手段之一。
5. 事务与隔离级别
5.1 什么是事务(Transaction)?有哪四大特性(ACID)?
考点:事务基础。
回答要点:
- 事务:一组要么全部成功、要么全部失败的操作,是数据库保证数据一致性的重要机制。
- ACID:
- A(Atomicity,原子性):事务中的操作要么全部成功,要么全部失败回滚。
- C(Consistency,一致性):事务前后,数据库从一种一致状态转变为另一种一致状态。
- I(Isolation,隔离性):并发事务互不影响,看起来像顺序执行一样。
- D(Durability,持久性):事务提交后,对数据的修改是持久的,即使系统故障也不会丢失(依赖日志、落盘机制)。
5.2 MySQL 有哪些事务隔离级别?各自会出现哪些并发问题?
考点:四个隔离级别、常见并发现象。
隔离级别(从低到高):
- READ UNCOMMITTED(读未提交)
- 可能出现:脏读、不可重复读、幻读。
- READ COMMITTED(读已提交)
- 解决脏读(只能读到已提交的数据)。
- 仍可能出现:不可重复读、幻读。
- REPEATABLE READ(可重复读) —— MySQL InnoDB 默认
- 在同一事务中,多次读取同一行结果一致(无不可重复读)。
- 在 MySQL 的 MVCC + 间隙锁机制下,通常也能避免大部分幻读问题。
- SERIALIZABLE(可串行化)
- 最高级别,所有事务串行执行,避免所有并发问题,但性能最差。
常见并发问题简单解释:
- 脏读:读到了“别的事务尚未提交”的数据。
- 不可重复读:同一事务里,两次读到同一行的结果不同(因为其他事务更新并提交了该行)。
- 幻读:同一事务里,两次统计/查询满足条件的行数不一样(别的事务插入或删除了一些符合条件的行)。
5.3 InnoDB 与 MyISAM 的区别?
考点:存储引擎差异。
要点:
- InnoDB:
- 支持事务、行级锁、外键;
- 默认存储引擎,可靠性好,适合大部分 OLTP 场景。
- MyISAM:
- 不支持事务、外键,锁粒度是表级锁;
- 插入/查询速度在某些场景下较快,但安全性和并发能力较差;
- 在新项目中已较少使用。
可以总结:现在大多数情况下我们都选 InnoDB。
6. 锁机制
6.1 行锁和表锁有什么区别?MySQL 里分别如何实现?
考点:锁粒度、性能影响。
回答要点:
- 表锁:
- 对整张表加锁;
- 并发度低,但开销小;
- MyISAM 主要使用表锁。
- 行锁:
- 只锁某一行或某几行;
- 并发度高,但实现更复杂,开销相对大;
- InnoDB 支持行级锁(也会在必要时退化为间隙锁/表锁)。
可以提到:InnoDB 通过索引实现行级锁,对没有索引的查询可能退化为锁全表。
6.2 什么是死锁?如何避免?
考点:并发控制意识。
简要回答:
- 死锁:两个(或多个)事务互相持有对方需要的锁,并都在等待对方释放,导致都无法继续执行。
- 避免方式:
- 访问表和行的顺序尽量一致,减少交叉锁;
- 尽量保持事务简短,避免长事务;
- 尽早释放锁;
- 合理设计索引,减少锁范围。
MySQL InnoDB 会自动检测死锁并回滚其中一个事务,需要在应用层做好重试逻辑。
7. 日志(binlog、redo log、undo log)
7.1 MySQL 中有哪些重要日志?作用是什么?
考点:日志类型及用途。
主要日志:
- 二进制日志(binlog):
- 记录对数据库进行的“写操作”的逻辑日志(如 INSERT、UPDATE、DELETE);
- 用于主从复制、数据恢复(基于 point-in-time 恢复)。
- 重做日志(redo log) —— InnoDB 专有:
- 记录页的物理修改,用于崩溃恢复(保证事务持久性);
- 提高写入性能(先写日志,再异步刷盘)。
- 回滚日志(undo log):
- 记录旧数据,用于事务回滚和实现 MVCC(多版本并发控制)。
- 错误日志(error log):记录启动、关闭、运行错误信息。
- 慢查询日志(slow query log):记录执行时间超过阈值的 SQL,便于优化。
面试中常被问的是 binlog、redo log、undo log 三者的区别与作用。
8. 性能优化与慢查询
8.1 如何定位并优化慢查询?
考点:排查思路。
典型回答结构:
- 开启慢查询日志:
- 记录执行时间超过某个阈值的 SQL(如
long_query_time = 1s)。
- 记录执行时间超过某个阈值的 SQL(如
- 使用 EXPLAIN 分析执行计划:
- 查看是否走了索引(key 列);
type是否为 ALL(全表扫描);rows预估扫描行数是否太大。
- 检查索引设计:
- 为 WHERE、JOIN、ORDER BY、GROUP BY 中的高频列建合适的索引;
- 避免函数操作导致索引失效;
- 合理使用复合索引(遵循最左前缀)。
- 优化 SQL 写法:
- 减少
SELECT *,只查必要列; - 避免不必要的子查询、嵌套;
- 对大表分页、分批处理。
- 减少
- 必要时考虑表结构与系统层面优化:
- 规范化/反规范化;
- 分表分库、读写分离等(进阶)。
8.2 EXPLAIN 输出中哪些字段比较重要?
考点:能读懂基础执行计划。
回答要点:
- 重要字段:
- type:访问类型,常见值有 ALL、index、range、ref、eq_ref、const 等。
- 从好到坏大致:
system > const > eq_ref > ref > range > index > ALL - ALL 表示全表扫描。
- key:实际用到的索引名,为 NULL 表示没用索引。
- rows:预估扫描的行数,越少越好。
- Extra:额外信息,常见如 Using where / Using index / Using filesort 等。
可以简单解释:关注是否走索引(key)、是否全表扫描(type=ALL)、扫描行数是否太大。
9. 设计题与综合题
9.1 如何设计一个学生-课程-成绩的表结构?
考点:三表设计、一对多、多对多。
推荐答案思路:
- 学生表 students:
CREATE TABLE students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
gender CHAR(1),
class_name VARCHAR(20)
);
- 课程表 courses:
CREATE TABLE courses (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
- 选课/成绩表 student_course_scores(中间表,多对多关系 + 成绩):
CREATE TABLE student_course_scores (
id INT PRIMARY KEY AUTO_INCREMENT,
student_id INT NOT NULL,
course_id INT NOT NULL,
score DECIMAL(5,2),
UNIQUE KEY uk_stu_course (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES students(id),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
说明:
- 学生和课程是多对多,用中间表表示;
UNIQUE (student_id, course_id)保证一个学生一门课只有一条记录;- 外键保证 student_id / course_id 的合法性(视项目是否实际启用外键)。
9.2 假如有一张访问日志表非常大(上亿条),你会如何优化它的查询?
考点:大表优化思路。
可回答方向:
- 合理建索引:
- 针对常用查询条件(如 user_id、url、时间范围)建立合适的单列或复合索引;
- 避免在索引列上用函数或不等值导致索引失效。
- 分区/分表(进阶):
- 按时间(按天、按月)分区或分表,减少单个查询扫描数据量;
- 老数据归档到历史表。
- 限制查询范围:
- 前端分页 + 限制最大可查时间区间;
- 避免一次性扫全历史。
- 使用缓存:
- 对热点统计结果做缓存(例如 Redis),减少重复查询。
10. 杂项与开放题
10.1 MySQL 中如何防止 SQL 注入?
考点:安全意识。
回答要点:
- 不要直接字符串拼接 SQL;
- 使用预编译语句(Prepared Statement) 或 ORM 框架提供的参数绑定;
- 对输入做必要的校验、长度限制;
- 严格控制数据库用户权限(只给必要权限)。
10.2 有哪些常用的 MySQL 优化经验?
可以罗列几个关键点:
- SQL 层面:
- 避免 SELECT *,只查必要列;
- WHERE 条件写在索引列上,避免函数包裹索引列;
- 减少子查询嵌套,适当改用 JOIN;
- 用 LIMIT 做分页和限制结果大小。
- 索引层面:
- 为高频查询、排序、分组列建合适索引;
- 合理设计复合索引(最左前缀);
- 清理长期不用、重复或低效索引。
- 表设计:
- 字段类型选择合理,避免过度宽表;
- 每表有主键,自增整数主键常见;
- 必要时归档历史数据。
- 运维层面:
- 打开慢查询日志,定期分析;
- 合理设置缓存、连接数等参数。
小结与面试建议
- 概念题:多用“先下定义,再举例子,再说优缺点”的结构回答;
- 操作题:脑中有 SQL 语法模板,尽量写出完整语句;
- 设计题:先讲清关系(一对多、多对多),再给出表结构;
- 优化题:从 SQL → 索引 → 表设计 → 系统/架构层面,分层次回答。
建议你:
- 先通读本文,把不懂的地方在前面的专题文档中查一遍;
- 自己拿纸写几遍索引、事务、Join 类型、隔离级别等核心知识点;
- 在本机 MySQL 上把常用命令(CRUD、建表、索引、事务、备份恢复)实操 1~2 遍,效果会更好。**