MySQL作为最流行的关系型数据库之一,在技术面试中占据重要地位。我整理了多年面试和被面试的经验,将MySQL核心知识点分为以下几个关键部分进行深入解析。
InnoDB和MyISAM是MySQL最常用的两种存储引擎,它们的核心差异体现在:
事务支持:
锁机制:
索引结构:
崩溃恢复:
生产环境建议:除非特别简单的只读场景,否则都应选择InnoDB引擎。MySQL 8.0已经将InnoDB作为默认引擎,MyISAM逐渐被淘汰。
MySQL的InnoDB引擎使用B+树作为索引结构,相比B树有以下优势:
索引优化实战技巧:
InnoDB的表数据本身就是按照主键组织的聚簇索引。如果没有显式定义主键:
二级索引(非聚簇索引)的叶子节点存储的是主键值,查询时需要先查二级索引再查主键索引,这个过程称为"回表"。
原子性(A):通过undo log实现。事务回滚时,利用undo log中的记录恢复数据到事务开始前的状态。
一致性(C):由其他三个特性共同保证,确保数据从一个一致状态转变为另一个一致状态。
隔离性(I):通过锁机制和MVCC实现,控制并发事务之间的相互影响。
持久性(D):通过redo log实现。事务提交时,先将redo log持久化到磁盘,即使系统崩溃也能恢复数据。
MySQL支持四种隔离级别,解决不同的并发问题:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现方式 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✓ | ✓ | ✓ | 无锁 |
| READ COMMITTED | ✗ | ✓ | ✓ | 行锁,无间隙锁 |
| REPEATABLE READ | ✗ | ✗ | △ | MVCC+间隙锁 |
| SERIALIZABLE | ✗ | ✗ | ✗ | 全表锁 |
MySQL默认使用REPEATABLE READ级别,通过MVCC和间隙锁的组合,在这个级别下也能避免大部分幻读问题。
MVCC(Multi-Version Concurrency Control)通过以下机制实现:
行级锁:
表级锁:
锁算法:
常见死锁场景:
死锁排查方法:
sql复制SHOW ENGINE INNODB STATUS\G
查看LATEST DETECTED DEADLOCK部分获取死锁详细信息
死锁处理策略:
执行EXPLAIN分析SQL时,重点关注以下字段:
type:表示访问类型,从好到坏依次为:
system > const > eq_ref > ref > range > index > ALL
key:实际使用的索引
rows:预估需要检查的行数
Extra:额外信息,常见值:
慢查询配置:
sql复制SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; -- 设置慢查询阈值(秒)
SET GLOBAL log_queries_not_using_indexes = ON;
慢查询分析工具:
常见优化手段:
复制原理:
复制模式:
垂直拆分:
水平拆分:
分页优化方案:
sql复制SELECT * FROM table a
JOIN (SELECT id FROM table LIMIT 100000, 10) b
ON a.id = b.id;
窗口函数:
sql复制SELECT
name,
salary,
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) as rank
FROM employees;
通用表表达式(CTE):
sql复制WITH dept_stats AS (
SELECT dept_id, AVG(salary) avg_salary
FROM employees
GROUP BY dept_id
)
SELECT * FROM employees e
JOIN dept_stats d ON e.dept_id = d.dept_id
WHERE e.salary > d.avg_salary;
JSON增强:
COUNT(*) vs COUNT(1) vs COUNT(列):
CHAR vs VARCHAR:
索引失效案例:
sql复制-- 案例1:隐式类型转换
SELECT * FROM users WHERE mobile = 13800138000; -- mobile是varchar类型
-- 案例2:对索引列使用函数
SELECT * FROM orders WHERE YEAR(create_time) = 2024;
-- 案例3:前导通配符
SELECT * FROM products WHERE name LIKE '%苹果%';
死锁案例:
sql复制-- 事务A
BEGIN;
DELETE FROM inventory WHERE product_id = 1001;
INSERT INTO order_items VALUES (1001, 10);
COMMIT;
-- 事务B
BEGIN;
INSERT INTO order_items VALUES (1001, 5);
DELETE FROM inventory WHERE product_id = 1001;
COMMIT;
解决方案:统一操作顺序,先删后插或先插后删
MySQL面试不仅考察知识点的记忆,更注重实际问题的解决能力。建议结合自己的项目经验,准备几个典型的数据库优化案例,能够清晰描述问题背景、分析过程和最终解决方案。