MySQL 基础

背景与动机#

Java 后端开发中,MySQL 是最常见的关系型数据库。面试和日常开发都会反复碰到:选 InnoDB 还是 MyISAM、事务隔离级别、索引为什么快、慢 SQL 怎么优化。本篇按「引擎 → 事务 → 索引 → 锁 → 优化」组织,便于和面试速查对照。

核心原理拆解#

1. 存储引擎:MyISAM 与 InnoDB#

维度MyISAMInnoDB
事务不支持支持 ACID
锁粒度表锁行锁(也支持表锁)
外键不支持支持
总行数保存不保存(需 count)
文件.frm.MYD.MYI表空间(共享或独立)
索引非聚集索引聚集索引(主键)+ 二级索引

选型:现代业务默认 InnoDB;只读、不要求事务的归档表才考虑 MyISAM。

2. MySQL 中的锁#

类型开销加锁速度死锁粒度并发
表锁不会
行锁
页锁介于二者介于二者介于二者一般

InnoDB 在可重复读下通过 MVCC + 行锁实现并发;锁优化思路:读写分离、缩小锁粒度、缩短持锁时间、多线程以相同顺序访问资源。

3. 事务与 ACID#

事务是一组 SQL 的逻辑单元,要么全部成功提交,要么全部回滚。

  • 原子性(A):全部执行或全部不执行
  • 一致性(C):数据从一种合法状态到另一种合法状态
  • 隔离性(I):并发事务互不干扰(由隔离级别控制)
  • 持久性(D):提交后结果持久保存

默认 autocommit=1,每条语句自动提交。需要事务时:SET autocommit=0START TRANSACTION,再 COMMIT / ROLLBACK

4. 四种隔离级别#

级别说明脏读不可重复读幻读
READ UNCOMMITTED读未提交可能可能可能
READ COMMITTED读已提交可能可能
REPEATABLE READ可重复读(InnoDB 默认)理论上可能,InnoDB 用 MVCC+间隙锁缓解
SERIALIZABLE串行化

5. 索引#

索引是帮助 MySQL 高效获取数据的数据结构。InnoDB 默认用 B+ 树

  • 叶子节点存数据(聚集索引存整行,二级索引存主键值)
  • 叶子节点之间有指针,适合范围查询
  • 建议使用自增主键,减少页分裂
类型说明
普通索引加快查询,允许重复
唯一索引列值唯一
主键索引特殊的唯一索引,一张表一个
联合索引多列组合,如 INDEX(a, b)

代价:加快读,减慢写(维护索引);占磁盘空间。

索引失效常见情况

  • LIKE '%xxx' 左模糊
  • OR 两侧未同时走索引
  • 隐式类型转换(如 varchar 与数字比较)
  • 对索引列做函数或运算

6. 连接查询#

  • 内连接:只返回两表匹配的行
  • 左外连接:左表全保留,右表无匹配填 NULL
  • 右外连接:右表全保留
  • 自连接:表与自身连接
  • 交叉连接:笛卡尔积,一般需加条件

7. 常用字段与类型#

  • CHAR vs VARCHAR:CHAR 定长、尾部空格处理;VARCHAR 变长,更省空间
  • 金额DECIMAL(p,s),避免浮点误差
  • 时间DATETIME(8 字节,与时区无关)、TIMESTAMP(4 字节,受时区影响,可自动更新)、DATE/TIME
  • 大文本TEXT(不区分大小写排序)vs BLOB(二进制)

8. SQL 分类#

  • DDLCREATEALTERDROP
  • DMLSELECTINSERTUPDATEDELETE
  • DCLGRANTREVOKE
  • DQL:查询(常归入 DML 讨论)

9. EXPLAIN 与慢 SQL 优化(面试必准备一个案例)#

索引为什么快:B+ 树把「全表扫描」变成「按索引定位」,减少读取页数。
什么时候加索引:WHERE / JOIN / ORDER BY 里高频、区分度好的列;避免过多索引拖慢写入。

EXPLAIN 看什么(记 3 个就够):

关注点
type最好 ref/range,避免 ALL(全表扫描)
key实际用到的索引
rows预估扫描行数,越小越好

话术模板(可套自己的项目)

列表接口变慢,EXPLAIN 看到 type=ALL、rows 很大,
在 where 里的 tenant_id + create_time 上加了联合索引后,
type 变为 range,接口从 3s 降到 300ms 以内。

10. 实践优化顺序#

  1. SQL 与索引优化(含 EXPLAIN)
  2. 表结构设计
  3. 数据库参数配置
  4. 硬件

常见手段:避免 SELECT *、减少不必要的 JOIN、读写分离、分表、缓存(Redis 等)、合理索引、字段类型尽量小。

11. SQL 注入与防护#

原因:拼接 SQL 时未过滤特殊字符。防护:预编译(PreparedStatement)、参数校验、最小权限、避免暴露错误堆栈。

常见陷阱与错误示例#

1. 在 InnoDB 表上使用 SELECT COUNT(*) 期望 O(1)#

InnoDB 不保存总行数,COUNT(*) 需扫描索引或全表(视版本与优化器而定)。

2. 联合索引不满足最左前缀#

索引 (a, b, c) 查询只有 b 条件时通常用不上该索引。

3. 大事务长时间持锁#

拆小事务,避免在事务中做 RPC 或复杂计算。

面试高频问题#

完整 40 道速查见 MySQL 面试题 40 道

一句话总结#

MySQL 面试主线:InnoDB + 事务隔离 + B+ 树索引 + 行锁;优化先 SQL 和索引,再架构(读写分离、分表、缓存);写操作注意索引维护成本与事务范围。

文章目录

文章目录