为什么这份『MySQL面试题集』值得你收藏?
在当今数据驱动的时代,MySQL作为全球最流行的开源关系型数据库,被广泛应用于互联网、金融、电商、政务等核心业务系统中。从阿里、腾讯、字节跳动到中小创业公司,MySQL几乎无处不在。然而,面试中关于MySQL的问题远不止“什么是索引”“事务四大特性是什么”这种基础题——越来越多企业开始考察候选人对底层原理、性能瓶颈、故障排查、高可用设计等真实场景的掌握深度。
我们发现,多数求职者存在三大痛点:
- 仅背答案,缺乏原理理解(如:为什么RR隔离级别下InnoDB能防止幻读?)
- 只知命令,不懂执行计划(如:如何通过EXPLAIN判断SQL是否走索引失效?)
- 未经历线上事故,缺乏调优经验(如:大表DDL如何在线变更不锁表?)
本『MySQL面试题集』由一线DBA与技术面试官联合编写,覆盖【基础概念→SQL编写→索引机制→执行计划→锁机制→事务隔离→主从复制→分库分表→监控运维→故障排查】十大维度,每题均含:
✓ 考点解析
✓ 高频追问
✓ 实战案例
✓ 避坑指南
✓ 拓展思考
全文超3000字,所有内容均基于MySQL 8.0最新版本,结合真实生产环境经验整理,助你从“背题者”升级为“解题者”。
基础篇:那些你以为简单、实则暗藏玄机的考点
Q1:MySQL中MyISAM与InnoDB引擎的核心区别有哪些?为什么现在默认用InnoDB?
核心差异:
| 特性 | MyISAM | InnoDB |
|---|---|---|
| 事务支持 | ❌ 不支持 | ✅ 支持(ACID) |
| 锁粒度 | 表级锁 | 行级锁(支持MVCC) |
| 外键约束 | ❌ 不支持 | ✅ 支持 |
| 崩溃恢复 | ❌ 无自动恢复能力 | ✅ 通过redo log自动恢复 |
| COUNT(*)性能 | ✅ 极快(保存总行数) | 较慢(需全表扫描) |
| 全文索引 | ✅ 原生支持 | MySQL 5.6+ 支持 |
追问点:为什么MyISAM的COUNT(*)更快?—— 因为其元数据中保存了表行数(`table_rows`),但该值在有`WHERE`条件或并发写入时可能不准确;而InnoDB需实际扫描数据页计数,更可靠但更慢。
为什么默认用InnoDB?
自MySQL 5.5起(2010年),官方将InnoDB设为默认引擎,原因有三:
- 事务需求普及:电商订单、支付流水等业务必须强一致性;
- 并发写入场景激增:表锁易导致锁等待,行锁更适应高并发;
- 高可用演进需求:InnoDB支持热备(Percona XtraBackup)、在线DDL等特性,是主从复制、集群的基础。
Q2:CHAR与VARCHAR的区别?TEXT类型何时使用?
CHAR:固定长度字符串,存储时用空格补足长度;查询时自动去除尾部空格(除非`PAD_CHAR_TO_FULL_LENGTH`模式)。适合长度固定的数据(如性别、省份编码)。
VARCHAR:可变长度字符串,仅存储实际内容+1~2字节长度标识。适合长度不定的数据(如用户名、地址)。
TEXT家族:
- `TINYTEXT`:最大255字节
- `TEXT`:最大65KB
- `MEDIUMTEXT`:最大16MB
- `LONGTEXT`:最大4GB
关键注意:TEXT字段无法使用普通索引(需指定前缀长度,如`INDEX(content(255))`),且在排序、GROUP BY时会使用临时表,影响性能。建议:
- 能用VARCHAR就不用TEXT(如微博正文用VARCHAR(500));
- 大文本内容可拆分存储(主表存摘要+ID,详情表存全文);
- 全文检索用`FULLTEXT`索引或Elasticsearch。
面试高频场景:执行`EXPLAIN SELECT * FROM users WHERE email = 'test@abc.com';`后,重点关注以下列:
- `type`:`ALL`表示全表扫描(最差),`range`(范围扫描)、`ref`(非唯一索引扫描)、`const`(常量查询)更优;
- `key`:实际使用的索引名,若为`NULL`则未用索引;
- `rows`:预估扫描行数,越小越好;
- `Extra`:`Using index`(覆盖索引)、`Using where`(回表)、`Using temporary`(需建临时表)、`Using filesort`(需额外排序)均为性能预警信号。
避坑示例:`WHERE SUBSTRING(name,1,3) = '张'`会导致索引失效!正确写法:`WHERE name LIKE '张%'`。
Q3:MySQL的事务四大特性(ACID)如何实现?
虽然面试常被问及定义,但企业更关注实现机制:
- Atomicity(原子性):通过undo log实现回滚。每条写操作前先记录undo日志,失败时反向执行undo操作;
- Consistency(一致性):由应用层+数据库约束共同保障(如外键、CHECK约束、触发器);
- Isolation(隔离性):通过锁机制(next-key lock)+ MVCC(多版本并发控制)实现;
- Durability(持久性):通过redo log(物理日志,循环写入)+ double write buffer(防止部分写失败)保障。
延伸追问:RR(可重复读)隔离级别下,InnoDB如何防止幻读?
答案:使用Next-Key Lock(记录锁+间隙锁的组合)。例如:
`SELECT * FROM t WHERE id > 10 FOR UPDATE;`
会锁定(10, +∞)区间,包括11、12等记录,同时阻塞新记录插入,从而避免幻读。
优化篇:从SQL编写到索引设计的性能跃升
Q4:如何编写高性能的JOIN语句?驱动表选择有哪些坑?
MySQL的JOIN实现为Nested-Loop Join(嵌套循环连接),小表驱动大表是铁律。但注意:
- 驱动表≠小表:优化器会自动选择过滤后行数更少的表为驱动表;
- 被驱动表必须有索引:JOIN字段(如`users.id = orders.user_id`)需在被驱动表建索引;
- BNL算法陷阱:若被驱动表无索引,MySQL会启用BNL(Block Nested-Loop),用join buffer缓存驱动表数据,但内存不足时性能极差。
优化案例:
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active';
ALTER TABLE orders ADD INDEX idx_user_id (user_id);
Q5:索引失效的10大场景(附真实案例)
以下情况会导致索引失效,需重点规避:
| 失效场景 | 错误示例 | 正确写法 |
|---|---|---|
| 函数/表达式操作 | `WHERE YEAR(create_time)=2023` | `WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'` |
| 隐式类型转换 | `WHERE phone = 13800138000`(phone为VARCHAR) | `WHERE phone = '13800138000'` |
| NOT / != / <> | `WHERE status != 'invalid'` | `WHERE status = 'valid'`(若有效值少) |
| LIKE以%开头 | `WHERE name LIKE '%张'` | `WHERE name LIKE '张%'` |
| OR条件未全建索引 | `WHERE a=1 OR b=2`(a、b单列索引) | `WHERE a=1 UNION ALL SELECT ... WHERE b=2 AND a!=1` |
进阶技巧:联合索引需遵循最左前缀原则。例如索引`INDEX(a,b,c)`:
- `WHERE a=1 AND b=2` ✅ 可用
- `WHERE b=2 AND c=3` ❌ 不可用
- `WHERE a=1 AND c=3` ✅ 可用a,但c需回表
大表分页优化:`LIMIT 1000000,10`会导致扫描100万+10行数据,性能极差。优化方案:
SELECT * FROM orders
WHERE id > 1000000
ORDER BY id
LIMIT 10;
SELECT o.* FROM orders o
INNER JOIN (
SELECT id FROM orders
ORDER BY create_time
LIMIT 1000000, 10
) t ON o.id = t.id;
覆盖索引:查询字段全部在索引中,无需回表。例如索引`INDEX(user_id, status, amount)`可优化:
SELECT user_id, status, amount
FROM orders
WHERE user_id = 123;
架构篇:主从复制、分库分表与高可用设计
Q6:MySQL主从复制的原理与延迟问题排查
核心流程:
- 主库写入binlog(Binary Log);
- 从库IO线程拉取binlog并写入relay log;
- 从库SQL线程重放relay log更新数据。
延迟常见原因:
- 大事务:主库一个事务写入10万行,从库需串行重放;
- 从库压力大:从库同时承担读请求,CPU/IO资源被抢占;
- 参数配置不当:如`sync_binlog=1`导致频繁刷盘;
- 单线程复制:MySQL 5.6前SQL线程单线程,5.7起支持组提交(GTID+多线程)。
排查命令:
SHOW MASTER STATUS;
SHOW SLAVE STATUS\G
Q7:分库分表方案选型(ShardingSphere vs MyCat)
当单表数据超500万,或写入QPS超2000,需考虑分片:
| 方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| ShardingSphere(Apache) | Java生态原生,支持读写分离、分布式事务 | 需业务改造,运维复杂 | 互联网中大型项目 |
| MyCat | 对应用透明(伪装MySQL) | 社区活跃度下降,高可用依赖外部组件 | 传统企业系统改造 |
| ProxySQL | 轻量级,专注读写分离+查询缓存 | 不支持分片 | 读多写少场景 |
分片键选择原则:
- 高频查询字段(如`user_id`);
- 分布均匀(避免数据倾斜);
- 不可变(如订单号,非创建时间)。
从单机 → 主从 → MHA(Master High Availability)→ MySQL Group Replication(MGR)→ 分布式数据库(TiDB/ClickHouse)
基于Paxos协议实现多主复制,数据强一致性,支持自动故障转移,但要求网络延迟<10ms。
AWS RDS、阿里云PolarDB等云原生数据库已内置高可用、自动备份、弹性扩缩容能力,中小团队优先选用。
安全篇:权限控制、SQL注入与审计
Q8:如何防止SQL注入?MyBatis中#与$的区别?
本质区别:
- `#{param}`:预编译参数(PreparedStatement),值作为参数传入,自动转义;
- `${param}`:字符串拼接,直接替换SQL文本,存在注入风险。
错误示例:
SELECT * FROM users WHERE name = '${param}';
正确写法:
SELECT * FROM users WHERE name = #{param};
额外防护:
- 最小权限原则:应用账号仅授予必要权限(如SELECT/INSERT/UPDATE,禁用DROP/ALTER);
- 输入校验:后端校验参数类型、长度、格式(如邮箱、手机号正则);
- 开启审计日志:记录所有SQL执行(需配置`general_log`或使用Percona Server)。
Q9:MySQL密码安全策略与弱口令检测
MySQL 5.7+ 引入`validate_password`插件:
SHOW VARIABLES LIKE 'validate_password%';
SET GLOBAL validate_password.length = 8;
SET GLOBAL validate_password.mixed_case_count = 1;
SET GLOBAL validate_password.number_count = 1;
SET GLOBAL validate_password.special_char_count = 1;
弱口令检测脚本:
SELECT user, host, authentication_string
FROM mysql.user
WHERE authentication_string = '' OR
authentication_string IS NULL OR
password = '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9' ;
实战篇:生产环境真实问题与解决方案
Q10:大表DDL如何在线变更而不锁表?
传统`ALTER TABLE`会锁表,导致写入阻塞。解决方案:
- pt-online-schema-change(Percona工具):
- 新建空表,结构为变更后版本;
- 创建触发器同步新旧表数据;
- 分批拷贝数据;
- 切换新表名,删除旧表。
- MySQL 5.6+ INPLACE DDL:支持部分操作(如ADD COLUMN)在线执行,但需满足条件;
- 云数据库方案:阿里云DMS、AWS Schema Change Optimization可自动优化DDL流程。
案例:某电商订单表(2亿行)需新增字段`is_deleted`,使用pt-online-schema-change耗时4小时,期间无业务中断。
Q11:如何诊断“慢查询”?慢查询日志配置与分析
开启慢查询日志:
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = ON;
SHOW VARIABLES LIKE 'slow_query_log_file';
分析工具:
- `mysqldumpslow -s c -t 10 /var/log/mysql/slow.log`:统计TOP10慢查询;
- `pt-query-digest`:深度分析,给出优化建议;
- 云厂商控制台(如阿里云DBAudit):可视化慢查询热力图。
关键指标:查询时间、扫描行数、锁等待时间、临时表使用次数。
Q12:内存使用过高?InnoDB Buffer Pool调优实战
`innodb_buffer_pool_size`是影响性能最关键的参数,通常设为物理内存的50%~70%(独占数据库服务器)。
调优步骤:
- 监控`Innodb_buffer_pool_read_requests`(逻辑读)与`Innodb_buffer_pool_reads`(物理读);
- 计算命中率:
`命中率 = (逻辑读 - 物理读) / 逻辑读 × 100%`
正常应>99%,若<95%需增大Buffer Pool; - 动态调整(MySQL 5.7+支持在线扩容):
`SET GLOBAL innodb_buffer_pool_size = 4*1024*1024*1024;`
避坑:不要设为100%,需预留内存给OS、Query Cache、线程栈等。
最后的话:技术成长的正确路径
面试不是背题,而是展现解决问题的思维。我们建议:
- 理解原理:知道“为什么”比“是什么”更重要;
- 动手实践:在测试环境复现问题,比阅读十遍文档更有效;
- 输出总结:写技术博客、分享案例,知识留存率提升70%;
- 关注生态:MySQL只是基础,还需了解Redis(缓存)、Kafka(消息)、TiDB(分布式)等协同技术。
本『MySQL面试题集』将持续更新,欢迎访问:www.kaoacca.cn