MySQL面试问题汇总

35 阅读7分钟

1、MySQL 的存储引擎有哪些?

答:

  • InnoDB(默认):支持事务、行级锁、外键约束,适用于高并发写入。
  • MyISAM:不支持事务,表级锁,适用于读密集型应用。
  • Memory:数据存储在内存中,速度快,但重启后数据丢失。
  • Archive:适用于存储和检索大量归档数据,压缩率高。只支出插入和查询。

2、InnoDB 和 MyISAM 的区别?

特性InnoDBMyISAM
事务支持✅ 支持❌ 不支持
锁机制行级锁表级锁
外键支持✅ 支持❌ 不支持
崩溃恢复✅ 支持❌ 不支持
全文索引✅(MySQL 5.6+)✅ 支持
适用场景高并发写入、事务处理读多写少、查询快

3、什么是事务?MySQL 如何支持事务?

答:  事务是一组 SQL 操作,要么全部成功(COMMIT),要么全部失败(ROLLBACK)。MySQL 通过 InnoDB 引擎支持事务,并提供 ACID 特性:

  • A(Atomicity)原子性:事务不可分割。
  • C(Consistency)一致性:数据在事务前后保持一致。
  • I(Isolation)隔离性:事务之间互不干扰。
  • D(Durability)持久性:事务提交后数据永久保存。

4、MySQL 的隔离级别有哪些?

答:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED✅ 可能✅ 可能✅ 可能
READ COMMITTED❌ 不可能✅ 可能✅ 可能
REPEATABLE READ❌ 不可能❌ 不可能✅ 可能
SERIALIZABLE❌ 不可能❌ 不可能❌ 不可能

MySQL 默认隔离级别是 REPEATABLE READ(可重复读)。

脏读: 指的是一个事务读取了另一个尚未提交的事务修改过的数据。(数据尚未提交,被别人查询了

不可重复读: 指的是在同一个事务内,多次读取同一数据时,由于其他并发事务的修改或删除,导致前后读取的结果不一致。(数据可能不一致

幻读: 指的是在同一事务内,多次执行相同的查询,但由于其他并发事务插入(INSERT)了新数据,导致前后查询结果的"行数"发生变化。(数据一致,但是记录行数可能有变化

5、如何优化 MySQL 查询?

答:

  • 使用 EXPLAIN 分析 SQL 执行计划
  • 合理使用索引(避免全表扫描)
  • 避免 SELECT ***,只查询需要的字段
  • 优化 JOIN 查询(小表驱动大表)
  • 尽量少用子查询,必须使用时,用in,子查询的记录数越少越好。
  • 使用 LIMIT 分页(避免 OFFSET 过大)
  • 对适合分区的大表,进行分区操作
  • 对经常性需要聚合统计的表,设计中间聚合表,定期进行数据聚合

6、什么是死锁?如何避免?

答:  死锁是多个事务互相等待对方释放锁,导致无限阻塞。
避免方法:

  • 按固定顺序访问表和行
  • 减少事务持有锁的时间(比如查询扫描记录行数尽量少,索引选择度要高)
  • 合理使用索引,不同索引,字段顺序不要相反
  • 将大事务拆分成小事务,并且避免事务嵌套
  • 不要使用高级锁,或者强行指定高级锁。如 update 。。。。 for update。
  • 使用 SHOW ENGINE INNODB STATUS 分析死锁
  • 应用层面检测并设置超时重试等。

7、MySQL 主从复制的原理?

答:

  1. 主库(Master)  记录 binlog(二进制日志)。
  2. 从库(Slave)  的 IO 线程拉取 binlog。
  3. SQL 线程 重放 binlog 到从库。

8、什么情况下索引会失效?

答:

  • 使用 != NOT IN
SELECT * FROM users WHERE age != 30; 
  • LIKE 以通配符开头
SELECT * FROM users WHERE name LIKE '%张%';
  • 对列进行运算或函数操作
SELECT * FROM users WHERE YEAR(create_time) = 2023; 
  • OR 条件未全部使用索引
SELECT * FROM users WHERE id = 1 OR name = '张三'; -- 如果 name 无索引,全表扫描

9、什么是 MVCC?如何实现?

MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库管理系统(如MySQL InnoDB、PostgreSQL等)实现高并发访问的核心机制,它通过数据多版本快照读的方式,使读写操作可以并发执行而不互相阻塞,从而大幅提高数据库性能。

一、核心思想

MVCC 的核心是:

  • 保留数据的多个版本,每个事务看到的是符合其隔离级别的数据快照(Snapshot)
  • 读操作不阻塞写操作,写操作也不阻塞读操作
  • 通过版本链可见性判断实现不同事务看到不同的数据版本

二、实现关键

在 InnoDB 中,MVCC 主要依赖以下技术实现:

1). 隐藏字段

InnoDB 每行记录(row)包含几个隐藏字段:

  • DB_TRX_ID(6字节):最近修改该行的事务ID
  • DB_ROLL_PTR(7字节):回滚指针,指向 undo log 中的旧版本数据
  • DB_ROW_ID(6字节):行ID(如果没有主键,InnoDB 会自动生成)

2). Undo Log(回滚日志)

  • 存储数据修改前的旧版本,形成版本链
  • 用于事务回滚和 MVCC 的可见性判断

3). ReadView(读视图)

事务执行快照读时生成的一个数据可见性快照

包含:

    • m_ids:当前活跃(未提交)的事务ID列表
    • min_trx_id:最小活跃事务ID
    • max_trx_id:下一个要分配的事务ID
    • creator_trx_id:创建该 ReadView 的事务ID

三、如何判断数据可见性?

InnoDB 通过 ReadView + 版本链 判断某行数据是否对当前事务可见:

  1. 如果行的 DB_TRX_ID < min_trx_id,说明该行在 ReadView 创建前已提交,可见
  2. 如果 DB_TRX_ID ≥ max_trx_id,说明该行在 ReadView 创建后修改,不可见
  3. 如果 min_trx_id ≤ DB_TRX_ID < max_trx_id

如果 DB_TRX_ID 在 m_ids 中(即事务未提交),不可见

否则(事务已提交),可见

  1. 如果 DB_TRX_ID == creator_trx_id,说明是当前事务自己修改的,可见

如果不可见,则通过 DB_ROLL_PTR 找到 undo log 中的旧版本,继续判断。

10、MySQL 主从延迟怎么解决?

答:

优化从库配置

    • 提升从库硬件性能(CPU、SSD)。
    • 设置 slave_parallel_workers 启用并行复制。
    • 减少大事务:避免主库执行长时间事务。
    • 从库可以考虑临时关闭写日志参数。
    • 修改从库日志刷新方式参数。
sync_binlog=0
innodb_flush_log_at_trx_commit=120

1:每次事务提交都刷盘(最安全)
0:每秒刷盘一次(性能最好,风险最高)
2:每次提交写到OS缓存,每秒刷盘(折中)

11、InnoDB的索引组织结构是怎样的?为什么推荐使用自增主键?

答案:
InnoDB索引采用B+树结构,特点包括:

  • 非叶子节点只存索引键和指针
  • 叶子节点包含完整数据(聚簇索引)或主键值(二级索引)
  • 叶子节点通过双向链表连接

推荐自增主键原因:

  1. 插入性能:避免随机IO,减少页分裂
  2. 空间利用率:顺序写入填充率高
  3. 范围查询:对主键的范围查询效率极高

12、如何处理MySQL中的海量删除操作?

答案:

  • 如果是整表删除,使用drop/truncate
drop table log;
或者
truncate table log
  • 分批删除:

可以借助于脚本,进行遍历分批删除

DELETE FROM logs WHERE created_at < '2020-01-01' LIMIT 1000; -- 循环执行直到影响行数为0
  • 创建新表交换:
CREATE TABLE new_logs LIKE logs; 
INSERT INTO new_logs SELECT * FROM logs WHERE created_at >= '2020-01-01'; 
RENAME TABLE logs TO old_logs, new_logs TO logs; 
DROP TABLE old_logs;

13、如何优化大表查询

语句优化

1)增加 limit 限制查询返回条数

2)避免设置offset大数值的分页

select * from log limit 1000000,10; 
# 优化前
# 优化器需要一条一条,找到第1000000条,再向后找10条,并返回

select * from log where id > 1000000 order by id limit 10; 
# 优化后
# 优化器直接根据id 索引,一步到位,定位到第1000000 条记录,再向后找10条,并返回

索引优化

1)根据查询字段建立合适的索引

2)尽量建立覆盖索引,减少回表。

如果索引包含所有查询字段,则查询可以根据索引查询即可,不需要再回表,根据主键等查询到其他字段。

架构优化

1)一般日志性、统计性,或者一些不会经常性变更的表,可以根据时间字段,设计为分区表,可以减少查询时遍历大批无效数据

2)进行历史数据归档。按时间设置为原表名+时间格式的新表,业务可以根据规律进行历史数据查询。