MySQL 快速复习版
MySQL 快速复习版
用途:考前速看、面试前回顾、写 SQL 前查模板。
风格:只保留高频概念、常用语法、易错点和面试表达。
范围:MySQL 基础篇、进阶篇、运维篇。
0. 总复习路线
MySQL 可以按三层记:
基础层:库、表、字段、增删改查、多表查询、事务
原理层:存储引擎、索引、执行计划、锁、InnoDB、MVCC
运维层:日志、主从复制、分库分表、MyCat、读写分离
最重要的一条主线:
SQL 怎么写 -> SQL 怎么查得快 -> 并发下怎么保证正确 -> 数据量大后怎么扩展
第一部分:基础 SQL 必会
1. 数据库基本概念
| 名称 | 含义 | 记忆 |
|---|---|---|
| DB | 数据库,存储数据的仓库 | 仓库 |
| DBMS | 数据库管理系统,如 MySQL | 管仓库的软件 |
| SQL | 操作关系型数据库的语言 | 操作命令 |
关系型数据库特点:
数据存储在二维表中。
表和表之间可以通过字段建立关系。
SQL 是操作关系型数据库的统一标准。
MySQL 常用默认信息:
默认端口:3306
超级用户:root
本地连接:mysql -u root -p
远程连接:mysql -h 主机IP -P 3306 -u 用户名 -p
易错点:
-P 大写:端口
-p 小写:密码
SQL 语句通常以 ; 结尾
2. SQL 分类
| 分类 | 作用 | 关键字 |
|---|---|---|
| DDL | 定义库、表、字段 | CREATE、ALTER、DROP |
| DML | 增删改表中数据 | INSERT、UPDATE、DELETE |
| DQL | 查询数据 | SELECT |
| DCL | 用户和权限控制 | CREATE USER、GRANT、REVOKE |
口诀:
DDL 建结构,DML 改数据,DQL 查数据,DCL 管权限。
SQL 注释:
-- 单行注释
# 单行注释
/* 多行注释 */
3. DDL:库和表结构
3.1 数据库操作
SHOW DATABASES;
SELECT DATABASE();
CREATE DATABASE 数据库名;
CREATE DATABASE IF NOT EXISTS 数据库名 DEFAULT CHARSET utf8mb4;
USE 数据库名;
DROP DATABASE IF EXISTS 数据库名;
常用推荐:
CREATE DATABASE IF NOT EXISTS db01 DEFAULT CHARSET utf8mb4;
3.2 表操作
查看表:
SHOW TABLES;
DESC 表名;
SHOW CREATE TABLE 表名;
创建表模板:
CREATE TABLE 表名 (
字段名 字段类型 [约束] [COMMENT '注释'],
字段名 字段类型 [约束] [COMMENT '注释']
) COMMENT '表注释';
示例:
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID',
name VARCHAR(50) NOT NULL COMMENT '姓名',
age INT COMMENT '年龄',
entrydate DATE COMMENT '入职日期'
) COMMENT '员工表';
删除表:
DROP TABLE IF EXISTS 表名;
TRUNCATE TABLE 表名;
区别:
DROP:删除整张表,包括结构。
TRUNCATE:清空数据,保留表结构。
DELETE:删除数据,可带 WHERE。
3.3 字段类型速记
数值:
| 类型 | 常用场景 |
|---|---|
TINYINT |
状态、性别、很小整数 |
INT |
普通整数 ID、年龄等 |
BIGINT |
大整数、雪花 ID |
DECIMAL(m,d) |
金额、精确小数 |
DOUBLE |
普通浮点数 |
字符串:
| 类型 | 区别 |
|---|---|
CHAR(n) |
定长,适合长度固定,如性别、状态码 |
VARCHAR(n) |
变长,最常用,如姓名、手机号 |
TEXT |
大文本 |
日期时间:
| 类型 | 含义 |
|---|---|
DATE |
年月日 |
TIME |
时分秒 |
DATETIME |
年月日时分秒 |
TIMESTAMP |
时间戳,和时区相关 |
常用选择:
姓名:VARCHAR
年龄:INT
金额:DECIMAL
创建时间:DATETIME
状态:TINYINT
3.4 修改表结构
添加字段:
ALTER TABLE 表名 ADD 字段名 类型 [约束] [COMMENT '注释'];
修改字段类型:
ALTER TABLE 表名 MODIFY 字段名 新类型;
修改字段名和类型:
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新类型;
删除字段:
ALTER TABLE 表名 DROP 字段名;
修改表名:
ALTER TABLE 表名 RENAME TO 新表名;
4. DML:增删改数据
4.1 INSERT
指定字段插入:
INSERT INTO 表名(字段1, 字段2) VALUES(值1, 值2);
全部字段插入:
INSERT INTO 表名 VALUES(值1, 值2, 值3);
批量插入:
INSERT INTO 表名(字段1, 字段2)
VALUES
(值1, 值2),
(值1, 值2),
(值1, 值2);
易错点:
字符串和日期要加引号。
字段数量和值数量要一致。
自增主键可以写 NULL 或不写。
4.2 UPDATE
UPDATE 表名 SET 字段1 = 值1, 字段2 = 值2 WHERE 条件;
示例:
UPDATE emp SET age = 20 WHERE id = 1;
危险点:
UPDATE emp SET age = 20;
这会修改整张表。
4.3 DELETE
DELETE FROM 表名 WHERE 条件;
示例:
DELETE FROM emp WHERE id = 1;
危险点:
DELETE FROM emp;
这会删除整张表数据。
5. DQL:查询核心
5.1 SELECT 完整语法
SELECT 字段列表
FROM 表名
WHERE 条件
GROUP BY 分组字段
HAVING 分组后条件
ORDER BY 排序字段 ASC|DESC
LIMIT 起始索引, 查询条数;
执行顺序理解:
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
考试/面试最常问:
WHERE 是分组前过滤。
HAVING 是分组后过滤。
5.2 基础查询
查询所有字段:
SELECT * FROM 表名;
查询指定字段:
SELECT 字段1, 字段2 FROM 表名;
去重:
SELECT DISTINCT 字段 FROM 表名;
别名:
SELECT 字段 AS 别名 FROM 表名;
SELECT 字段 别名 FROM 表名;
5.3 WHERE 条件查询
比较运算:
= 等于
<> 或 != 不等于
> >= < <=
BETWEEN 值1 AND 值2
IN (...)
IS NULL
IS NOT NULL
逻辑运算:
AND
OR
NOT
模糊查询:
LIKE
_ 匹配单个字符
% 匹配任意多个字符
示例:
SELECT * FROM emp WHERE age BETWEEN 18 AND 30;
SELECT * FROM emp WHERE name LIKE '张%';
SELECT * FROM emp WHERE name LIKE '_三';
SELECT * FROM emp WHERE dept_id IN (1, 2, 3);
SELECT * FROM emp WHERE id IS NOT NULL;
5.4 聚合函数
| 函数 | 作用 |
|---|---|
COUNT(*) |
统计行数 |
MAX() |
最大值 |
MIN() |
最小值 |
AVG() |
平均值 |
SUM() |
求和 |
示例:
SELECT COUNT(*) FROM emp;
SELECT AVG(age) FROM emp;
SELECT MAX(salary), MIN(salary) FROM emp;
注意:
聚合函数一般会忽略 NULL。
COUNT(*) 常用于统计总行数。
5.5 GROUP BY 分组
SELECT 分组字段, 聚合函数
FROM 表名
WHERE 分组前条件
GROUP BY 分组字段
HAVING 分组后条件;
示例:
SELECT dept_id, COUNT(*)
FROM emp
GROUP BY dept_id;
带条件:
SELECT dept_id, AVG(salary)
FROM emp
WHERE age > 20
GROUP BY dept_id
HAVING AVG(salary) > 5000;
易错点:
WHERE 后面不能直接写聚合函数条件。
聚合函数条件要写在 HAVING 后面。
5.6 ORDER BY 排序
SELECT * FROM emp ORDER BY age ASC;
SELECT * FROM emp ORDER BY age DESC;
SELECT * FROM emp ORDER BY age DESC, entrydate ASC;
默认升序:ASC。降序:DESC。
5.7 LIMIT 分页
SELECT * FROM 表名 LIMIT 起始索引, 查询条数;
第一页,每页 10 条:
SELECT * FROM emp LIMIT 0, 10;
第 n 页,每页 size 条:
起始索引 = (页码 - 1) * 每页条数
SELECT * FROM emp LIMIT (page - 1) * size, size;
MySQL 中也可以写:
SELECT * FROM emp LIMIT 10;
表示查前 10 条。
6. DCL:用户和权限
创建用户:
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
修改密码:
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';
删除用户:
DROP USER '用户名'@'主机名';
查看权限:
SHOW GRANTS FOR '用户名'@'主机名';
授权:
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';
撤销权限:
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
示例:
GRANT ALL ON db01.* TO 'test'@'%';
记忆:
% 表示任意主机都可以连接。
*.* 表示所有数据库的所有表。
7. 约束
约束用于保证表中数据正确、有效、完整。
| 约束 | 关键字 | 含义 |
|---|---|---|
| 非空约束 | NOT NULL |
不能为 NULL |
| 唯一约束 | UNIQUE |
不能重复 |
| 主键约束 | PRIMARY KEY |
非空且唯一 |
| 默认约束 | DEFAULT |
不传值时用默认值 |
| 检查约束 | CHECK |
限制取值范围 |
| 外键约束 | FOREIGN KEY |
建立表关系 |
常见建表:
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
phone VARCHAR(20) UNIQUE,
age INT CHECK(age > 0 AND age <= 120),
status TINYINT DEFAULT 1
);
外键:
ALTER TABLE 子表
ADD CONSTRAINT 外键名
FOREIGN KEY (子表字段) REFERENCES 主表(主表字段);
删除外键:
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
外键删除/更新行为:
| 行为 | 含义 |
|---|---|
RESTRICT |
有关联数据则不允许删除/更新 |
CASCADE |
主表变,子表跟着变 |
SET NULL |
主表删除/更新后,子表外键设为 NULL |
8. 多表查询
8.1 表关系
| 关系 | 例子 | 实现方式 |
|---|---|---|
| 一对多 | 部门-员工 | 多的一方加外键 |
| 多对多 | 学生-课程 | 建中间表,两个外键 |
| 一对一 | 用户-用户详情 | 任意一方加唯一外键 |
8.2 笛卡尔积
多表查询如果没有连接条件,会产生笛卡尔积:
SELECT * FROM emp, dept;
正确写法要加连接条件:
SELECT *
FROM emp, dept
WHERE emp.dept_id = dept.id;
8.3 内连接
隐式内连接:
SELECT 字段
FROM 表1, 表2
WHERE 表1.字段 = 表2.字段;
显式内连接:
SELECT 字段
FROM 表1
INNER JOIN 表2 ON 表1.字段 = 表2.字段;
内连接含义:
只查询两张表都匹配上的数据。
8.4 外连接
左外连接:
SELECT 字段
FROM 左表
LEFT JOIN 右表 ON 条件;
右外连接:
SELECT 字段
FROM 左表
RIGHT JOIN 右表 ON 条件;
记忆:
LEFT JOIN:保留左表全部数据。
RIGHT JOIN:保留右表全部数据。
8.5 自连接
自己连接自己,常见于员工和领导在同一张表:
SELECT e.name 员工, m.name 领导
FROM emp e
LEFT JOIN emp m ON e.manager_id = m.id;
8.6 UNION
SELECT 字段 FROM 表1
UNION
SELECT 字段 FROM 表2;
SELECT 字段 FROM 表1
UNION ALL
SELECT 字段 FROM 表2;
区别:
UNION:去重。
UNION ALL:不去重,效率更高。
8.7 子查询
子查询就是 SQL 中嵌套 SQL。
标量子查询:返回一个值。
SELECT * FROM emp WHERE salary > (SELECT AVG(salary) FROM emp);
列子查询:返回一列。
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE name LIKE '%研发%');
行子查询:返回一行多列。
SELECT * FROM emp
WHERE (salary, manager_id) = (SELECT salary, manager_id FROM emp WHERE name = '张三');
表子查询:返回多行多列。
SELECT *
FROM (SELECT * FROM emp WHERE entrydate > '2020-01-01') t;
9. 事务
事务:一组操作要么全部成功,要么全部失败。
典型场景:转账。
A 扣钱成功,B 加钱也必须成功。
中间任意一步失败,都要回滚。
事务控制:
START TRANSACTION;
-- SQL操作
COMMIT;
ROLLBACK;
关闭自动提交:
SELECT @@autocommit;
SET @@autocommit = 0;
COMMIT;
ROLLBACK;
ACID:
| 特性 | 含义 |
|---|---|
| 原子性 Atomicity | 要么都成功,要么都失败 |
| 一致性 Consistency | 事务前后数据合法一致 |
| 隔离性 Isolation | 并发事务之间互不干扰 |
| 持久性 Durability | 提交后数据永久保存 |
并发事务问题:
| 问题 | 含义 |
|---|---|
| 脏读 | 读到别人未提交的数据 |
| 不可重复读 | 同一事务两次读同一行结果不同 |
| 幻读 | 同一事务两次查同一范围,行数不同 |
隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不会 | 可能 | 可能 |
| REPEATABLE READ | 不会 | 不会 | 可能/被 InnoDB 处理 |
| SERIALIZABLE | 不会 | 不会 | 不会 |
MySQL InnoDB 默认:
REPEATABLE READ,可重复读。
查看和设置隔离级别:
SELECT @@TRANSACTION_ISOLATION;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
第二部分:进阶原理必会
10. MySQL 体系结构
从上到下:
连接层 -> 服务层 -> 引擎层 -> 存储层
| 层 | 作用 |
|---|---|
| 连接层 | 连接处理、认证、权限校验 |
| 服务层 | SQL 解析、优化、内置函数、执行计划 |
| 引擎层 | 真正负责数据存储和读取 |
| 存储层 | 数据、日志、索引等落盘 |
关键点:
索引是在存储引擎层实现的。
存储引擎是基于表的,不是基于库的。
11. 存储引擎
查看支持的存储引擎:
SHOW ENGINES;
建表指定引擎:
CREATE TABLE 表名 (...) ENGINE = InnoDB;
三大引擎对比:
| 引擎 | 特点 | 场景 |
|---|---|---|
| InnoDB | 支持事务、行锁、外键,默认引擎 | 绝大多数业务表 |
| MyISAM | 不支持事务和外键,只支持表锁,读快 | 读多写少、老系统 |
| Memory | 数据存在内存,速度快,断电丢失 | 临时表、缓存 |
面试回答:InnoDB 和 MyISAM 区别:
1. InnoDB 支持事务,MyISAM 不支持。
2. InnoDB 支持行锁,MyISAM 主要支持表锁。
3. InnoDB 支持外键,MyISAM 不支持。
4. InnoDB 更适合高并发读写,MyISAM 更偏读多写少。
InnoDB 逻辑存储结构:
表空间 -> 段 -> 区 -> 页 -> 行
重点记:
InnoDB 页默认 16KB。
区默认 1MB。
数据最终按行存储。
12. 索引
索引:帮助 MySQL 高效获取数据的有序数据结构。
优点:
提高查询效率,降低 IO 成本。
可降低排序成本。
缺点:
占用磁盘空间。
降低 INSERT、UPDATE、DELETE 效率。
常见索引结构:
| 结构 | 特点 |
|---|---|
| B+Tree | 最常见,适合范围查询和排序 |
| Hash | 等值查询快,不支持范围查询 |
| R-Tree | 空间索引 |
| Full-text | 全文索引 |
默认重点:
平时说 MySQL 索引,一般指 B+Tree 索引。
12.1 为什么用 B+Tree
B+Tree 特点:
1. 非叶子节点只存索引,不存真实数据。
2. 所有数据都在叶子节点。
3. 叶子节点之间形成双向链表。
4. 树高度低,磁盘 IO 少。
5. 适合范围查询和排序。
二叉树/红黑树的问题:
数据量大时树太高,查询磁盘 IO 多。
顺序插入时普通二叉树可能退化成链表。
B-Tree 和 B+Tree 区别:
B-Tree:非叶子节点和叶子节点都可能存数据。
B+Tree:数据都在叶子节点,非叶子节点只做索引。
12.2 索引分类
按字段特性:
| 类型 | 含义 |
|---|---|
| 主键索引 | 主键自动创建 |
| 唯一索引 | 索引列值不能重复 |
| 普通索引 | 普通加速查询 |
| 全文索引 | 文本检索 |
按字段个数:
单列索引:一个字段。
联合索引:多个字段组合。
按 InnoDB 存储方式:
| 类型 | 含义 |
|---|---|
| 聚集索引 | 数据和索引放一起,叶子节点保存整行数据 |
| 二级索引 | 叶子节点保存主键值,需要时再回表 |
重点:
InnoDB 表一定有且只有一个聚集索引。
主键索引通常就是聚集索引。
二级索引查不到完整数据时,需要通过主键再查一次,这叫回表。
12.3 索引语法
创建索引:
CREATE INDEX 索引名 ON 表名(字段名);
创建唯一索引:
CREATE UNIQUE INDEX 索引名 ON 表名(字段名);
联合索引:
CREATE INDEX idx_name_age ON user(name, age);
查看索引:
SHOW INDEX FROM 表名;
删除索引:
DROP INDEX 索引名 ON 表名;
13. 索引使用规则和失效情况
13.1 最左前缀法则
联合索引:
CREATE INDEX idx_user_name_age_status ON user(name, age, status);
可以走索引:
WHERE name = '张三'
WHERE name = '张三' AND age = 20
WHERE name = '张三' AND age = 20 AND status = 1
不一定完整走索引:
WHERE age = 20
WHERE status = 1
WHERE age = 20 AND status = 1
口诀:
联合索引从最左边字段开始连续使用,中间不能断。
13.2 范围查询影响后续字段
WHERE name = '张三' AND age > 18 AND status = 1
可能出现:
name、age 用到索引,status 不一定充分利用。
建议:
联合索引中,等值查询字段尽量放前面,范围查询字段尽量放后面。
13.3 常见索引失效
- 对索引列进行函数或运算:
WHERE SUBSTRING(phone, 10, 2) = '15'
WHERE age + 1 = 20
- 字符串不加引号:
WHERE phone = 13800000000
正确:
WHERE phone = '13800000000'
- LIKE 左模糊:
WHERE name LIKE '%三'
WHERE name LIKE '%三%'
可以利用索引的形式:
WHERE name LIKE '张%'
- OR 条件两边不是都有索引:
WHERE id = 1 OR name = '张三'
如果 name 没索引,可能导致索引失效。
- 数据分布影响:
如果 MySQL 判断全表扫描更快,也可能不走索引。
13.4 覆盖索引
覆盖索引:查询需要的字段在索引里已经都有,不需要回表。
CREATE INDEX idx_user_name_age ON user(name, age);
可能覆盖:
SELECT name, age FROM user WHERE name = '张三';
可能回表:
SELECT * FROM user WHERE name = '张三';
优化建议:
尽量避免 SELECT *。
能查需要字段,就不要查全部字段。
13.5 索引设计原则
适合建索引:
1. 经常出现在 WHERE、ORDER BY、GROUP BY 的字段。
2. 区分度高的字段,如手机号、身份证号。
3. 多表连接的关联字段。
4. 查询频繁的大表字段。
不适合建索引:
1. 数据量很小的表。
2. 经常更新的字段。
3. 区分度低的字段,如性别、状态。
4. 很少用于查询条件的字段。
14. SQL 性能分析
14.1 查看执行频率
SHOW GLOBAL STATUS LIKE 'Com_______';
可以大致看当前数据库是读多还是写多。
14.2 慢查询日志
作用:记录执行慢的 SQL。
查看是否开启:
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
配置示例:
slow_query_log=1
long_query_time=2
意思:执行超过 2 秒的 SQL 记录到慢查询日志。
14.3 EXPLAIN
查看 SQL 执行计划:
EXPLAIN SELECT * FROM user WHERE id = 1;
重点字段:
| 字段 | 看什么 |
|---|---|
id |
查询执行顺序 |
select_type |
查询类型 |
type |
连接访问类型,越靠前越好 |
possible_keys |
可能用到的索引 |
key |
实际用到的索引 |
key_len |
索引使用长度 |
rows |
预估扫描行数 |
Extra |
额外信息 |
type 常见顺序,从好到差:
system > const > eq_ref > ref > range > index > all
重点:
ALL 表示全表扫描,通常需要重点优化。
15. SQL 优化速记
15.1 INSERT 优化
批量插入优于一条条插入。
手动提交事务优于每条自动提交。
主键顺序插入优于乱序插入。
大批量导入可用 LOAD DATA。
批量插入:
INSERT INTO user(name, age) VALUES
('a', 18), ('b', 19), ('c', 20);
15.2 主键优化
建议:
主键长度尽量短。
主键尽量顺序插入。
尽量不要用无序 UUID 当主键。
原因:
InnoDB 聚集索引按主键组织数据,无序主键容易导致页分裂,影响性能。
15.3 ORDER BY 优化
优化方向:
让排序字段尽量走索引。
避免出现 Using filesort。
联合索引顺序要和排序顺序匹配。
15.4 GROUP BY 优化
GROUP BY 字段可以建立索引。
分组前尽量用 WHERE 先过滤,减少参与分组的数据量。
15.5 LIMIT 深分页优化
低效写法:
SELECT * FROM user LIMIT 1000000, 10;
优化思路:
SELECT * FROM user
WHERE id > 1000000
LIMIT 10;
或先查主键,再回表:
SELECT u.*
FROM user u
JOIN (
SELECT id FROM user ORDER BY id LIMIT 1000000, 10
) t ON u.id = t.id;
15.6 COUNT 优化
常见:
SELECT COUNT(*) FROM 表名;
记忆:
COUNT(*) 一般是推荐写法。
COUNT(字段) 不统计 NULL。
15.7 UPDATE 优化
UPDATE 时 WHERE 条件字段尽量有索引。
否则可能从行锁升级成更多范围的锁,影响并发。
示例:
UPDATE user SET name = '张三' WHERE id = 1;
16. 视图、存储过程、函数、触发器
16.1 视图
视图是虚拟表,本身不直接存数据,数据来自查询结果。
创建:
CREATE VIEW 视图名 AS SELECT 语句;
查询:
SELECT * FROM 视图名;
删除:
DROP VIEW 视图名;
作用:
简化复杂 SQL。
隐藏敏感字段。
统一查询口径。
16.2 存储过程
存储过程是一组预先编译好的 SQL 语句集合。
DELIMITER $
CREATE PROCEDURE p1()
BEGIN
SELECT COUNT(*) FROM emp;
END$
DELIMITER ;
CALL p1();
参数类型:
| 类型 | 含义 |
|---|---|
IN |
输入参数 |
OUT |
输出参数 |
INOUT |
既输入又输出 |
16.3 触发器
触发器:在 INSERT、UPDATE、DELETE 前后自动执行。
CREATE TRIGGER 触发器名
BEFORE INSERT ON 表名
FOR EACH ROW
BEGIN
-- SQL
END;
常用场景:
日志记录、数据校验、同步冗余字段。
17. 锁
锁的目的:解决并发访问下的数据安全问题。
按粒度:
| 锁 | 范围 | 特点 |
|---|---|---|
| 全局锁 | 整个数据库实例 | 备份时常用 |
| 表级锁 | 整张表 | 开销小,冲突大 |
| 行级锁 | 某些行 | 并发高,开销大 |
17.1 全局锁
加锁:
FLUSH TABLES WITH READ LOCK;
释放:
UNLOCK TABLES;
作用:
让整个库处于只读状态,常用于一致性备份。
17.2 表级锁
表锁:
LOCK TABLES 表名 READ;
LOCK TABLES 表名 WRITE;
UNLOCK TABLES;
元数据锁 MDL:
对表进行增删改查时自动加 MDL 读锁。
修改表结构时自动加 MDL 写锁。
作用是防止查询过程中表结构被别人改掉。
意向锁:
用于协调行锁和表锁,表示表中某些行将要被加锁。
17.3 行级锁
InnoDB 支持行级锁。
常见:
| 锁 | 含义 |
|---|---|
| 行锁 Record Lock | 锁一行记录 |
| 间隙锁 Gap Lock | 锁记录之间的间隙 |
| 临键锁 Next-Key Lock | 行锁 + 间隙锁 |
重点:
行锁是加在索引上的。
如果查询条件没有走索引,可能扫描更多数据,锁范围变大。
18. InnoDB 与 MVCC
18.1 InnoDB 内存结构
常见组成:
Buffer Pool:缓存数据页和索引页。
Change Buffer:缓存普通二级索引变更。
Adaptive Hash Index:自适应哈希索引。
Log Buffer:redo log 缓冲。
最重要:
Buffer Pool 是 InnoDB 性能的核心缓存区域。
18.2 redo log 与 undo log
redo log:
保证事务持久性。
记录“做了什么修改”。
崩溃恢复时使用。
undo log:
保证事务原子性。
记录“修改前的数据”。
回滚和 MVCC 都依赖 undo log。
对比:
| 日志 | 作用 |
|---|---|
| redo log | 崩溃恢复,保证已提交事务不丢 |
| undo log | 回滚和多版本并发控制 |
18.3 MVCC
MVCC:多版本并发控制。
作用:
在不加锁或少加锁的情况下,实现并发读写,提高性能。
依赖:
隐藏字段 + undo log 版本链 + ReadView
InnoDB 隐藏字段:
| 字段 | 作用 |
|---|---|
DB_TRX_ID |
最近修改该记录的事务 ID |
DB_ROLL_PTR |
指向 undo log 中上一个版本 |
DB_ROW_ID |
隐藏主键,没有主键时生成 |
ReadView:
用于判断当前事务能看到哪个版本的数据。
RC 和 RR 的区别:
READ COMMITTED:每次 SELECT 都生成新的 ReadView。
REPEATABLE READ:事务第一次 SELECT 生成 ReadView,后续复用。
第三部分:运维架构必会
19. MySQL 常用工具
连接:
mysql -h 127.0.0.1 -P 3306 -u root -p
查看服务状态:
mysqladmin -u root -p version
mysqladmin -u root -p variables
查看 binlog:
mysqlbinlog binlog.000001
备份:
mysqldump -u root -p 数据库名 > db.sql
mysqldump -u root -p --all-databases > all.sql
恢复:
mysql -u root -p 数据库名 < db.sql
MySQL 内执行脚本:
SOURCE /path/db.sql;
20. 日志
| 日志 | 作用 |
|---|---|
| 错误日志 error log | 记录启动、停止、严重错误 |
| 二进制日志 binlog | 记录 DDL 和 DML,不记录 SELECT |
| 查询日志 general log | 记录客户端所有操作,默认关闭 |
| 慢查询日志 slow query log | 记录执行慢的 SQL |
20.1 error log
作用:
数据库启动失败、运行异常,优先看错误日志。
查看位置:
SHOW VARIABLES LIKE '%log_error%';
20.2 binlog
作用:
1. 数据恢复。
2. 主从复制。
查看是否开启:
SHOW VARIABLES LIKE '%log_bin%';
格式:
| 格式 | 含义 |
|---|---|
| STATEMENT | 记录 SQL 语句 |
| ROW | 记录每一行数据变更,默认常用 |
| MIXED | 混合模式 |
清理 binlog:
RESET MASTER;
PURGE MASTER LOGS TO 'binlog.000010';
PURGE MASTER LOGS BEFORE '2024-01-01 00:00:00';
20.3 慢查询日志
配置:
slow_query_log=1
long_query_time=2
log_queries_not_using_indexes=1
记忆:
慢查询日志用于定位慢 SQL,是 SQL 优化的入口。
21. 主从复制
主从复制:主库数据变更通过 binlog 同步到从库。
作用:
1. 主库故障时,从库可顶上。
2. 实现读写分离,减轻主库压力。
3. 可以在从库备份,减少对主库影响。
原理三步:
1. Master 提交事务时,把变更写入 binlog。
2. Slave IO 线程读取 Master binlog,写入 relay log。
3. Slave SQL 线程重放 relay log,使从库数据追上主库。
主库关键配置:
server-id=1
read-only=0
创建复制用户:
CREATE USER 'itcast'@'%' IDENTIFIED WITH mysql_native_password BY 'Root@123456';
GRANT REPLICATION SLAVE ON *.* TO 'itcast'@'%';
SHOW MASTER STATUS;
从库关键配置:
server-id=2
read-only=1
从库设置主库信息,MySQL 8.0.23 后:
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='主库IP',
SOURCE_USER='复制用户',
SOURCE_PASSWORD='密码',
SOURCE_LOG_FILE='binlog文件名',
SOURCE_LOG_POS=位置;
启动复制:
START REPLICA;
SHOW REPLICA STATUS;
旧版本写法:
START SLAVE;
SHOW SLAVE STATUS;
22. 分库分表
为什么需要分库分表:
单库单表数据量过大后,会出现磁盘 IO、网络 IO、CPU 瓶颈。
分库分表的核心是:把数据分散存储,降低单库单表压力。
拆分方式:
| 方式 | 依据 | 特点 |
|---|---|---|
| 垂直分库 | 按业务拆表到不同库 | 每个库表不同,数据不同,并集为全量 |
| 垂直分表 | 按字段拆成不同表 | 表结构不同,通过主键关联 |
| 水平分库 | 按字段规则把数据拆到不同库 | 库结构相同,数据不同,并集为全量 |
| 水平分表 | 按字段规则把一张表拆成多张表 | 表结构相同,数据不同,并集为全量 |
例子:
垂直分库:用户库、订单库、商品库分开。
垂直分表:商品基本信息和商品详情分开。
水平分库:订单按用户ID取模分到不同库。
水平分表:订单表按月份或ID范围拆成多张表。
实现技术:
ShardingJDBC:应用层分库分表组件。
MyCat:数据库中间件,对应用伪装成一个 MySQL。
23. MyCat 与读写分离
MyCat:数据库中间件,客户端连接 MyCat,MyCat 再根据规则转发 SQL 到真实数据库。
核心概念:
| 概念 | 含义 |
|---|---|
| schema | 逻辑库 |
| table | 逻辑表 |
| dataNode | 数据节点 |
| dataHost | 数据主机 |
| rule | 分片规则 |
常见端口:
8066:MyCat 数据访问端口。
9066:MyCat 管理端口。
读写分离:
写操作走主库。
读操作走从库。
目的:降低主库压力,提高系统整体读能力。
一主一从:
Master 负责写。
Slave 负责读。
Master 通过 binlog 同步数据到 Slave。
双主双从:
两个主库互为主从,下面再挂从库。
可以提高可用性,但配置更复杂。
第四部分:高频易错点和面试表达
24. 必背执行顺序
SQL 写法顺序:
SELECT ...
FROM ...
WHERE ...
GROUP BY ...
HAVING ...
ORDER BY ...
LIMIT ...;
SQL 执行理解顺序:
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
25. WHERE 和 HAVING
| 对比 | WHERE | HAVING |
|---|---|---|
| 执行时机 | 分组前 | 分组后 |
| 能否使用聚合函数 | 一般不能 | 可以 |
| 作用对象 | 原始行数据 | 分组结果 |
示例:
SELECT dept_id, COUNT(*)
FROM emp
WHERE age > 18
GROUP BY dept_id
HAVING COUNT(*) > 5;
26. DELETE、TRUNCATE、DROP
| 命令 | 作用 | 是否保留表结构 |
|---|---|---|
| DELETE | 删除部分或全部数据 | 保留 |
| TRUNCATE | 清空整表数据 | 保留 |
| DROP | 删除整张表 | 不保留 |
27. 内连接、外连接
内连接:只要两边匹配上的数据。
左外连接:左表全部保留,右表匹配不上补 NULL。
右外连接:右表全部保留,左表匹配不上补 NULL。
28. 索引口诀
大表高频查,考虑建索引。
where、order by、group by、join 字段优先。
联合索引看最左,范围之后要小心。
函数运算会失效,字符串记得加引号。
LIKE 百分号别放左,SELECT 星号少使用。
索引不是越多越好,增删改也要付成本。
29. 事务和锁的关系
事务解决一组操作的一致性问题。
锁解决并发访问时的数据安全问题。
事务隔离性通常依赖锁和 MVCC 实现。
30. InnoDB 面试表达
可以这样说:
InnoDB 是 MySQL 默认存储引擎,支持事务、行级锁和外键,适合高并发业务场景。
它使用 B+Tree 组织索引,主键索引是聚集索引,叶子节点保存整行数据;二级索引叶子节点保存主键值,必要时需要回表。
InnoDB 通过 redo log 保证持久性,通过 undo log 支持回滚和 MVCC,通过 MVCC 在读写并发场景下提高性能。
31. 主从复制面试表达
可以这样说:
MySQL 主从复制基于 binlog。主库提交事务后把 DDL 和 DML 变更写入 binlog;从库 IO 线程读取主库 binlog 写入 relay log;从库 SQL 线程重放 relay log,从而保持主从数据同步。主从复制常用于读写分离、故障切换和备份。
32. 分库分表面试表达
可以这样说:
当单库单表数据量过大,出现 IO、CPU 或连接压力时,可以通过分库分表把数据分散存储。垂直拆分按业务或字段拆,水平拆分按某个字段规则拆。垂直拆分解决业务耦合和宽表问题,水平拆分主要解决单表数据量过大的问题。
33. 最后总口诀
基础 SQL:DDL建表,DML改数,DQL查询,DCL管权。
查询顺序:from where group having select order limit。
多表查询:先想表关系,再写连接条件,避免笛卡尔积。
事务重点:ACID、并发问题、隔离级别。
索引重点:B+Tree、最左前缀、回表、覆盖索引、索引失效。
优化重点:慢查询日志 + EXPLAIN + 合理建索引。
InnoDB重点:事务、行锁、外键、redo、undo、MVCC。
运维重点:binlog、主从复制、读写分离、分库分表。