ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

mysql日常学习及面试题

mysql日常学习及面试题
  • 本文旨在记录近期面试中遇到的 MySQL 核心考点,帮助深入理解 MySQL 内部构造,为日后工作中排查疑难问题打下基础。
  • 文中内容多由 AI 辅助生成,如有疏漏或错误,恳请指正。欢迎一起学习,共同进步。

一. InnoDB和MyISAM的区别?(考点:事务)

核心区别

  • 事务支持:InnoDB 支持事务,MyISAM 不支持。
  • 锁粒度:InnoDB 支持行级锁和表级锁,MyISAM 仅支持表级锁。因此,在高并发场景下,InnoDB 的性能表现更优。
  • 外键约束:InnoDB 支持外键,MyISAM 不支持。

事务区别详述:

a. InnoDB 完整实现 ACID,支持四种事务隔离级别:
读未提交、读已提交、可重复读(MySQL 默认)、串行化。

b. 依靠 undo log(回滚日志)与 redo log(重做日志)实现事务:

  • redo log:用于崩溃恢复,保证事务的持久性。
  • undo log:用于事务回滚,并支持 MVCC(多版本并发控制)。

c. MyISAM 没有事务机制:

  • DML 语句执行中途失败无法回滚,可能导致数据部分写入。
  • 没有 commit/rollback 概念,每条 SQL 都被视为独立操作。

实战影响:

  • 对于订单、支付、账户扣减等强一致性业务,必须使用 InnoDB。
  • 对于简单的日志表或静态数据,且无需事务的场景,可考虑 MyISAM(目前极少使用)。

锁粒度区别详述:

InnoDB 支持行锁和表锁,而 MyISAM 仅支持表锁。

  • MyISAM 表级锁

    • 执行 UPDATE、DELETE、INSERT 等写操作时,会立即锁定整张表。
    • 问题:同一时刻只能有一个线程执行写操作,其他读写操作均被阻塞。
    • 适用场景:读多写极少。在高并发写入场景下极易发生阻塞,产生大量等待。
  • InnoDB 行级锁(Record Lock)

    • 前提:WHERE 条件必须使用有效索引,否则行锁会退化为表锁(高频面试点)。
  • 附加锁机制:

    • Gap Lock(间隙锁)Next-Key Lock(临键锁),用于在默认的“可重复读”隔离级别下解决幻读问题。
    • InnoDB 也可手动加表锁(如LOCK TABLES ... WRITE;),但开发中一般不推荐使用。
  • 并发差异总结

    • 高并发读写、频繁更新的场景:InnoDB 优势显著。
    • MyISAM 的写操作会阻塞所有读写,在并发写入场景下性能会急剧下降。

外键约束区别详述(没太搞懂):

InnoDB 支持,MyISAM 不支持

  • 外键作用:保证参照完整性
    例如订单表 order 关联用户表 user,外键可以阻止插入不存在用户 ID 的订单;
  • 外键底层要求:
    关联字段必须同类型、建立索引;
    生产环境普遍不推荐使用外键!(重要实战考点)
  • 原因:
    外键约束校验增加数据库压力;
    分布式分库分表场景下,外键完全失效;
    出现死锁概率提升;
    微服务架构下,数据完整性一般交给应用代码控制,不在数据库层面约束。
  • 结论:InnoDB 虽然支持外键,但企业项目大多禁用。

补充区别

  • MVCC(多版本并发控制)
    InnoDB:支持 MVCC,不加锁实现读操作(快照读),读写不冲突;
    MyISAM:没有 MVCC,查询是当前数据,写阻塞读。
  • 崩溃恢复
    InnoDB:依靠 redo log,宕机重启自动恢复数据,安全性高;
    MyISAM:无崩溃安全机制,断电、宕机极易损坏表文件,需要执行repair table修复。
  • 索引结构与存储
    InnoDB:聚簇索引,主键和数据存在同一个文件;二级索引存储主键值;
    MyISAM:非聚簇索引,数据文件和索引文件完全分离。
  • 缓存
    InnoDB:缓冲池 (Buffer Pool) 缓存索引 + 数据页;
    MyISAM:缓存只存索引,数据靠操作系统文件缓存。

二. 为什么都用B+Tree作为索引结构?(考点:B+Tree的索引结构)

InnoDB与MyISAM默认采用B+Tree结构
(为什么不用二叉树、AVL、红黑树、B-Tree、哈希表)

  • 对比二叉树 / 红黑树
    只有两路分支,树很高,磁盘 IO 多;B+Tree 多路平衡,树矮,减少 IO。
  • 对比 B-Tree
    B-Tree 节点同时存 key + 数据;
    B+Tree 非叶子只存 key,一页能放下更多索引,树更低;叶子用有序链表相连,范围查询、排序更快;所有查询都走到叶子,性能稳定。
  • 对比哈希表
    哈希只支持等值查询,不支持范围、排序、前缀匹配,无法满足大部分 SQL 场景。
  • 总结:磁盘 IO 是瓶颈,B+Tree 最大限度降低 IO,适配数据库等值 + 范围查询需求。

话术总结:

  • B+Tree的所有叶子节点通过双向链表项链,更适合范围查询
  • B+Tree的所有数据存放在叶子节点中,非叶子节点仅作路由作用,因此每次查询都会走到叶子节点,因此查询路径长度固定,效率稳定
  • B+Tree的非叶子节点只存储索引键值,不存储数据,这样每个节点能容纳更多键值,树的高度更低,查询所需的IO次数也就更少

三. 什么是聚簇索引?InnoDB有聚簇索引吗?(考点:B+Tree的查询机制,sql优化(回表))

话术总结:

  • 聚簇索引与数据行的存储顺序一直,数据本身直接存放在索引的叶子节点上,一张表只能有一个聚簇索引
  • InnoDB一定存在聚簇索引,创建规则:①:默认主键索引。②:如无主键,默认为第一个非空唯一索引。③:以上都没有则在表生成时创建一个名为row_id的隐藏聚簇索引(字节)

扩展:

  • 除聚簇索引外(如联合索引等),也叫二级索引的存储顺序与物理行的存储顺序不一致,它的也自己节点存储的是对应的主键值
  • 回表:当通过非聚簇索引进行查询时,如果select 的字段包含除主键外的其他字段,则此时需要根据叶子节点里的主键值去聚簇索引上对应的主键值所在的叶子节点获取对应的行数据,此时这个动作就叫回表。(也不是所有的二级索引会引发回表,只要保证当前select的字段在当前索引中存在即可避免回表,比如联合索引字段a,b,此时select a,b便不会引发回表操作)

四. MVCC 具体是什么?有什么特点?(考点:隔离级别,具体应用看第五题)

  • 话术总结:MVCC 会为一条数据维护多个历史版本,通过 undo log 构建版本链;事务查询时利用 ReadView 选择满足可见性规则的数据版本。快照读读取历史版本,无需加锁,实现读写不阻塞,以此完成并发控制,这就是多版本并发控制。
  • 现象层面总结
    ① 依靠 ReadView 可见性规则,看不到其他事务未提交版本,避免脏读
    ② 写事务持有排他锁阻塞其他写请求;快照读不走锁,读取 undo 历史版本,实现读写不阻塞
  • 个人总结:主要应用在’读已提交’与’可重复读’的隔离级别,在这两种隔离级别中,当开启事务后进行select操作,会通过undo log构建的版本链找到一条符合可见规则的历史版本返回查询结果。对于这两种不同的隔离级别有着不用的处理。

举个例子,对于不同级别下MVCC的一个处理机制与结果(两个事务并发执行)

#事务Abegin;#开启事务selectid,name,statuswhereid=1;selectid,name,statuswhereid=1;selectid,name,statuswhereid=1;
#*事务Bbegin;#开启事务updatetsetstatus=0whereid=1;commit;#提交事务

已以上两个事务,A事务首先执行但并未提交,B事务随后执行已提交

  • 读已提交:在事务A执行时,此时查询的结果为当前行数据status字段=0,在事务B提交后再次查询当前行数据status字段=1,出现不可重复读
  • 可重复五:在事务A执行时,此时在事务A提交之前查询的结果一直为当前行数据status字段=1,前后查询结果一致,规避不可重复读
    MVCC 简要执行流程,执行普通 select 快照读:
    1.生成 ReadView(RC 每次查询新建;RR 事务首次快照读创建,全程复用);
    2.顺着 undo log 的回滚指针遍历版本链;
    3.使用 ReadView 规则逐一判断每条版本是否对当前事务可见;
    4.返回第一条满足可见性的数据版本。

五. MySQL默认隔离级别是什么?能解决哪些问题(考点:隔离级别)

数据操作的三个定义:

  • 脏读:读到其他事务未提交的数据;对方回滚,读到的数据无效。
  • 不可重复读:同一事务内,两次查询同一行,中间被别的事务修改提交,两次结果不一样。
  • 幻读:同一事务范围查询,别的事务新增 / 删除数据并提交,前后查询行数不一致。
  • 区分:不可重复读侧重数据更新;幻读侧重新增、删除。

1. READ UNCOMMITTED 读未提交(最低隔离级别)

  • 允许读取未提交数据
  • 存在问题:脏读、不可重复读、幻读
  • 线上几乎没人使用
    如:事务 B 还没 commit,事务 A 就能看到它的修改,极易读到脏数据。

2. READ COMMITTED(RC)读已提交

Oracle 默认级别,很多互联网项目主动切换至此,对于一致性要求不是特别严格的可以用

  • 只能读到其他事务已经提交的数据
  • ✅ 解决:脏读
  • ❌ 存在:不可重复读、幻读

此处MVCC特点

  • 每次普通 select(快照读)都会新建 Read View。
  • 别的事务提交更新后,当前事务再次查询能立刻看到最新值。
  • 优点:间隙锁失效,锁范围更小,死锁概率降低;
    缺点:同一事务多次查询同一行,结果可能变化(不可重复读)。

3. REPEATABLE READ(RR)可重复读

InnoDB 默认隔离级别

  • ✅ 解决:脏读、不可重复读
  • ⚠️幻读:
    快照读(普通 select):MVCC 快照,看不到新插入数据,感受不到幻读
    当前读(update/delete/for update):仍会出现幻读,依靠临键锁 Next-Key Lock解决

此处MVCC特点

  • 事务中第一次快照读时创建 Read View,整个事务复用。
  • 同一事务多次查询,始终看到同一套快照数据,不受外部事务提交影响。

4. SERIALIZABLE 串行化

最高隔离级别

  • ✅ 脏读、不可重复读、幻读全部解决
  • 工作方式:普通 select 自动转为 select … lock in share mode,全部变成当前读,读写互相阻塞。
    缺点:并发能力极差,大量锁等待、死锁,业务极少使用(常规项目正式生产环境基本不用)
隔离级别脏读不可重复读幻读
读未提交发生发生发生
读已提交 RC杜绝发生发生
可重复读 RR (默认)杜绝杜绝快照读规避;当前读依靠临键锁解决
串行化杜绝杜绝杜绝
  • 扩展:RC 和 RR 最核心区别?
    回答:ReadView 生成时机:RC 每次快照读新建;RR 事务首次快照读创建,全程复用。

六. 拿到一条慢SQL该做什么?(考点:SQL优化流程)

  • 话术总结:
    在格式没问题的前提下,优先聚焦where条件进行逐步排查
    ①. where后的查询条件是否走索引字段。
    ②是否索引失效(如条件加入函数运算、隐式转换、like '%xxx’前置通配符、in/not in等)
    ③索引正常命中,是否由于查询字段过多引发的回表操作导致性能过低。

完整排查流程:

① 定位慢SQL
  • 开启慢查询日志:SET GLOBAL slow_query_log = 'ON';
  • 设置阈值:SET GLOBAL long_query_time = 1;(单位:秒,默认10秒)
  • 查看慢日志:SHOW VARIABLES LIKE '%slow_query_log%';
  • 实时查看线程:SHOW FULL PROCESSLIST;
  • 借助监控工具(阿里云ARMS、Prometheus + Grafana)
② EXPLAIN分析执行计划
  • 语法:EXPLAIN SELECT ... FROM ... WHERE ...;
  • 重点关注以下字段:
字段含义优化目标
type访问类型至少达到range,最好是const/eq_ref/ref
key实际使用的索引不为 NULL
rows扫描行数越小越好
Extra额外信息避免Using filesortUsing temporary
  • type性能排序:system > const > eq_ref > ref > range > index > ALL
  • 出现ALL表示全表扫描,必须优化
③ 常见优化策略(扩展)
索引优化:
  • 为 WHERE、JOIN、ORDER BY 字段建索引
  • 联合索引遵循最左前缀法则
    -> 使用覆盖索引减少回表(Extra 中出现Using index
  • 避免索引失效(详见上方总结)
SQL 改写:
  • 禁止SELECT *,只查询需要的字段
  • 小表驱动大表(合理选用 IN / EXISTS)
  • 批量操作代替循环单条插入
  • 避免在索引列上使用函数、表达式、隐式类型转换
  • 合理使用分页:LIMIT+ 游标 / 主键分段
表结构优化:
  • 字段类型选择合理(尽量小、精确)
    -> NOT NULL 设默认值,减少 NULL 判断
  • 大字段(TEXT / BLOB)垂直拆分
  • 单表数据量过大(> 500万)考虑水平拆分
架构层面:
  • 引入缓存(Redis)减轻 DB 压力
  • 读写分离(主从架构)
  • 分库分表(Sharding-JDBC、MyCat)
  • 冷热数据分离
返回列表