1. 面试中被问到的问题

  1. 数据库中范式是什么?

设计数据库表时, 用来减少数据冗余,避免更新异常的一套规范

常见的有三范式:

  1. 第一范式: 字段不可再拆,保证原子性

  2. 第二范式: 非主键字段必须完全依赖主键

  3. 第三范式: 非主键字段不能依赖其它非主键字段


  1. MySql的主键和外键分别是什么? 有什么区别?

主键是: 用来唯一标识一张表中的一行数据

外键是: 用来标识两张表之间的关联关系, 通常外键指向的是另一张表的主键

主键和外键的区别?

主键强调唯一标识, 外键强调数据关联和引用完整性.

2. 高频面试问题

优先级

题目

1

MySQL 索引是什么

2

为什么 InnoDB 使用 B+ 树

3

聚簇索引和非聚簇索引区别

4

什么是回表

5

什么是覆盖索引

6

什么是最左前缀原则

7

哪些情况会导致索引失效

8

事务 ACID

9

MySQL 隔离级别

10

redo log、undo log、binlog 区别

先别先死磕 MVCC、间隙锁、两阶段提交这种深题。先把索引这条线背熟,因为面试官最容易从“索引是什么”一路追到“B+树、回表、覆盖索引、最左前缀、索引失效、Explain”。先把这条线答顺了,比零散背 25 题有用得。


A. SQL 与基础设计

1. MySQL查询语句执行顺序

核心就是一句话: 先找表, 然后过滤,然后分组,然后过滤组, 再选字段, 最后排序分页

MySQL的执行顺序不是按照书写顺序执行的, 逻辑执行顺序一般是先From确定数据来源, 再where进行过滤, 然后group by进行分组, having过滤分组,接着select选择字段, 最后order by进行排序, limit限制返回条数

举一个例子:

SELECT department, COUNT(*) AS cnt
FROM employee
WHERE age > 25
GROUP BY department
HAVING cnt > 3
ORDER BY cnt DESC
LIMIT 10;
  1. 从emploee表取数据

  2. 先筛选age>25

  3. 再按department进行分组

  4. 再保留人数大于三的分组

  5. 查询部门和人数

  6. 按人数倒序进行排序

  7. 只返回前10条

2. WHEREHAVING 区别

核心一句话:

WHERE 是分组前过滤行,HAVING 是分组后过滤组。

面试回答:

WHERE 用于 GROUP BY 之前,过滤的是原始数据行,一般不能直接使用聚合函数;HAVING 用于 GROUP BY 之后,过滤的是分组后的聚合结果,可以使用 COUNTSUMAVG 等聚合函数。

例子:

SELECT department, COUNT(*) AS cnt
FROM employee
WHERE age > 25
GROUP BY department
HAVING COUNT(*) > 3;

这里:

  • WHERE age > 25:先过滤年龄大于 25 的员工

  • HAVING COUNT(*) > 3:再过滤人数大于 3 的部门


3. INNER JOINLEFT JOINRIGHT JOIN 区别

核心一句话:

INNER JOIN 只保留两边都匹配的数据;LEFT JOIN 保留左表全部数据;RIGHT JOIN 保留右表全部数据。

面试回答:

INNER JOIN 表示内连接,只返回两张表中能匹配上的数据;LEFT JOIN 表示左连接,会返回左表的全部数据,右表匹配不到的字段用 NULL 填充;RIGHT JOIN 表示右连接,会返回右表的全部数据,左表匹配不到的字段用 NULL 填充。


4. COUNT(*)COUNT(1)COUNT(列名) 区别

核心一句话:

COUNT(*)COUNT(1) 都统计行数,COUNT(列名) 不统计 NULL

面试回答:

COUNT(*) 统计表中满足条件的总行数,包括值为 NULL 的行;COUNT(1) 本质也是统计行数,和 COUNT(*) 差别很小;COUNT(列名) 只统计该列不为 NULL 的行数。所以如果只是统计总行数,通常推荐使用 COUNT(*),语义最清晰。


5. DELETETRUNCATEDROP 区别

核心一句话:

DELETE 删除数据,TRUNCATE 清空表,DROP 删除表结构。

面试回答:

DELETE 是 DML 操作,可以带 WHERE 条件删除部分数据,会记录日志,可以回滚;TRUNCATE 是清空整张表,不能加 WHERE,速度通常更快,会重置自增 ID;DROP 是直接删除整张表,包括表结构和数据。

语句

作用

DELETE

删除部分或全部数据,表还在

TRUNCATE

清空整张表,表结构还在

DROP

删除整张表,表结构也没了


6. CHARVARCHAR 区别

核心一句话:

CHAR 定长,VARCHAR 变长。

面试回答:

CHAR 是定长字符串,比如 CHAR(10),即使只存 3 个字符,也会占用固定长度;VARCHAR 是变长字符串,只占用实际内容长度加额外长度记录。CHAR 适合长度固定的字段,比如性别、状态码;VARCHAR 适合长度不固定的字段,比如用户名、标题、邮箱。


7. 金额字段为什么用 DECIMALBIGINT

核心一句话:

金额不能用 FLOAT / DOUBLE,因为它们有精度误差。

面试回答:

金额字段通常不能用 FLOATDOUBLE,因为它们是浮点数,存在精度误差。一般有两种做法:一种是用 DECIMAL 保存精确小数,比如 DECIMAL(10,2);另一种是用 BIGINT 以“分”为单位存储整数,比如 100.25 元存成 10025 分。这样可以避免金额计算误差。


B. 索引

8. MySQL 索引是什么?

核心一句话:

索引是帮助 MySQL 快速查找数据的数据结构,类似书的目录。

面试回答:

MySQL 索引是一种用于提高查询效率的数据结构,常见实现是 B+ 树。没有索引时,MySQL 可能需要全表扫描;有索引后,可以通过索引快速定位数据,减少扫描行数。但索引也会占用额外空间,并且会降低插入、更新、删除的性能,所以不是越多越好。


9. 为什么 InnoDB 使用 B+ 树?

核心一句话:

B+ 树层高低、范围查询强、磁盘 IO 少,适合数据库索引。

面试回答:

InnoDB 使用 B+ 树,主要是因为 B+ 树非常适合磁盘存储场景。它的非叶子节点只存索引 key,不存完整数据,所以一个节点可以存更多 key,树的高度更低,查询时磁盘 IO 更少。同时 B+ 树的叶子节点之间通过链表连接,非常适合范围查询和排序查询。

可以补一句:

相比二叉树,B+ 树更矮;相比 Hash 索引,B+ 树支持范围查询和排序。


10. 聚簇索引和非聚簇索引区别

核心一句话:

聚簇索引的叶子节点存整行数据,非聚簇索引的叶子节点存主键值。

面试回答:

在 InnoDB 中,主键索引就是聚簇索引,叶子节点存储的是整行数据;普通索引是非聚簇索引,叶子节点存储的是索引字段值和对应的主键值。如果通过普通索引查到了主键,但查询字段不在索引中,就需要再根据主键去聚簇索引中查整行数据,这个过程叫回表。


11. 什么是回表?

核心一句话:

通过普通索引查到主键后,再去主键索引查整行数据,这个过程叫回表。

面试回答:

回表通常发生在使用普通索引查询时。因为普通索引的叶子节点只保存索引字段和主键值,如果查询的字段不在普通索引里,MySQL 需要先通过普通索引找到主键,再根据主键去聚簇索引中查询完整数据,这个过程就叫回表。

例子:

SELECT name, age FROM user WHERE name = '张三';

如果只有 name 索引,但 age 不在索引里,就可能需要回表。


12. 什么是覆盖索引?

核心一句话:

查询需要的字段都在索引里,不需要回表,就是覆盖索引。

面试回答:

覆盖索引是指 SQL 查询需要的字段都可以从索引中直接获取,不需要再回到聚簇索引查询整行数据。覆盖索引可以减少回表次数,提高查询性能。

例子:

CREATE INDEX idx_name_age ON user(name, age);

SELECT name, age FROM user WHERE name = '张三';

这里 nameage 都在联合索引 idx_name_age 中,就可能走覆盖索引。


13. 什么是最左前缀原则?

核心一句话:

联合索引从最左边字段开始连续匹配,不能跳过中间字段。

面试回答:

最左前缀原则是指使用联合索引时,查询条件必须从索引最左边的字段开始连续匹配。如果跳过了最左字段,或者中间字段断开,后面的字段就可能无法充分利用索引。

例子:

CREATE INDEX idx_a_b_c ON t(a, b, c);

可以使用索引:

WHERE a = 1;
WHERE a = 1 AND b = 2;
WHERE a = 1 AND b = 2 AND c = 3;

不符合最左前缀:

WHERE b = 2;
WHERE c = 3;
WHERE b = 2 AND c = 3;

14. 哪些情况会导致索引失效?

核心一句话:

对索引列做函数、计算、隐式转换、左模糊匹配,或者不符合最左前缀,都可能导致索引失效。

面试回答:

常见索引失效场景包括:对索引列使用函数或表达式计算;字符串字段没有加引号导致隐式类型转换;使用 LIKE '%xxx' 这种左模糊匹配;联合索引不符合最左前缀原则;使用 OR 时部分条件没有索引;以及查询条件选择性太差时,优化器可能选择全表扫描。

常见例子:

WHERE YEAR(create_time) = 2024;
WHERE age + 1 = 20;
WHERE phone = 123456;
WHERE name LIKE '%三';

15. 慢 SQL 怎么排查?

核心一句话:

先定位慢 SQL,再用 Explain 分析执行计划,重点看是否走索引、扫描行数、是否回表、是否排序临时表。

面试回答:

排查慢 SQL 一般先通过慢查询日志定位具体 SQL,然后用 EXPLAIN 查看执行计划,重点看 typekeyrowsExtra 等字段,判断是否走了索引、扫描行数是否过多、是否出现 Using filesortUsing temporary。然后根据情况优化索引、改写 SQL、减少返回字段、避免回表,必要时做分页优化、分库分表或缓存。


16. Explain 主要看哪些字段?

核心一句话:

主要看 typekeyrowsExtra

面试回答:

EXPLAIN 主要用来查看 SQL 的执行计划。我一般重点看几个字段:type 表示访问类型,判断是全表扫描还是索引查询;key 表示实际使用的索引;rows 表示预估扫描行数;Extra 表示额外信息,比如是否出现 Using filesortUsing temporaryUsing index。其中 type 越接近 constrefrange 越好,ALL 通常表示全表扫描,需要重点关注。


C. 事务与 MVCC

17. 什么是事务 ACID?

核心一句话:

ACID 是事务的四个特性:原子性、一致性、隔离性、持久性。

面试回答:

事务的 ACID 包括四个特性:原子性表示事务中的操作要么全部成功,要么全部失败;一致性表示事务执行前后数据要满足约束和业务规则;隔离性表示多个事务并发执行时互不干扰;持久性表示事务一旦提交,数据就会被持久保存,即使数据库宕机也不应该丢失。

特性

含义

A 原子性

要么全成功,要么全失败

C 一致性

数据前后符合约束

I 隔离性

并发事务互不影响

D 持久性

提交后数据不丢


18. MySQL 有哪些隔离级别?

核心一句话:

读未提交、读已提交、可重复读、串行化。MySQL 默认是可重复读。

面试回答:

MySQL 事务隔离级别有四种:读未提交、读已提交、可重复读、串行化。读未提交可能出现脏读;读已提交解决脏读,但可能出现不可重复读;可重复读解决不可重复读,是 MySQL InnoDB 默认隔离级别;串行化隔离级别最高,但并发性能最差。

隔离级别

可能问题

Read Uncommitted

脏读、不可重复读、幻读

Read Committed

不可重复读、幻读

Repeatable Read

理论上可能幻读,InnoDB 通过 MVCC + 锁机制解决大部分幻读问题

Serializable

基本避免并发问题,但性能差


19. 脏读、不可重复读、幻读分别是什么?

核心一句话:

脏读读到未提交数据;不可重复读是同一行前后读到不同值;幻读是前后查询到的记录数量不一致。

面试回答:

脏读是一个事务读到了另一个事务还没有提交的数据,如果对方回滚,就读到了无效数据。不可重复读是同一个事务中,两次读取同一行数据,结果不一致,通常是因为其他事务修改并提交了这行数据。幻读是同一个事务中,两次按照相同条件查询,结果集数量不一致,通常是其他事务插入或删除了符合条件的数据。


20. MVCC 是怎么实现的?

核心一句话:

MVCC 通过隐藏字段、undo log 和 ReadView 实现快照读。

面试回答:

MVCC 是多版本并发控制,主要用来提高并发读写性能。InnoDB 中每行数据有隐藏字段,比如事务 ID 和回滚指针;更新数据时,会通过 undo log 保留旧版本;事务查询时会生成 ReadView,根据 ReadView 判断当前事务能看到哪个版本的数据。这样普通查询可以读取快照版本,不需要加锁,从而实现读写并发。


21. undo log 有什么作用?

核心一句话:

undo log 用于事务回滚和 MVCC 版本链。

面试回答:

undo log 主要有两个作用:第一是用于事务回滚,当事务执行失败或主动回滚时,可以根据 undo log 恢复到修改前的数据;第二是用于 MVCC,当其他事务需要读取旧版本数据时,可以通过 undo log 形成的版本链找到可见的数据版本。


22. 当前读和快照读区别?

核心一句话:

快照读读历史版本,不加锁;当前读读最新版本,通常要加锁。

面试回答:

快照读是读取数据的历史版本,普通 SELECT 一般就是快照读,依赖 MVCC 实现,不会加锁。当前读读取的是数据的最新版本,并且通常会加锁,比如 SELECT ... FOR UPDATEUPDATEDELETEINSERT 都属于当前读。当前读需要保证读到的是最新数据,并防止其他事务并发修改。


D. 锁与日志

23. MySQL 有哪些锁?行锁、表锁、间隙锁、临键锁分别是什么?

核心一句话:

表锁锁整张表,行锁锁具体行,间隙锁锁范围间隙,临键锁等于行锁 + 间隙锁。

面试回答:

MySQL 常见锁包括表锁、行锁、间隙锁、临键锁等。表锁是锁整张表,粒度大,并发性能低;行锁是锁具体的数据行,粒度小,并发性能高,是 InnoDB 常用锁;间隙锁锁的是索引记录之间的范围,用来防止其他事务在这个范围插入数据;临键锁是行锁和间隙锁的组合,锁住某条记录以及它前面的间隙,主要用于解决幻读问题。


24. redo logundo logbinlog 分别有什么作用?

核心一句话:

redo log 保证持久性,undo log 保证回滚和 MVCC,binlog 用于主从复制和数据恢复。

面试回答:

redo log 是 InnoDB 的重做日志,主要保证事务的持久性,数据库宕机后可以通过 redo log 恢复已提交事务的数据;undo log 是回滚日志,用于事务回滚和 MVCC 版本链;binlog 是 MySQL Server 层的二进制日志,记录数据库变更操作,主要用于主从复制和基于时间点的数据恢复。

日志

作用

redo log

崩溃恢复,保证持久性

undo log

回滚,MVCC

binlog

主从复制,数据恢复


25. 为什么 redo logbinlog 要两阶段提交?

核心一句话:

为了保证 redo logbinlog 的一致性,避免主库和从库数据不一致。

面试回答:

redo log 是 InnoDB 层的日志,binlog 是 MySQL Server 层的日志。一个事务提交时,两份日志都要写。如果只写成功其中一个,可能会导致数据库崩溃恢复后的数据和 binlog 中记录的数据不一致,从而影响主从复制和数据恢复。所以 MySQL 使用两阶段提交:先把 redo log 写成 prepare 状态,再写 binlog,最后提交 redo log。这样即使中间宕机,也可以根据两份日志的状态判断事务是否应该提交,保证一致性。