Skip to content

食用指南:

1、选择一个知识点

2、阅读知识点对应的“必学资料”

3、背题

ps:

1、答案由 AI 生成,仅供参考。

2、个人认为背题最合理的方式是参透题目背后的知识点,用自己的语言组织出答案,只有这样才能将知识点内化于心,面试时游刃有余。

3、针对第二步,个人认为不是必需项。你完全可以直接开始背题,如果在背题过程中有疑问大可请教 AI,但如果你想深入了解底层原理,广泛阅读和深度思考是必不可少的。

4、在面试、看面经过程中注意多多积累,形成自己的专属题库。

🧭 目录


一、SQL 基础与范式(🌟)

答题/背诵技巧

必学资料

面试题逐题回答

  1. 数据库三大范式是什么?
    第一范式要求字段不可再分,这是关系型的底线;第二范式在满足 1NF 基础上,要求非主属性必须完全依赖整个主键,消除部分依赖;第三范式进一步要求任何非主属性不能传递依赖于主键,即消除‘主键→A→B’的传递路径。实际项目中,为了查询性能或统计方便,有时会反范式化,但必须先理解范式,再决定破坏哪一级。

  1. 主键设计的原则是什么?为什么不推荐使用有业务含义的主键?
    我遵循四项原则:无业务含义、不可变、短整型优先、单列有序。业务字段如手机号、订单号可能重复、变更或需要格式化,一旦变更会级联影响所有关联表;同时字符串长、随机性高,二级索引叶子节点变大,内存与 IO 都会放大。所以我一直用 bigint 自增或雪花 ID,把业务唯一约束交给唯一索引。

  1. 主键应使用自增 ID 还是 UUID?各自优缺点?
    自增 ID 有序、长度短 8 字节,B+ 树顺序插入页分裂少,范围分页友好;缺点是分库分表后可能冲突、可预测性强。UUID 全局唯一、安全、适合分布式;但 36 字节字符串,主键和二级索引都膨胀,随机写入导致页分裂与磁盘离散 IO,QPS 高时瓶颈明显。我的折中方案是雪花 ID:64 位有序 long,既保持顺序又支持分布式。

  1. 如果不定义主键会发生什么?
    InnoDB 必须有一个聚簇索引,如果建表时没有主键也没有非空唯一索引,会隐式追加 6 字节 row_id 列做聚簇键。这个隐藏列全局共享,高并发插入容易成为瓶颈,且复制到从库时行定位不确定,可能导致主从不一致。所以线上规范强制每张表显式指定主键。

  1. 外键约束是什么?
    外键是表与表之间的参照完整性约束,定义在从表上,要求插入或更新的列值必须存在于主表的对应列,支持级联更新或删除。它把关系模型里的‘引用’语义变成数据库强制执行的行为。

  1. 外键的优缺点?为什么互联网项目常禁用外键?
    优点是能保证数据一致性,省掉部分校验代码。缺点是:1) 高并发写入时会在主表加共享锁,甚至级联锁,放大锁范围;2) 批量导入、在线 DDL 需按依赖顺序操作,维护成本高;3) 分库分表后跨库无法生效;4) 故障排查时级联删除难以回滚。因此我们在应用层做‘逻辑外键’:代码先查再插,删除前校验,配合定时任务兜底。

  1. 字段为什么建议定义为 NOT NULL?
    NULL 会让 count(col) 跳过该行,导致业务统计异常;B+ 树索引不会存储 NULL,范围查询需要回表;同时查询条件 IS NULL 无法使用覆盖索引。更关键的是 MySQL 对 NULL 采用三值逻辑(true/false/unknown),组合条件容易踩坑。除非业务语义确实需要‘未知’,否则一律给默认值并 NOT NULL。

  1. truncate、delete、drop 区别?
    truncate 是 DDL,按页整体回收,不写逐行日志,自增计数器重置,速度快但不能带 where;delete 是 DML,逐行加锁写 undo/redo,可回滚,可带条件;drop 直接把表定义和数据文件一起移除,不可回滚。线上清理全表数据优先 truncate,但触发器、外键存在时会退化为 delete,要注意行锁风险。

  1. union 和 union all 的区别?
    union 会在临时表上做 distinct 去重并排序,代价是额外的一次哈希或排序;union all 只是把两个结果集简单拼接,不去重也不排序,性能高。如果业务语义保证两边无重复,或允许重复,一律用 union all,把去重逻辑放到代码或冗余唯一索引里。

  1. JOIN 有几种?INNER / LEFT / RIGHT / CROSS 的区别?
    四种常见连接:inner join 只返回两边匹配的行;left join 返回左表全部行,右表无匹配补 NULL;right join 相反;cross join 是笛卡尔积,无连接条件。执行计划里驱动表选择会影响性能:小表驱动大表可利用 Nested-Loop,把大表走索引;left join 通常左表为驱动,禁止把左表做大表。

  1. 子查询与连接查询性能差异?
    相关子查询会对外表每一行都执行一次子查询,复杂度 O(n²),在 5.5 之前是灾难;派生表(from 里的子查询)会物化到临时表,无索引,大数据量会落盘。改写成 join 后优化器可以选驱动表、用索引、做 hash join,复杂度降到 O(n)。线上规范要求把 in/exists 相关子查询全部改写成 join 或半连接。

  1. 临时表与派生表的区别?
    临时表分两种:一是用户显式 create temporary table,会话级可见,可建索引,用完自动回收;二是优化器在执行过程中生成的派生表(derived table),来自子查询或 union,5.7 之前会物化到磁盘临时表且无索引,成为性能黑洞。8.0 已支持 derived condition pushdown 和 hash join,但仍建议大结果集先手动存临时表并加索引。

  1. 视图(View)与物化视图的区别?
    普通视图是存储的 select 语句,每次访问都重新执行,数据实时但性能取决于底层查询;物化视图把结果集物理落表,可建索引,查询直接扫表,速度快,但需要刷新机制。MySQL 原生只提供普通视图,物化视图需用触发器或定时任务手动实现。对报表类慢查询,我会用物化视图+增量刷新,把 30s 的复杂 join 降到 200ms。

  1. 一条 SQL 语句的执行过程

在 MySQL 中,一条 SQL 语句从执行到返回结果大致经历以下过程:客户端首先通过连接器建立连接并完成身份认证,SQL 语句进入服务层后由解析器进行语法分析和语义校验,再经过优化器生成最优的执行计划,随后执行器根据计划调用存储引擎(如 InnoDB)读取或写入数据,最后将结果返回给客户端。在这个过程中,查询优化器决定了 SQL 的执行效率,而存储引擎负责真正的数据存取与事务控制。

二、存储引擎与数据类型(🌟)

答题/背诵技巧

  • InnoDB事务行锁,MyISAM表锁快
  • Memory内存存表,断电数据丢
  • InnoDB日志支持ACID,MyISAM无事务
  • 在线DDL,InnoDB边改边读
  • 索引锁表看引擎,InnoDB可在线
  • CHAR定长快,VARCHAR变长省
  • DATETIME不受时区,TIMESTAMP随区转
  • 小精准类型优,冗余大文本避

必学资料

01 数字类型:避免自增踩坑

02 字符串类型:不能忽略的 COLLATION

03 日期类型:TIMESTAMP 可能是巨坑

面试题逐题回答

  1. MySQL 常见存储引擎有哪些?

MySQL 常见的存储引擎主要有 InnoDB、MyISAM 和 Memory。InnoDB 是默认引擎,支持事务和行级锁,适合大多数业务场景;MyISAM 不支持事务,但读操作速度快,适合统计或分析型业务;Memory 存储在内存中,速度极快,但数据断电后会丢失,通常用于临时表和缓存。


  1. 各存储引擎的区别与应用场景?

InnoDB 与 MyISAM 的核心区别在于事务支持、锁粒度和数据安全。InnoDB 支持事务(ACID)、行级锁和外键,崩溃恢复能力强,适合高并发读写和数据完整性要求高的场景。MyISAM 不支持事务、只支持表级锁,读取速度快,适合读多写少或报表分析场景。Memory 引擎速度最快,但数据存储在内存中,适合临时表或缓存场景。


  1. InnoDB 为什么支持事务?MyISAM 为什么不支持?

InnoDB 支持事务是因为其实现了 undo log、redo log 和锁机制,保证了事务的原子性、一致性、隔离性和持久性(ACID)。MyISAM 不支持事务,主要是设计上只关注高效的读写性能,没有实现日志和行级锁机制,因此无法保证事务回滚或一致性。


  1. 在线修改表结构(DDL)时会发生什么?

执行 ALTER TABLE 修改表结构时,MySQL 会根据存储引擎选择不同策略。InnoDB 支持 在线 DDL,在大多数修改操作下可以边读写边修改(但写操作可能有轻微阻塞);MyISAM 则通常会锁表,整个表不可读写,直到操作完成。DDL 还可能触发表复制、索引重建和元数据更新。


  1. 创建索引时是否会锁表?

是否锁表取决于存储引擎和 MySQL 版本。InnoDB 在现代版本中支持 在线创建索引(ONLINE=TRUE),可以在大多数读写操作同时进行,但部分写操作可能被短暂阻塞;MyISAM 创建索引时会锁表,整个表在索引构建期间不可操作。


  1. CHAR 和 VARCHAR 的区别?

CHAR 是固定长度字段,存储效率高但可能浪费空间;VARCHAR 是可变长度字段,节省空间但在修改或排序时性能略低。选择时,如果字段长度固定(如身份证号、性别),用 CHAR;长度不固定(如用户名、邮箱),用 VARCHAR。


  1. DATETIME 和 TIMESTAMP 的区别?

DATETIME 存储完整日期时间('YYYY-MM-DD HH:MM:SS'),不受时区影响;TIMESTAMP 存储自 1970-01-01 UTC 起的秒数,会根据系统时区自动转换。DATETIME 占用 8 字节,TIMESTAMP 占用 4 字节。选择时,跨时区应用可使用 TIMESTAMP,本地固定时间可用 DATETIME。


  1. 如何选择合适的数据类型以优化空间和性能?

原则是 尽量小、精确、满足业务需求:

数字类型按值范围选择最小类型(TINYINT、SMALLINT、INT、BIGINT)。

字符类型固定长度用 CHAR,可变长度用 VARCHAR。

日期类型考虑是否需要跨时区,选择 TIMESTAMP 或 DATETIME。

减少冗余字段和大文本字段,利于缓存和索引效率。

三、索引(🌟🌟🌟🌟)

答题/背诵技巧

  • B+ 树叶链表,范围查找快
  • 聚簇主键存行,二级索引回表
  • 覆盖索引全字段,无需回表
  • 最左匹配,联合索引起作用
  • 索引越多写越慢,选用高选择
  • 函数类型转换,索引易失效
  • EXPLAIN 看 type、rows、key、Extra
  • ICP 下推,减少回表提效率

必学资料

08 索引:排序的艺术

09 索引组织表:万物皆索引

10 组合索引:用好,性能提升 10 倍!

面试题逐题回答

  1. 为什么 MySQL 使用 B+ 树而不是其他结构?

MySQL 使用 B+ 树是因为它非常适合 磁盘存储和范围查询。B+ 树的叶子节点链表可以顺序访问,支持范围扫描;内节点只存储索引,减少磁盘 IO;高度平衡保证查找时间复杂度 O(log n)。相比 Hash 索引,B+ 树支持范围查询和排序,而 Hash 只适合等值查询。


  1. B+ 树的优缺点?

优点:

支持范围查询和排序。

磁盘 IO 少,高度平衡。

叶子节点链表可以快速遍历。

缺点:

写入和更新成本比 Hash 高。

维护复杂,需要分裂、合并节点。


  1. InnoDB 的索引存储结构是什么?

InnoDB 使用 聚簇索引存储数据,主键索引的叶子节点存储整行数据。非主键索引(辅助索引)叶子节点只存储主键值,查询非主键字段需回表访问聚簇索引。


  1. 什么是回表?为什么会回表?如何避免?

回表是指查询使用二级索引时,需要通过主键再访问聚簇索引获取完整行数据。回表会增加 IO 开销。
避免方式:使用 覆盖索引,即索引包含查询所需的所有列,这样可以直接从索引返回结果,无需访问聚簇索引。


  1. 什么是索引覆盖(Covering Index)?

覆盖索引是指索引包含查询语句所需的全部字段,查询可以直接使用索引返回结果,不需要回表访问数据行。这不仅减少了 IO,也提高了查询性能。


  1. 什么是索引合并(Index Merge)?

索引合并是 MySQL 查询优化器在执行查询时,将多个单列索引的结果进行 交集或并集,再返回符合条件的行。它可以提高多条件查询效率,但对高选择性索引效果更明显。


  1. MySQL 有哪些索引类型?

MySQL 索引类型主要有:

主键索引:唯一且非空,用作聚簇索引。

唯一索引:保证列值唯一,但可为空。

普通索引:无唯一性约束。

全文索引:用于全文搜索(MATCH ... AGAINST)。

前缀索引:对字符串前 N 个字符建立索引,节省空间。


  1. 聚簇索引与非聚簇索引的区别?

聚簇索引:数据和索引存储在一起,叶子节点存储完整行数据。

非聚簇索引:叶子节点存储主键值,访问非主键列时需回表。
聚簇索引适合主键查找,非聚簇索引适合辅助查询,但回表开销较高。


  1. InnoDB 主键索引与二级索引的关系?

二级索引的叶子节点存储主键值,通过主键去聚簇索引回表获取完整数据。也就是说,所有二级索引都依赖主键索引。


  1. 普通索引与唯一索引的区别?哪个更新性能更好?

唯一索引保证列值唯一,需要维护额外约束,写入和更新成本略高;普通索引没有唯一性约束,更新性能更好。选择时可根据业务唯一性需求使用唯一索引。


  1. 为什么要建索引?索引有哪些代价?

作用:提高查询效率,支持排序和分组。
代价:增加写入和更新成本(INSERT/UPDATE/DELETE)。占用磁盘和内存空间。索引过多可能导致性能下降。


  1. 哪些字段适合建索引?哪些不适合?

适合:高选择性字段、常用于查询条件、排序、分组的字段。
不适合:低选择性字段(性别、状态)、频繁更新字段、大文本字段。


  1. 索引越多越好吗?

不是,索引越多会增加写入成本、占用更多存储,可能导致性能下降。索引应根据查询场景精心设计。


  1. 哪些场景下索引反而会降低性能?

高频写入和更新表。

查询条件使用函数或类型转换导致索引失效。

索引数量过多导致维护成本高。


  1. 查询语句一定会使用索引吗?

不一定。优化器会根据表大小、索引选择性和统计信息决定是否使用索引。有时全表扫描比索引扫描更快。


  1. 哪些操作会导致索引失效?

对列使用函数(如 DATE(col))。

类型不匹配或隐式转换。

前置模糊匹配(LIKE '%abc')。

OR 连接多个条件且无合适索引。


  1. 什么是最左匹配原则?

联合索引 (a,b,c) 必须从最左列开始查询,才能使用索引。查询条件从中间或右侧列开始将导致索引失效。


  1. 什么是索引下推(ICP)?

索引下推(Index Condition Pushdown)是存储引擎在扫描二级索引时,将 WHERE 条件部分下推到索引层过滤,减少回表行数,提高查询效率。


  1. EXPLAIN 各字段代表什么?

type:访问类型(ALL/INDEX/RANGE/REF/EQ_REF)。

rows:扫描的行数估算。

key:使用的索引。

Extra:额外信息,如 Using index 表示覆盖索引,Using where 表示回表后过滤。


  1. EXPLAIN 中 Using index 与 Using where 的区别?

Using index:查询完全使用索引,覆盖索引,无需回表。

Using where:查询需要访问数据行,索引无法覆盖所有字段,需要回表或额外过滤。


  1. 如何使用执行计划定位 SQL 性能问题?

通过 EXPLAIN 查看访问类型、扫描行数、索引使用情况和 Extra 信息,判断是否全表扫描、索引命中率低、回表次数过多,进而优化索引或改写 SQL。


  1. 如果字段是 varchar 类型日期 '20230922',where time=20230922(不加引号)会命中索引吗?为什么?

不会命中索引,因为 20230922 是数字,MySQL 会隐式类型转换,将 time 列的 VARCHAR 转为数字或将数字转换为字符串,这会导致索引失效。正确写法应使用引号:WHERE time='20230922'。


  1. 联合索引 (a, b, c) 时,哪些查询能走索引?

WHERE a>1 AND b=2 AND c<3:可以走索引,遵循最左匹配原则,但 a>1 是范围查询,b、c 会被部分使用或回表。

WHERE a=? AND b=? ORDER BY c:可以走联合索引,a、b 完全匹配,并且索引列 c 可用于排序优化,减少排序开销。

四、事务(🌟🌟🌟🌟)

答题/背诵技巧

事务特性

  • ACID 四性:原子一致隔离持久
  • Undo 回滚旧值保原子
  • Redo 顺序写保持久
  • 锁配 MVCC 保隔离
  • Redis 弱事务,无回滚

隔离级别与并发问题

  • 四级隔离:RU RC RR S
  • 可重复读加间隙锁防幻
  • 默认 RR,用 MVCC 实现
  • 脏读未提,重读不一,幻行变多
  • 串行化全锁,性能最低

MVCC 机制

  • MVCC 多版本,读无锁
  • 创建删版号判可见
  • RC 新视图,RR 一视图
  • RC 并发高,RR 一致强
  • 互联网常用 RC 提性能

事务日志与持久化

  • Undo 保回滚,Redo 保恢复
  • Undo 原子性,Redo 持久性
  • 两阶段提交保双写一致
  • 先 Redo 后 Binlog 再提交
  • Redo 顺序写,I/O 高效

必学资料

事务简介

Redo Log (上)

Redo Log (下)

Undo Log(上)

Undo Log(下)

事务隔离级别

面试题逐题回答

事务特性

  1. MySQL 事务的四大特性(ACID)是什么?

事务的四大特性包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
原子性保证事务中的所有操作要么全部成功、要么全部失败;一致性保证事务前后数据状态必须合法;隔离性保证多个事务并发执行时互不干扰;持久性则保证事务一旦提交,其结果会永久保存到数据库中。


  1. 事务的原子性、隔离性、持久性是如何实现的?

原子性通过 Undo Log 实现,记录事务修改前的旧值,当事务回滚时可恢复原状。
隔离性依靠 锁机制 和 MVCC(多版本并发控制) 实现,确保不同事务读写操作互不影响。
持久性通过 Redo Log 保证,Redo Log 记录事务提交后的修改内容,即使数据库崩溃,也能通过日志恢复数据。


  1. MySQL 与 Redis 的事务机制有何不同?

MySQL 的事务基于 ACID 特性,具备回滚、隔离和持久化能力;而 Redis 的事务更轻量,本质是命令队列(MULTI/EXEC),没有隔离性,也不支持回滚。Redis 的事务只保证命令的顺序执行,不保证中途失败可恢复,因此属于“弱事务机制”。

隔离级别与并发问题

  1. MySQL 支持哪些事务隔离级别?

MySQL 支持四种标准的事务隔离级别:
读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable)。


  1. 各隔离级别分别解决了哪些问题?

读未提交:可能出现脏读、不可重复读、幻读。

读已提交:避免脏读,但仍会出现不可重复读和幻读。

可重复读:避免脏读和不可重复读,InnoDB 通过 MVCC 解决大部分幻读问题。

串行化:通过加锁彻底避免所有并发问题,但性能最差。


  1. 默认隔离级别是什么?如何实现?

MySQL InnoDB 的默认隔离级别是 可重复读(Repeatable Read)。
它通过 MVCC 实现一致性读,通过「当前读 + 间隙锁(Next-Key Lock)」避免大多数幻读场景。


  1. 脏读、不可重复读、幻读的区别?

脏读:读到了未提交事务修改的数据。

不可重复读:同一事务多次读取同一行,结果不同。

幻读:同一查询条件下,前后读取到的行数不同(新增或删除了数据)。


  1. 串行化隔离级别如何实现?

串行化通过强制事务 按顺序执行 来实现,所有读写操作都加锁(通常是表级锁),从而避免任何并发读写冲突。性能低,但隔离性最强。


  1. 可重复读级别如何解决幻读?是否完全避免?

InnoDB 在可重复读级别下使用 MVCC + 间隙锁(Next-Key Lock)。
MVCC 解决了快照读下的幻读问题,而间隙锁用于当前读(如 SELECT ... FOR UPDATE)场景下防止插入新数据。
但在某些特殊场景(如非一致性读或手动关闭间隙锁)下,仍可能出现幻读。

MVCC(多版本并发控制)

  1. 什么是 MVCC?

MVCC(Multi-Version Concurrency Control)是一种多版本并发控制机制。它让读操作无需加锁即可读取数据的历史版本,从而提高并发性能。每行记录都有隐藏字段:创建版本号和删除版本号,用来判断对事务是否可见。


  1. MVCC 如何判断行记录是否对事务可见?

当事务执行查询时,InnoDB 会根据当前事务的 Read View(快照视图)判断记录是否可见:

若记录的创建版本号 ≤ 当前事务 ID 且删除版本号为空或 > 当前事务 ID,则该记录可见。

否则说明该行被其他事务修改或删除,不可见。


  1. 读已提交与可重复读隔离级别的 MVCC 差异?

在 读已提交(RC) 下,每次执行 SELECT 都会生成新的 Read View,读取最新提交版本。
在 可重复读(RR) 下,整个事务只生成一次 Read View,因此多次查询结果一致。


  1. 为什么互联网公司常用读已提交?

读已提交(RC)在高并发场景下性能更好,避免长时间持有快照视图导致的版本链过长问题。
同时,业务系统往往对“可重复读”的一致性要求不高,轻微的并发差异可以通过业务逻辑容忍或补偿。

事务日志与持久化

  1. 什么是 Undo Log?它的作用是什么?

Undo Log 是用于记录事务操作前数据状态的日志。
它的主要作用是支持事务回滚和 MVCC 版本控制:回滚时可恢复原始数据,MVCC 读取旧版本时也依赖 Undo Log。


  1. Undo Log 与 Redo Log 如何共同实现事务一致性?

Undo Log 保证原子性,Redo Log 保证持久性。
事务提交时先写入 Redo Log,再写入 Binlog,崩溃恢复时通过 Redo Log 重放提交数据,或根据 Undo Log 回滚未提交事务,从而确保一致性。


  1. 什么是两阶段提交?其作用是什么?

两阶段提交(2PC)用于保证 Redo Log 与 Binlog 数据的一致性。
阶段一:准备阶段,先写入 Redo Log 并标记为 prepare;
阶段二:提交阶段,写入 Binlog 后标记 Redo Log 为 commit。
这样即使崩溃,也能根据两者状态判断事务是否成功提交。


  1. 事务提交过程中 Redo Log 与 Binlog 的写入顺序?

事务提交时,InnoDB 会先写 Redo Log(prepare 状态),再写 Binlog,最后将 Redo Log 标记为 commit。
此顺序保证在系统崩溃时,能够根据 Redo Log 与 Binlog 的状态安全恢复。


  1. Redo Log 为什么采用顺序写?

Redo Log 使用顺序写是为了减少磁盘寻址开销、提高写入性能。
顺序写的 I/O 效率远高于随机写,加上 InnoDB 的写缓冲机制(Log Buffer),能在保证持久性的同时提升事务吞吐量。

五、锁机制(🌟🌟🌟)

答题/背诵技巧

锁机制

  • 六类锁:表行意向间隙 Next-Key、MDL
  • 行锁两类:共享 S、排他 X
  • 间隙锁锁空隙,Next-Key“记录+范围”
  • UPDATE 无 WHERE,全表上 X
  • 未命中索引,扫描全表上锁
  • 同行不同列,行锁仍冲突
  • FOR UPDATE 扫描范围会锁多行
  • 行锁随索引定,没命中则全表锁

死锁与并发

  • 乐观锁靠版本,失败重试
  • 悲观锁先加锁,防并发写
  • 死锁是循环等
  • 等待图查死锁,自动回滚
  • 避死锁靠顺序、小事务、用索引

必学资料

面试题逐题回答

锁分类与原理

  1. MySQL 有哪些锁?(表锁、行锁、意向锁、间隙锁、Next-Key Lock、MDL)

在 MySQL 中,不同层面提供了多种类型的锁,目的是兼顾并发性能与数据一致性。首先,从操作粒度来看,有表锁与行锁:表锁作用于整个表,常见于 MyISAM,引入成本低但并发能力弱;行锁则由 InnoDB 提供,以记录为粒度,能显著提升高并发下的吞吐量。其次,InnoDB 使用意向锁来快速判断表锁与行锁的兼容性,使加锁判断的成本保持常量级。为了避免幻读,InnoDB 引入了间隙锁与 Next-Key Lock:前者用于锁定记录之间的空隙,后者是“记录锁 + 间隙锁”的组合,确保范围查询的隔离性。除此之外,还存在元数据锁(MDL),用于在 DDL 与 DML 并发时保护表结构安全。整体来看,这套锁体系保证了 MySQL 在不同场景下的隔离性、并发性与一致性。


  1. InnoDB 的行级锁有哪些?

InnoDB 基于索引实现记录级锁,核心包括共享锁(S)与排他锁(X)。共享锁允许多个事务同时读取记录,但禁止对记录进行修改;排他锁则确保只有当前事务能够读或写记录,从而保护更新操作的安全性。除此之外,行级锁并不仅限于具体记录:在范围查询下,InnoDB 会额外使用间隙锁与 Next-Key Lock 来锁定范围,以保证可重复读隔离级别下的一致性读取。在实际应用中,行锁依赖索引,因此设计合适的索引结构直接决定加锁范围与并发性能。


  1. 间隙锁(Gap Lock)和 Next-Key Lock 的区别?

两者都是为了防止范围查询中的幻读问题,但作用范围存在差异。间隙锁仅锁定记录之间的区间,并不锁定具体记录本身,因此主要用于阻止其他事务在锁定区间内插入新记录。而 Next-Key Lock 则由记录锁和间隙锁共同构成,不仅锁定记录本身,还覆盖该记录前的间隙,形成半开半闭的区间。由于其覆盖范围更大,可重复度隔离级别下的 InnoDB 会默认使用 Next-Key Lock,从而保证范围查询的稳定性。这种机制避免了其他事务插入新行导致的幻读,同时保证读取结果在事务生命周期内一致。


  1. 一条 UPDATE 不带 WHERE 会加什么锁?

不带 WHERE 条件的 UPDATE 会导致 InnoDB 进行全表扫描,因此对每一条记录加排他行锁(X 锁),并且由于锁遍整个表,会自动持有相应的意向排他锁。这种加锁方式相当于对整个表加上强制性的写锁,导致其他事务无法对表中的任意记录执行读写操作,因此会严重影响并发性能。在生产环境中,应避免在高峰期执行无条件 UPDATE,否则容易造成大面积阻塞。


  1. 带 WHERE 但未命中索引,会加什么锁?

未命中索引的 WHERE 条件会让 SQL 执行退化为全表扫描,此时 InnoDB 会逐条读取记录,并对扫描到的每条记录加排他锁。这种方式与无 WHERE UPDATE 相似,都会造成大量锁持有时间较长,导致并发性能下降。尤其是在大表场景中,此类 SQL 可能引发锁等待、阻塞甚至死锁。因此设计良好的索引结构不仅提高查询性能,还能减少锁时间与锁竞争,是高并发系统中非常关键的优化点。


  1. 两条事务更新同一行记录会加什么锁?

两条事务更新同一条记录时,都会尝试对该记录加排他锁。第一个事务成功加锁后,第二个事务会进入等待状态,直到前者提交或回滚。由于排他锁之间是互斥的,这种写写冲突属于典型的行级竞争情形。为了降低这类冲突的概率,应尽量将热数据拆分、使用分库分表、或采用缓存层缓冲写入压力。


  1. 更新同一行的不同字段,会加锁冲突吗?

会造成冲突。InnoDB 锁的粒度是“行”,而不是“字段”。因此无论更新某一列还是多列,事务在更新时都需要对整条记录加排他锁。结果是,只要两个事务操作的是同一条记录,即便更新字段不一致,也会产生锁竞争。这也是为什么数据库设计中强调避免热点行,以及为何无法通过列级拆分减少并发冲突。


  1. SELECT ... FOR UPDATE 会锁多行吗?为什么?

在 InnoDB 中,SELECT FOR UPDATE 会根据查询条件锁定扫描到的记录。加锁范围由索引范围决定,因此不仅实际返回的记录会加锁,符合扫描范围但未返回的记录也可能被加锁。若是范围查询,还会触发间隙锁或 Next-Key Lock,因此最终加锁范围可能远大于返回行数。在索引命中不佳或全表扫描时,加锁范围甚至可能扩大到整个表,从而显著影响并发能力。


  1. 行锁的加锁范围是如何确定的?(如范围查询)

行锁的核心依据是索引。命中唯一索引时,锁定范围只包含特定记录,开销最低。而在范围查询中,InnoDB 需要保证可重复读隔离级别,因此采用 Next-Key Lock,对目标记录及其前后间隙加锁,确保范围稳定。如果查询未命中索引,则退化为全表扫描,对所有扫描到的记录加锁,导致锁范围显著扩大。因此,索引设计直接控制加锁范围,是保障并发能力的核心因素之一。


  1. 什么是乐观锁?MySQL 如何实现?

乐观锁假设并发冲突不频繁,因此不会在数据读取阶段加锁,而是在更新时检查数据是否被修改。MySQL 中常见的实现方式是利用 version 字段或更新时间字段,通过条件更新保证数据一致性:例如 UPDATE ... WHERE id=? AND version=?。如果更新行数为 0,说明其他事务已经修改过数据,此时需要重试或给出冲突提示。乐观锁适合读多写少的场景,避免了悲观锁的大量阻塞开销。


  1. 什么是悲观锁?

悲观锁假设冲突必然发生,因此会在数据读取或修改前就提前加锁,阻止其他事务并发修改。例如 InnoDB 的 UPDATE、SELECT FOR UPDATE 与 LOCK IN SHARE MODE 均属于悲观锁实现方式。悲观锁适合写竞争较激烈的场景,但会带来大量阻塞与等待,需要合理控制事务范围。


  1. 什么是死锁?

死锁指多个事务持有部分锁资源,同时等待对方释放所需资源,形成循环等待链,导致所有相关事务无法继续执行。如事务 A 等 B 的锁,B 又等待 A 的锁。在高并发系统中,死锁是难以避免的现象,因此数据库必须具备自动检测与恢复机制。


死锁与冲突分析

  1. InnoDB 如何检测死锁?

InnoDB 通过构建等待图来分析事务之间的依赖关系。当图中出现循环依赖时,即表示发生了死锁。此时 InnoDB 会主动选择一个成本较低(通常是修改数据量较少)的事务进行回滚,从而打破循环,使其他事务得以继续执行。这种自动检测机制能及时恢复系统健康,避免事务永久阻塞。


  1. 如何排查死锁问题?

排查死锁最常用的方法是执行 SHOW ENGINE INNODB STATUS,查看最近一次死锁的详细信息,包括参与的 SQL、锁类型、等待链等。如果开启 innodb_print_all_deadlocks,所有死锁会记录到日志,便于历史分析。根据日志可以识别是否为索引缺失、访问顺序不一致或大事务导致,从而针对性优化。


  1. 如何避免死锁?

死锁无法完全避免,但可以减少其发生概率。常见策略包括:保证事务访问顺序一致,避免产生循环依赖;将大事务拆解为小事务,缩短锁持有时间;为查询增加合适的索引,避免全表扫描扩大加锁范围;充分利用缓存减少数据库写压力;尽量避免在事务中执行复杂或耗时操作。在良好设计下,死锁的影响可以被降低到可控范围。

六、日志系统(🌟🌟🌟)

答题/背诵技巧

  • 三类日志:Redo、Undo、Binlog
  • Redo 物理恢复,Binlog 逻辑同步
  • 崩溃恢复用 Redo,顺序写性能高
  • Binlog 三种:Statement、Row、Mixed
  • Redo 先写 Log Buffer,再刷磁盘
  • Redo 保障持久性+MVCC,支持回滚
  • 两阶段提交保 Redo 与 Binlog 一致
  • Redo 刷盘策略:每事务、每秒、异步

必学资料

  • 见事务部分

面试题逐题回答

  1. MySQL 有哪些日志?(Redo、Undo、Binlog)

MySQL 核心日志包括 Redo、Undo 和 Binlog。Redo 用于崩溃恢复,记录已提交事务的物理变化;Undo 用于事务回滚和 MVCC,保存修改前的旧值;Binlog 用于主从复制和审计,记录逻辑层操作顺序,实现数据同步和审计。


  1. Redo Log 与 Binlog 的区别与应用场景?

Redo Log 是物理日志,保证事务持久性和崩溃恢复;Binlog 是逻辑日志,记录 SQL 执行顺序,用于复制和审计。Redo 适用于本地恢复,Binlog 用于主从同步和数据回溯。


  1. 为什么崩溃恢复使用 Redo Log 而不是 Binlog?

Redo Log 保存物理数据修改,且顺序写性能高,可快速恢复未写入磁盘的事务;Binlog 逻辑层次高,可能存在事务未提交或顺序不完整,效率低且依赖事务提交,无法保证崩溃恢复原子性。


  1. Binlog 的三种格式(statement / row / mixed)?

Statement 格式记录 SQL 语句;Row 格式记录具体行数据变化;Mixed 格式结合两者,默认使用 Statement,遇复杂语句使用 Row。选择影响复制精确性与性能。


  1. Redo Log 如何实现持久化?

Redo Log 先写入内存 Log Buffer,再异步刷盘到磁盘,根据策略(innodb_flush_log_at_trx_commit)决定同步时机,保证数据持久性且提升写入效率。


  1. Redo Log 除了崩溃恢复还有哪些作用?

Redo Log 支持事务持久性、保障原子性,并结合 Undo 实现 MVCC 版本控制,提高并发读性能,是 InnoDB 事务引擎核心机制。


  1. 为什么需要两阶段提交?

两阶段提交保证 Redo Log 与 Binlog 的一致性。先写 Redo 并标记 prepare,再写 Binlog,最后提交 Redo。即使崩溃,也能判断事务是否成功,确保数据一致。


  1. Redo Log 的三种刷盘策略?

每事务提交同步刷盘(最安全);

每秒刷盘(性能与安全平衡);

不刷盘(性能高但可能丢失数据),可通过 Log Buffer 异步写入提升性能。

七、性能优化(🌟🌟)

答题/背诵技巧

**性能优化 **

  • 判断索引:EXPLAIN 看 key/type/rows
  • 定位慢 SQL:slow_query_log + 分析工具
  • 优化慢 SQL:索引、改写、拆表、分页
  • type=ALL/rows 大:全表扫描,索引缺失
  • SQL 慢但索引没问题:检查 IO、锁、join
  • 深分页优化:延迟关联、keyset、预聚合
  • 大表删改:分批、加索引、临时表、拆分
  • Performance Schema:分析慢 SQL、锁、IO
  • 查询缓存废弃:高并发易失效,buffer pool 替代

必学资料

面试题逐题回答

  1. 如何判断 SQL 是否走索引?

可以通过 EXPLAIN 查看查询执行计划,关注 key、key_len、rows 等字段。如果 key 显示使用索引且 type 非 ALL,说明走了索引。结合 SHOW PROFILEEXPLAIN ANALYZE 可进一步确认索引命中情况。


  1. 如何定位慢 SQL?如何使用 slow_query_log?

开启 slow_query_log 并设置 long_query_time,MySQL 会记录执行超过阈值的 SQL。结合 mysqldumpslowpt-query-digest 分析日志,可找出耗时最长或频繁执行的 SQL。


  1. 如何优化慢 SQL?

优化慢 SQL 可从索引优化、查询改写、拆分大表、分页优化、避免 SELECT * 和子查询入手。同时关注表结构、统计信息和事务设计,减少全表扫描与锁等待。


  1. explain 中 type=ALL、rows 很大说明什么问题?

type=ALL 表示全表扫描,rows 大说明扫描行数多,可能没有使用索引或索引选择不当。这通常是慢查询的主要原因,需要优化索引或改写 SQL。


  1. SQL 和索引都没问题但查询仍慢怎么办?(IO / 锁等待 / join 缓慢)

需要从系统层面排查:查看 IO 是否瓶颈、锁等待是否严重、join 是否产生临时表或文件排序。可使用 SHOW ENGINE INNODB STATUS、Performance Schema 或监控工具分析。


  1. 深分页查询如何优化?(limit offset)

深分页用大 offset 会扫描大量数据,可改为延迟关联分页(where id > 上一页最大 id)、使用 keyset 分页或预聚合,避免全表扫描与排序。


  1. 大表删除或更新的风险与优化方式?

大表 DELETE/UPDATE 会锁表或生成大量 undo/redo,可能导致阻塞或日志膨胀。优化方式包括分批处理、加索引条件、使用临时表或按时间/主键拆分操作。


  1. 如何分析 Performance Schema?

Performance Schema 提供 SQL、等待事件、锁、IO 等监控信息。可通过查询 events_statements_summary_by_digestevents_waits_summary_by_instance 等表,分析慢 SQL、锁等待和资源瓶颈。


  1. MySQL 查询缓存为什么被废弃?

查询缓存仅缓存结果集,写表时需清空相关缓存,高并发下频繁失效,维护成本高。现代 InnoDB 通过 buffer pool、索引和缓存层实现更高效的性能,因此查询缓存被废弃。

八、高可用与扩展(🌟🌟)

答题/背诵技巧

  • 主从复制:主写 Binlog → IO 线程 → 从库 SQL 执行
  • 复制模式:异步 / 半同步 / 全同步
  • 默认模式:异步,提交不等从库确认
  • 主从延迟:大事务、网络、从库性能,优化 SQL 和硬件
  • 半同步:主库等待至少一个从库收到确认
  • 读写分离:写主库,读从库 + 负载均衡
  • 主库宕机 failover:提升从库,重建同步
  • 分库分表:拆库拆表减压力,提高性能
  • 分表:单表大数据量;分库:单库压力大
  • 问题与解决:分布式事务、全局 ID、跨库查询、路由层设计

必学资料

23 读写分离如何在业务中落地?

19 高可用套件:选择这么多,你该如何选?

24 为什么需要分库分表,如何实现?

面试题逐题回答

  1. MySQL 主从复制的基本过程?

主库将写操作记录到 Binlog,IO 线程读取 Binlog 并发送给从库,从库的 SQL 线程执行这些日志,从而保持数据同步,实现异地备份和读写分离。


  1. 有哪几种复制模式?(异步 / 半同步 / 全同步)

复制模式包括异步、半同步和全同步。异步复制主库提交后不等待从库确认;半同步复制等待至少一个从库确认提交;全同步复制要求所有从库确认后主库才完成提交。


  1. 默认复制模式是什么?

MySQL 默认是异步复制,提交事务无需等待从库确认,适合高性能写场景,但可能存在主从数据短暂不一致。


  1. 主从延迟的常见原因与解决方案?

常见原因包括大事务、网络延迟、从库性能不足、锁等待。解决方案有优化 SQL、增加从库数量、提升硬件、分库分表、异步刷盘等。


  1. 什么是半同步复制?

半同步复制指主库提交事务后,会等待至少一个从库收到并写入 Relay Log 才确认提交,兼顾性能和数据安全性,降低数据丢失风险。


  1. 主从架构下如何实现读写分离?

通过应用层或中间件将写操作定向到主库,读操作分发到从库,同时结合负载均衡和一致性策略,提升读吞吐量并减轻主库压力。


  1. 主库宕机后如何恢复?(failover 机制)

可通过监控发现主库宕机,将从库提升为主库,并重新配置其他从库同步,保证系统继续提供服务。自动化工具如 MHA、Orchestrator 可实现快速 failover。


  1. 什么是分库分表?

分库分表是将数据按业务或规则拆分到多个库或表,减小单库/表数据量,提高读写性能和系统扩展性。


  1. 什么时候需要分表?什么时候需要分库?

当单表数据量巨大、查询和写入压力大时需要分表;当单库容量或并发到达瓶颈时需要分库。分表侧重水平拆表,分库侧重数据库层面扩展。


  1. 分库分表后常见问题及解决方案
  • 分布式事务:可用柔性事务、业务幂等、两阶段提交或中间件协调。
  • 全局唯一 ID:使用雪花算法、UUID 或数据库自增 + 前缀。
  • 跨库查询与聚合:通过中间件、应用层汇总,或拆分报表任务。
  • 路由层设计:通过规则或中间件路由 SQL 到对应库表,保证查询正确性和性能。

九、综合与思考题(🌟🌟)

必学资料

Explain(上)

Explain(下)

面试题逐题回答

  1. 请结合具体 SQL,分析一次慢查询的排查与优化过程。

首先通过 slow_query_log 定位慢 SQL,例如 SELECT * FROM orders WHERE status='pending' ORDER BY created_at DESC LIMIT 1000。用 EXPLAIN 查看执行计划,发现全表扫描。优化方法:为 status + created_at 建复合索引,改写查询避免 SELECT *,增加分页或延迟关联查询,最终查询只扫描必要行并利用索引排序,提高性能。


  1. 如何设计一张支持高并发写入的日志表?

日志表设计应采用 InnoDB,主键建议自增或雪花 ID,减少热点行;列尽量少,避免 TEXT/BLOB;支持批量插入;可按日期或模块分表,结合分区表减少单表压力,结合异步写入或队列缓冲高并发流量。


  1. 解释一次 MySQL 事务的完整执行过程(含 redo、undo、binlog)。

事务开始后修改数据,生成 Undo Log 保存旧值,便于回滚;修改写入 Buffer Pool,同时记录 Redo Log,用于崩溃恢复;提交时先刷 Binlog,再刷 Redo Log 提交;事务提交后,Undo Log 保留用于 MVCC 读取,Redo Log 可清理,确保 ACID 一致性与持久性。


  1. 如果一个表写入量巨大,如何防止表膨胀与性能下降?

可采用分表分库、按时间或业务模块拆表,使用分区表;定期归档历史数据;控制索引数量,避免更新热点索引;分批写入减少锁竞争;结合缓存缓冲热点查询和写入压力,保持表体积与查询性能稳定。


  1. MySQL 的常见性能调优手段有哪些?(索引、SQL、缓存、分表、参数)

包括优化索引结构和覆盖索引、改写慢 SQL、减少 SELECT *、利用缓存(Redis/应用层)、分库分表、参数调优(innodb_buffer_pool_size、flush_log_at_trx_commit)、优化事务和锁策略,以及监控慢查询与性能指标。


  1. 线上 MySQL CPU 飙高,你的排查步骤是什么?

首先用 top/iostat 查看系统负载,确认 CPU 占用来源;结合 Performance Schema 或 SHOW PROCESSLIST 分析慢 SQL 或锁等待;检查查询计划 EXPLAIN,分析是否全表扫描;观察索引使用和缓存命中率;必要时开启慢查询日志分析热点 SQL,并考虑 SQL 优化或增加硬件资源。