Mysql高频面试题

一、索引原理与优化

1. 为什么InnoDB选择B+树作为索引结构?对比B树、哈希、红黑树
  • B+树 vs B树
    • B+树非叶子节点不存数据,仅存索引键,能容纳更多索引项,树高更低(减少磁盘I/O);
    • B+树叶子节点通过双向链表连接,范围查询性能远优于B树(B树需中序遍历)。
  • vs 哈希索引
    • 哈希索引仅支持等值查询,无法范围查询、排序;
    • 哈希冲突时性能退化,且不支持最左前缀匹配。
  • vs 红黑树
    • 红黑树是内存数据结构,树高随数据量增长快(磁盘I/O次数多);
    • B+树利用磁盘预读特性(页大小通常16KB),一次I/O读取更多数据。
2. 聚簇索引 vs 非聚簇索引?InnoDB与MyISAM的区别
  • 聚簇索引(InnoDB):
    • 索引和数据存储在一起,叶子节点即数据行(表数据按聚簇索引顺序组织);
    • 一张表只能有一个聚簇索引(默认主键,无主键则选唯一非空索引,再无则隐式生成rowid);
    • 优点:通过聚簇索引查询直接获取数据,性能高;范围查询高效。
  • 非聚簇索引(MyISAM):
    • 索引和数据分离,叶子节点存数据指针(需回表查询);
    • 一张表可多个非聚簇索引;
    • InnoDB的二级索引也是非聚簇索引,叶子节点存主键值(回表需再查聚簇索引)。
3. 最左前缀原则?联合索引底层存储结构
  • 最左前缀原则
    • 联合索引(a,b,c)按a→b→c顺序排序,查询时必须从最左列开始匹配,且不能跳过中间列;
    • 例如:where a=1 and c=3仅用a列,where b=2完全失效。
  • 底层存储
    • B+树叶子节点按联合索引列顺序存储,先按a排序,a相同再按b排序,以此类推;
    • 数据行物理存储按聚簇索引顺序,与联合索引无关。
4. 索引失效的常见场景
  • 违反最左前缀原则;
  • 使用函数/表达式/类型转换(如where age+1=10where id='123');
  • 模糊查询%在前(如like '%abc');
  • OR条件中存在未索引列;
  • 数据分布不均(如性别字段,索引区分度低,优化器选择全表扫描);
  • 使用NOT IN、NOT EXISTS(部分场景)。
5. 覆盖索引?如何避免回表?
  • 覆盖索引:查询的列完全包含在索引中(无需回表查聚簇索引);
    • 例如:联合索引(a,b),查询select a,b from t where a=1,直接从二级索引获取数据。
  • 避免回表的方法
    • 设计联合索引,将查询列包含在索引中;
    • 若需查询大量列,优先考虑聚簇索引(如主键查询)。

二、事务与隔离级别

6. 事务ACID特性及InnoDB实现
  • 原子性(Atomicity)
    • 事务内操作要么全成功,要么全失败;
    • 实现:undo log(记录数据修改前的版本,回滚时恢复)。
  • 一致性(Consistency)
    • 事务前后数据从一个合法状态到另一个合法状态;
    • 实现:原子性+隔离性+持久性+业务逻辑保证。
  • 隔离性(Isolation)
    • 并发事务间互不干扰;
    • 实现:锁机制+MVCC(多版本并发控制)。
  • 持久性(Durability)
    • 事务提交后数据永久丢失;
    • 实现:redo log(先写日志再刷盘,保证崩溃恢复)。
7. 四种隔离级别及解决的问题
隔离级别脏读不可重复读幻读实现方式
读未提交(Read Uncommitted)无特殊机制
读已提交(Read Committed)MVCC(语句级Read View)
可重复读(Repeatable Read,默认)❌(部分解决)MVCC(事务级Read View)+ Next-Key Lock
串行化(Serializable)全表锁/范围锁
  • 脏读:事务A读取到事务B未提交的数据;
  • 不可重复读:同一事务内,两次读取同一行数据结果不同(因其他事务修改并提交);
  • 幻读:同一事务内,两次范围查询结果行数不同(因其他事务插入/删除并提交)。
8. InnoDB如何解决幻读?
  • MVCC(快照读)
    • 普通SELECT(不加锁)使用快照读,通过Read View保证事务内看到的数据版本一致;
    • RC隔离级别:每次SELECT生成新的Read View;RR隔离级别:事务第一次SELECT生成Read View,后续复用。
  • Next-Key Lock(当前读)
    • 加锁的SELECT(for updatelock in share mode)、INSERT、UPDATE、DELETE使用当前读;
    • Next-Key Lock = 记录锁(行锁)+ 间隙锁(锁定索引间隙),防止其他事务在范围内插入数据;
    • 例如:select * from t where id between 10 and 20 for update,锁定(10,20]的记录及间隙,避免插入id=15的行。
9. MVCC实现原理(隐藏字段、undo log、Read View)
  • 隐藏字段
    • DB_TRX_ID:最后修改该行的事务ID;
    • DB_ROLL_PTR:指向undo log中该行的上一个版本;
    • DB_ROW_ID:隐式主键(无主键时生成)。
  • undo log(版本链)
    • 每次修改数据前,将旧版本写入undo log,通过DB_ROLL_PTR形成链表;
    • 链表头是最新版本,链表尾是最旧版本。
  • Read View(可见性判断)
    • 包含字段:m_ids(当前活跃事务ID列表)、min_trx_id(最小活跃ID)、max_trx_id(下一个要分配的ID)、creator_trx_id(创建Read View的事务ID);
    • 可见性规则:
      1. DB_TRX_ID == creator_trx_id,可见(自己修改的);
      2. DB_TRX_ID < min_trx_id,可见(已提交的旧事务);
      3. DB_TRX_ID >= max_trx_id,不可见(新事务);
      4. DB_TRX_IDm_ids中,不可见(活跃未提交);否则可见(已提交)。

三、锁机制

10. InnoDB锁类型
  • 按粒度分
    • 表锁:开销小,并发低(如alter table);
    • 行锁:开销大,并发高(InnoDB默认)。
  • 按类型分
    • 共享锁(S锁):读锁,多个事务可同时持有;
    • 排他锁(X锁):写锁,仅一个事务持有;
    • 意向锁(IS/IX):表级锁,表明事务即将对表内行加S/X锁(避免表锁与行锁冲突)。
  • 按算法分
    • 记录锁(Record Lock):锁定单行索引记录;
    • 间隙锁(Gap Lock):锁定索引间隙(不包含记录本身);
    • Next-Key Lock:记录锁+间隙锁(锁定左开右闭区间)。
11. 死锁产生条件、检测与避免
  • 产生条件(缺一不可):
    • 互斥:锁只能被一个事务持有;
    • 请求与保持:持有锁的同时请求新锁;
    • 不剥夺:锁只能由持有者释放,不能被强制剥夺;
    • 循环等待:事务间形成锁等待环。
  • InnoDB死锁检测
    • 等待图(Wait-for Graph):检测是否有环,有环则选择回滚代价小的事务(如修改行数少的)。
  • 避免方法
    • 统一加锁顺序(如先锁表A再锁表B);
    • 减少事务大小,缩短锁持有时间;
    • 避免大事务,拆分为小事务;
    • 合理设计索引,减少锁范围(如避免全表扫描);
    • 降低隔离级别(如从RR降到RC,减少间隙锁)。
12. 乐观锁 vs 悲观锁
  • 悲观锁
    • 假设并发冲突高,先加锁再操作;
    • 实现:select ... for update(排他锁)、lock in share mode(共享锁);
    • 适用:写多读少场景。
  • 乐观锁
    • 假设并发冲突低,操作时不加锁,提交时检查版本;
    • 实现:版本号(如update t set ..., version=version+1 where id=1 and version=old_version)、CAS;
    • 适用:读多写少场景。

四、日志与崩溃恢复

13. redo log、undo log、binlog的区别
特性redo logundo logbinlog
作用保证持久性(崩溃恢复)保证原子性(回滚)+ MVCC数据备份、主从复制
存储引擎InnoDB独有InnoDB独有Server层(所有引擎)
内容物理日志(“在某个数据页上做了什么修改”)逻辑日志(“把某行数据恢复到修改前的状态”)逻辑日志(SQL语句或行数据变更)
写入时机事务执行中先写redo log buffer,再刷盘事务执行中修改数据前写入事务提交时写入
大小固定大小(循环写入)不固定(随事务增长)不固定(可配置滚动)
14. 两阶段提交(2PC)过程
  • 目的:保证redo log和binlog的一致性(崩溃恢复时数据不丢失、不重复)。
  • 过程
    1. 准备阶段:InnoDB将redo log写入磁盘,状态设为“prepare”;
    2. 提交阶段
      • Server层将binlog写入磁盘;
      • InnoDB将redo log状态改为“commit”,事务完成。
  • 崩溃恢复
    • 若redo log是commit状态:直接提交;
    • 若redo log是prepare状态:检查binlog是否完整,完整则提交,否则回滚。
15. binlog三种格式对比
  • STATEMENT
    • 记录SQL语句(如update t set age=age+1 where id=1);
    • 优点:日志量小;缺点:可能导致主从不一致(如now()limit无order by)。
  • ROW
    • 记录行数据变更(如“id=1的行,age从10改为11”);
    • 优点:主从一致;缺点:日志量大(如批量update)。
  • MIXED
    • 混合模式,默认用STATEMENT,特殊场景自动切换为ROW(如涉及函数、UUID等)。

五、SQL优化与执行计划

16. EXPLAIN关键字段解读
字段含义重点关注值
id查询序列号id相同:从上到下执行;id不同:id大的先执行
select_type查询类型SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)、DERIVED(派生表)、UNION(联合查询)
table操作的表-
type访问类型(性能从好到差)system > const > eq_ref > ref > range > index > ALL
(至少达到range,最好ref)
possible_keys可能用到的索引-
key实际用到的索引若为NULL,未用索引
key_len索引使用的长度越短越好(联合索引中可判断用到了几列)
ref与索引比较的列const(常量)、字段名
rows预计扫描的行数越少越好
Extra额外信息Using index(覆盖索引,好)、Using where(需过滤)、Using temporary(临时表,差)、Using filesort(文件排序,差)
17. 常见SQL优化技巧
  • 索引优化
    • 区分度低的字段(如性别)不建索引;
    • 联合索引按区分度从高到低排序;
    • 避免索引失效场景(见前文)。
  • 查询语句优化
    • 避免SELECT *,只查需要的列;
    • JOIN代替子查询(子查询可能产生临时表);
    • LIMIT大偏移量优化:
      • 延迟关联:select t.* from t join (select id from t limit 10000,10) as tmp on t.id=tmp.id
      • 记录上次查询位置:select * from t where id > last_id limit 10
  • 表结构优化
    • 选择合适的数据类型(如用INT存IP,用TINYINT存状态);
    • 避免NULL(NULL值占用额外空间,索引统计复杂);
    • 适当反范式(如冗余字段减少JOIN)。

六、架构与高可用

18. 主从复制原理与流程
  • 原理
    • 主库将数据变更写入binlog;
    • 从库I/O线程读取主库binlog,写入中继日志(relay log);
    • 从库SQL线程重放中继日志,将变更应用到从库。
  • 流程
    1. 主库执行事务,写入binlog;
    2. 主库dump线程将binlog推送给从库I/O线程;
    3. 从库I/O线程将binlog写入relay log;
    4. 从库SQL线程读取relay log,执行变更,数据同步完成。
19. 主从延迟原因与解决方案
  • 原因
    • 主库并发高,binlog写入量大,从库SQL线程单线程重放慢;
    • 从库硬件配置差(CPU、内存、磁盘I/O);
    • 网络延迟;
    • 大事务(如批量update、delete)。
  • 解决方案
    • 从库升级硬件(优先SSD);
    • 开启从库多线程复制(MySQL 5.7+支持基于组提交的并行复制);
    • 拆分大事务为小事务;
    • 读写分离,减少主库压力;
    • 采用半同步复制(保证至少一个从库收到binlog)。
20. 分库分表策略
  • 垂直拆分
    • 按业务模块拆分表(如用户表、订单表拆到不同库);
    • 按列拆分表(如大字段拆到独立表);
    • 优点:拆分规则简单;缺点:跨库JOIN困难。
  • 水平拆分
    • 按行拆分表(如按用户ID哈希、按时间范围);
    • 优点:单表数据量可控;缺点:拆分规则复杂,数据迁移困难。
  • 常见中间件
    • ShardingSphere(Apache顶级项目,支持分库分表、读写分离、分布式事务);
    • MyCat(国产中间件,功能丰富,社区活跃)。

七、InnoDB存储引擎核心特性

21. Buffer Pool(缓冲池)
  • 作用
    • 缓存磁盘数据页(减少磁盘I/O),包含数据页、索引页、undo页、change buffer等;
    • 大小通常设为物理内存的50%-80%(innodb_buffer_pool_size)。
  • LRU算法优化
    • 传统LRU:最近最少使用的页淘汰;
    • InnoDB优化:将LRU链表分为young区(热数据)和old区(冷数据);
    • 新页先插入old区,若在old区停留时间超过innodb_old_blocks_time(默认1s),才移到young区;
    • 避免全表扫描时热数据被淘汰。
22. Change Buffer(变更缓冲区)
  • 作用
    • 缓存二级索引的DML操作(INSERT、UPDATE、DELETE),减少磁盘I/O;
    • 当查询该索引页时,将change buffer中的变更合并到数据页(merge)。
  • 适用场景
    • 二级索引(聚簇索引不适用);
    • 索引区分度低(如状态字段),DML频繁且查询少。
  • 限制
    • 唯一索引不适用(需检查唯一性,必须读磁盘)。
23. Double Write Buffer(双写缓冲区)
  • 作用
    • 解决部分写失效(partial page write)问题:若数据库崩溃时,数据页只写了一半到磁盘,redo log无法恢复(redo log是物理日志,基于完整页);
  • 流程
    1. 先将数据页写入double write buffer(内存),再刷到共享表空间(磁盘);
    2. 成功后,再将数据页刷到各自的表空间;
    3. 若崩溃,从double write buffer恢复完整页,再用redo log恢复。

以上内容覆盖中高级开发工程师需掌握的MySQL核心知识点,建议结合实际项目经验深入理解,并通过LeetCode数据库题目、慢查询优化实战巩固。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值