MySQL 2026长效实战教程:从零基础入门到大厂生产架构落地
摘要
本文是一份面向2026年技术生态的 MySQL 8.0 LTS 全栈实战教程,覆盖从零基础入门到大厂生产架构落地的完整学习路径。全文共分四大模块:模块一系统讲解SQL基础语法、三大范式、数据类型选型、DDL/DML/DQL及SQL反模式避坑;模块二深入剖析MySQL 8.0核心新特性,包括窗口函数、CTE、JSON操作、Hash Join、Instant DDL、自增值持久化等;模块三聚焦生产环境必备技能,涵盖B+树原理、索引优化体系(ICP/MRR/直方图/EXPLAIN ANALYZE)、事务隔离级别、间隙锁与死锁实战、SKIP LOCKED高并发方案、Binlog体系及分区表设计;模块四提供运维架构方案(主从复制、分库分表、压测监控)及40+大厂高频面试题库。
版本说明:本文基于 MySQL 8.0 LTS(2026年生产主流),兼容5.7旧项目维护。所有示例均使用统一的电商数据库(用户、订单、商品、分类、评论)。
🧩 快速导航:
[基础]语法/DDL/范式 → 模块1[新特性]窗口函数/直方图/Instant DDL → 模块2[调优]执行计划/索引失效/SQL改写 → 模块3[面试]40+高频场景题 → 模块4👨🎓 阅读分层指引:
- 🟢 学生/零基础:模块1(基础语法+范式+数据类型+DDL/DML/DQL)
- 🟡 初级后端:模块1 + 模块2(8.0新特性)+ 模块3(索引+事务+锁+规范)
- 🔴 面试/进阶:全部四个模块,重点掌握模块3底层原理和模块4面试题库
📌 全文约定:
- ✅ 生产推荐:2026年项目标准写法
- ⚠️ 坑点:新手极易出错、线上故障场景
- 📌 面试考点:校招/社招高频考题
- 🔧 8.0专属:5.7不兼容特性
文章目录
模块0:前置导读
0.1 MySQL版本选型
| 版本 | 状态 | 推荐度 |
|---|---|---|
| MySQL 8.0 LTS | 2026生产主流 | ⭐⭐⭐⭐⭐ |
| MySQL 5.7 | 已停止维护,仅遗留项目 | ⭐⭐ |
| MyISAM引擎 | 已淘汰,新项目禁用 | ❌ |
核心变化:
- 字符集默认
utf8mb4(支持emoji) - 密码插件
caching_sha2_password - 原子DDL、隐藏索引、窗口函数、Hash Join、Instant DDL
- 自增值持久化(存于数据字典表
mysql.auto_increment_counter,重启不丢失) - 废弃
password()函数、utf8(3字节)、老式分区语法
0.2 全文约定
- ✅ 生产推荐:2026年项目标准写法
- ⚠️ 坑点:新手极易出错、线上故障场景
- 📌 面试考点:校招/社招高频考题
- 🔧 8.0专属:5.7不兼容特性
0.3 阅读分层指引
- 学生/零基础:模块1(基础语法+范式+数据类型+DDL/DML/DQL)
- 初级后端:模块1 + 模块2(8.0新特性)+ 模块3(索引+事务+锁+规范)
- 面试/进阶:全部四个模块,重点掌握模块3底层原理和模块4面试题库
模块1:MySQL零基础核心
1.1 SQL概述与四大语言
-- DDL: 数据定义语言
CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- DML: 数据操纵语言
INSERT INTO user(name) VALUES('张三');
-- DQL: 数据查询语言
SELECT * FROM user WHERE id = 1;
-- DCL: 数据控制语言
GRANT SELECT ON shop.* TO 'reader'@'%';
1.2 三大范式与反范式设计
- 第一范式:每列不可再分
- 第二范式:非主键列完全依赖于主键
- 第三范式:非主键列不传递依赖
- ✅ 反范式(生产常用):在订单表中冗余商品名称、价格,避免每次查询都JOIN商品表,提升读性能。
CREATE TABLE `order` ( id BIGINT UNSIGNED AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL DEFAULT '', -- 冗余字段 product_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, ... );
1.3 数据类型(2026标准)
| 类型 | 推荐用法 | 避坑 |
|---|---|---|
| INT / BIGINT | 主键用 BIGINT UNSIGNED AUTO_INCREMENT | 不要用INT做主键,可能溢出 |
| DECIMAL(M,D) | 金额专用,如 DECIMAL(10,2) | 禁止FLOAT/DOUBLE |
| VARCHAR(N) | 变长字符串,N为字符数 | 8.0中VARCHAR(255)以内一个字符占1~4字节 |
| CHAR(N) | 定长字符串,适合固定长度编码 | 如手机号 CHAR(11) |
| DATETIME | 推荐存储时间,范围’1000-01-01’到’9999-12-31’ | 避免TIMESTAMP(2038年问题) |
| JSON | 8.0原生支持,适合存储扩展属性 | 代替多字段冗余设计 |
| TEXT/BLOB | 大文本/二进制,建议单独分表存储 | 不在主表中存放 |
⚠️ 坑点:禁止使用TIMESTAMP,因为2038年溢出;禁止使用utf8(3字节),必须用utf8mb4。
1.4 库与表DDL操作
-- 建库
CREATE DATABASE shop
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- 建表(强制指定引擎和字符集)
CREATE TABLE user (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '用户ID',
name VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用户名',
phone CHAR(11) NOT NULL DEFAULT '' COMMENT '手机号',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
⚠️ 坑点:线上禁止无WHERE的UPDATE/DELETE,务必先SELECT确认范围。
1.5 五大约束(含生产建议)
| 约束 | 说明 | 生产建议 |
|---|---|---|
| PRIMARY KEY | 主键,唯一标识一行 | 推荐自增BIGINT UNSIGNED |
| FOREIGN KEY | 物理外键 | ⚠️ 分布式/微服务架构坚决禁用;单库小型后台管理(如内部OA)可根据团队规范酌情使用。现代架构推荐逻辑外键(业务代码保证) |
| UNIQUE | 唯一约束 | 用于手机号、身份证等业务唯一字段 |
| NOT NULL | 非空 | 尽量设置NOT NULL + DEFAULT值,减少索引开销 |
| CHECK | 检查约束 | 8.0完整支持,可用于简单校验(如年龄>0) |
1.6 DML增删改查
-- 批量插入(推荐)
INSERT INTO user(name, phone) VALUES
('张三','13800138000'),
('李四','13900139000');
-- 存在则更新(ON DUPLICATE KEY UPDATE)
INSERT INTO user(id, name) VALUES(1,'王五')
ON DUPLICATE KEY UPDATE name=VALUES(name);
-- 安全删除(分批避免长事务)
DELETE FROM order_log WHERE id < 1000 LIMIT 500;
-- 循环执行直到影响行数为0
-- TRUNCATE vs DELETE
-- DELETE可回滚,TRUNCATE不可回滚(重置自增ID),注意8.0下TRUNCATE受外键约束限制
1.7 DQL查询进阶
-- COUNT(*) vs COUNT(1) vs COUNT(字段)
-- COUNT(*) 统计行数最快,包含NULL;COUNT(字段)忽略NULL
-- 大分页优化(延迟关联 / 覆盖索引回表)
-- 坏写法:SELECT * FROM order LIMIT 100000, 20;
-- 好写法(通用无漏洞,支持任意跳页):
SELECT * FROM order t1
JOIN (SELECT id FROM order ORDER BY id LIMIT 100000, 20) t2
ON t1.id = t2.id;
-- 游标分页(仅适用于上一页/下一页,不适用于跳页,否则会漏行)
SELECT * FROM order WHERE id > 100000 ORDER BY id LIMIT 20;
-- GROUP BY 严格模式(8.0默认ONLY_FULL_GROUP_BY)
-- 解决办法:SELECT的列必须在GROUP BY中或使用聚合函数
1.8 多表连接与子查询
-- JOIN优化:小表驱动大表,关联字段建索引
SELECT u.name, o.order_no
FROM user u
INNER JOIN `order` o ON u.id = o.user_id
WHERE u.create_time > '2026-01-01';
-- 子查询嵌套不超过3层,推荐用CTE替代
1.9 常见SQL反模式自查表
| 错误写法(❌) | 正确写法(✅) | 原因 |
|---|---|---|
SELECT * FROM order WHERE name LIKE '%张%' | 前端改为输入完整前缀或使用全文索引 | 前缀模糊查询导致索引失效 |
WHERE status != 1 | 改写为 WHERE status IN (0,2,3) 或利用枚举 | !=是否走索引由优化器基于数据分布动态决定,非绝对失效 |
ORDER BY RAND() LIMIT 1 | 使用程序随机生成ID范围,或使用临时表 | ORDER BY RAND() 会导致全表扫描和排序 |
WHERE DATE(create_time) = '2026-01-01' | WHERE create_time >= '2026-01-01' AND create_time < '2026-01-02' | 对索引列使用函数会失效 |
HAVING COUNT(*) > 1 且 GROUP BY 列少 | 先用子查询过滤,再用 HAVING | HAVING 会在分组后再过滤,效率低于 WHERE |
模块2:MySQL 8.0现代核心特性
2.1 CTE公用表表达式(WITH)
-- 查询每个用户的订单总金额
WITH user_order_total AS (
SELECT user_id, SUM(amount) total_amount
FROM `order`
GROUP BY user_id
)
SELECT u.name, uot.total_amount
FROM user u
LEFT JOIN user_order_total uot ON u.id = uot.user_id;
2.2 窗口函数(面试必考)
-- 按部门工资排名
SELECT
emp_name,
dept_id,
salary,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rank_in_dept
FROM employee;
-- 计算月度销售额环比
SELECT
month,
revenue,
LAG(revenue,1) OVER (ORDER BY month) AS prev_month_revenue,
(revenue - LAG(revenue,1) OVER (ORDER BY month)) / LAG(revenue,1) OVER (ORDER BY month) AS growth_rate
FROM monthly_sales;
2.3 JSON完整操作
-- 插入JSON
INSERT INTO product_extra(product_id, attrs)
VALUES(1, JSON_OBJECT('color','red','size','M'));
-- 提取JSON字段
SELECT JSON_EXTRACT(attrs, '$.color') FROM product_extra;
-- JSON_TABLE:将JSON转为关系表
SELECT p.*, jt.color, jt.size
FROM product p,
JSON_TABLE(p.attrs, '$' COLUMNS(
color VARCHAR(10) PATH '$.color',
size VARCHAR(10) PATH '$.size'
)) AS jt;
2.4 索引新特性
-- 隐藏索引(测试删除效果而不真正删除)
ALTER TABLE user ALTER INDEX idx_phone INVISIBLE;
-- 观察一段时间后若不影响性能,再DROP
-- 降序索引(8.0支持)
CREATE INDEX idx_create_desc ON `order`(create_time DESC);
-- 函数索引(解决WHERE DATE(create_time)=...无法走索引)
CREATE INDEX idx_create_date ON `order`((DATE(create_time)));
2.5 安全与认证
-- 创建用户并设置密码过期时间
CREATE USER 'app_user'@'%' IDENTIFIED WITH caching_sha2_password BY 'StrongPass123!'
PASSWORD EXPIRE INTERVAL 90 DAY;
-- 密码强度策略
SET GLOBAL validate_password.policy = STRONG;
2.6 Hash Join(8.0.18+)
- 作用:当关联字段没有索引时,自动使用Hash Join替代传统的Block Nested-Loop Join,大幅提升性能。
- 触发条件:
join_buffer_size足够大,且optimizer_switch中hash_join=on。 - 示例(模拟无索引关联):
-- 旧版(5.7)会使用BNL,速度慢
-- 8.0会自动使用Hash Join
SELECT /*+ HASH_JOIN(o,oi) */ o.order_no, oi.product_name
FROM `order` o
JOIN order_item oi ON o.id = oi.order_id
WHERE o.create_time > '2026-01-01';
- 生产建议:仍应优先为关联字段建索引,Hash Join只是兜底方案。
2.7 Instant DDL(8.0.12+)
- 作用:某些ALTER操作可以瞬间完成,不阻塞DML。
- 支持的操作:
ADD COLUMN(MySQL 8.0.29+ 已支持在表的任意位置(FIRST/AFTER) 添加列,无需重建表;8.0.12~8.0.28仅支持末尾追加)DROP COLUMN(8.0.23+)MODIFY COLUMN(仅修改默认值)
- 查看是否instant:
ALTER TABLE user ADD COLUMN age TINYINT UNSIGNED DEFAULT 0, ALGORITHM=INSTANT;
2.8 8.0其他高频新特性
2.8.1 持久化全局变量(SET PERSIST)
-- 无需修改配置文件、无需重启
SET PERSIST long_query_time = 1;
2.8.2 角色管理(Role)
CREATE ROLE dev_read;
GRANT SELECT ON shop.* TO dev_read;
GRANT dev_read TO 'zhang'@'%';
2.8.3 表达式默认值
create_time DATETIME DEFAULT NOW()
2.8.4 自增ID不连续问题(面试必考)
- 现象:删除数据、事务回滚后,自增ID不会回填,导致ID断层。
- 5.7致命缺陷:自增值存在内存,重启数据库会重置自增值,导致复现已删除ID、主键冲突。
- ✅ 8.0核心优化:自增值持久化到
mysql.auto_increment_counter系统表(数据字典),重启不丢失、不回溯,彻底解决5.7重启ID错乱问题。(注意:并非存于redo log,redo log仅负责物理事务恢复) - 生产结论:自增ID不保证连续,只保证单调递增、唯一。业务绝对不能依赖ID连续(流水号、排序、统计)。
2.8.5 8.0彻底删除的过时特性
- 废弃
myisam为默认引擎 - 废弃
utf8(3字节),强制utf8mb4 - 废弃
password()函数,只能用caching_sha2_password - 废弃老式分区语法
ONLY_FULL_GROUP_BY8.0强制开启(5.7可关闭)
模块3:生产环境必备
3.1 存储引擎:InnoDB深度解析
- 聚簇索引:主键即数据,二级索引叶子节点存主键值
- 事务ACID:通过redo log(持久性)、undo log(原子性/隔离性)实现
- 隔离级别:默认RR(可重复读),通过MVCC + Next-Key Lock解决幻读
- 锁机制:行锁(Record Lock)、间隙锁(Gap Lock)、临键锁(Next-Key Lock)
- 死锁:两个事务互相等待对方释放锁,通过innodb_deadlock_detect自动检测并回滚其中一个
3.2 索引完整体系
3.2.1 B+树优势
- 非叶子节点不存数据,可存放更多索引,降低树高
- 叶子节点形成双向链表,支持范围查询高效
- 相比哈希索引,支持排序和范围查询
3.2.2 联合索引最左匹配原则
CREATE INDEX idx_a_b_c ON t(a,b,c);
-- 走索引:WHERE a=1 AND b=2
-- 走索引:WHERE a=1 AND c=3 (仅用到a列)
-- 不走索引:WHERE b=2 (没有a)
3.2.3 索引失效10大场景(精选)
LIKE '%xxx'前缀模糊- 对索引列使用函数
WHERE DATE(create_time)=... - 隐式类型转换
WHERE phone=13800138000(phone是varchar) - OR条件中有一个非索引列
!=或<>操作符(是否失效由优化器基于数据分布(选择性)动态决定,非绝对失效)IS NOT NULL(同样取决于数据分布,若匹配数据量极少则走索引)- 联合索引跳过中间列
- 表数据量很小(优化器认为全表扫描更快)
- 索引列参与运算
WHERE age+1=20 - 使用NOT IN、NOT EXISTS(可能失效)
📌 面试口诀:函数、运算、隐转、前模糊、or断索引、不满足最左。注意后三条需结合数据分布判断。
3.2.4 索引下推(ICP)
- 定义:在二级索引遍历时,提前过滤不符合条件的行,减少回表次数。
- 启用:
SET optimizer_switch = 'index_condition_pushdown=on';(默认开启) - 示例:
-- 联合索引 (zipcode, lastname, firstname)
SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%etrunia%';
-- 5.7:先通过zipcode找到所有记录,再回表过滤lastname
-- 8.0:在索引层面就判断lastname是否匹配,不匹配的不回表
- EXPLAIN显示:
Extra列出现Using index condition
3.2.5 MRR(Multi-Range Read)
- 作用:将随机I/O转化为顺序I/O,适用于二级索引范围查询后需要回表的情况。
- 原理:先将满足条件的二级索引的主键值放入缓冲区排序,然后按主键顺序回表读取数据。
- 启用:
SET optimizer_switch = 'mrr=on,mrr_cost_based=off'; - 适用场景:
WHERE key_part1 > 100 AND key_part1 < 200这种范围查询,且需要回表获取整行数据。
3.2.6 覆盖索引最佳实践
避免回表 = 极致性能,Extra显示Using index。
3.2.7 索引选择性概念
- 选择性 = 去重值数量 / 总行数。选择性太低(如性别、状态)不适合建索引,MySQL优化器会直接全表扫描。
- 建索引黄金标准:选择性 > 20% 且查询频繁的列。
3.2.8 直方图(Histograms)—— 8.0调优利器
- 作用:当索引列数据分布极度不均(如90%为0,10%为1)时,优化器可能选错索引。直方图为优化器提供列值分布统计信息,帮助其精准估算行数。
- 操作:
-- 更新直方图(默认采样100)
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, category_id;
-- 查看直方图元数据
SELECT * FROM information_schema.column_statistics WHERE table_name = 'orders';
-- 删除直方图
ANALYZE TABLE orders DROP HISTOGRAM ON status;
3.2.9 EXPLAIN执行计划解读(含EXPLAIN ANALYZE)
| 字段 | 含义 | 优秀值 |
|---|---|---|
| type | 访问类型 | const > eq_ref > ref > range > index > ALL |
| key | 实际使用的索引 | 不为NULL |
| rows | 预估扫描行数 | 越小越好 |
| Extra | 额外信息 | Using index(覆盖索引)最优 |
🔧 8.0专属 EXPLAIN ANALYZE(8.0.18+):
- 作用:实际执行SQL,输出每一步的真实耗时(毫秒)和循环次数,是排查"预估rows很小但实际执行极慢"的终极武器。
- 示例:
EXPLAIN ANALYZE SELECT * FROM order WHERE create_time > '2026-06-01'\G
-- 输出包含:实际执行时间 (actual time)、实际循环次数 (loops)、实际扫描行数
3.3 大厂建表规范(可直接落地)
CREATE TABLE `order` (
`id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT '订单ID',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
`order_no` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '订单号',
`total_amount` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额',
`status` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态:0待支付1已支付2已取消',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
规范要点:
- 必须有主键、注释、非空+默认值
- 避免TEXT/BLOB在主表,拆分附属表
- 时间统一DATETIME,不使用字符串
- 金额用DECIMAL,不用FLOAT/DOUBLE
- 禁止存储图片/文件,只存URL
3.4 SQL性能优化清单
- ✅ 禁止SELECT *,只查询需要的字段
- ✅ 模糊查询:
LIKE 'xxx%'可走索引,LIKE '%xxx'不可 - ✅ 多表JOIN:不超过3~5张,关联字段建索引
- ✅ 大事务拆分:单个事务执行时间控制在3秒内
- ✅ IN子查询:集合元素不宜超过1000,否则拆分为多个查询或用临时表
- ✅ OR条件:改为UNION ALL(如果两边都能走索引)
3.5 事务与锁:生产实战避坑
3.5.1 幻读的完整真相(面试90%人答错)
- 读幻读:普通快照读(MVCC)不会幻读
- 写幻读:当前读(UPDATE/DELETE/SELECT … FOR UPDATE)依然会幻读
- InnoDB RR解决的是「写幻读」(通过Next-Key Lock)
3.5.2 间隙锁触发条件(生产高频死锁根源)
只有 InnoDB RR 级别才会产生间隙锁。触发场景:
- 条件是范围查询(> < BETWEEN)
- 唯一索引不命中任何数据
- 普通索引范围更新
✅ 避坑方案(生产必用)
- 尽量使用精准等值查询更新
- 小事务、短锁、后置写操作
3.5.3 死锁必现场景 + 根治方案
必现死锁:两个事务加锁顺序相反
- T1:锁A → 等B
- T2:锁B → 等A
根治:所有业务统一加锁顺序(ID从小到大)
3.5.4 高并发神器:SKIP LOCKED 与 NOWAIT(8.0)
SKIP LOCKED:跳过已被其他事务锁定的行,永不阻塞、永不超时。适用于任务队列 / 秒杀扣减场景。NOWAIT:若遇到锁则立即报错返回,不等待。- 示例(任务队列取数):
START TRANSACTION;
-- 跳过已被其他消费者锁定的任务,取最新10个待处理任务
SELECT id, payload FROM task_queue
WHERE status = 0
ORDER BY priority DESC, id
LIMIT 10
FOR UPDATE SKIP LOCKED;
-- 更新状态后提交
UPDATE task_queue SET status = 1 WHERE id IN (...);
COMMIT;
3.5.5 事务实战:电商下单完整流程
-- 模拟下单流程(需在事务中执行)
START TRANSACTION;
-- 1. 扣减库存(使用行锁,防止超卖)
UPDATE inventory SET stock = stock - 1
WHERE product_id = 100 AND stock > 0;
-- 2. 检查扣减结果(影响行数应为1)
IF ROW_COUNT() = 0 THEN
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '库存不足';
END IF;
-- 3. 创建订单
INSERT INTO `order`(user_id, order_no, total_amount, status)
VALUES(1, 'ORD20260713001', 99.99, 0);
-- 4. 插入订单明细
INSERT INTO order_item(order_id, product_id, quantity, price)
VALUES(LAST_INSERT_ID(), 100, 1, 99.99);
COMMIT;
知识点延伸:
- 为什么扣库存要
WHERE stock > 0?—— 避免负库存,结合行锁保证原子性。 - 为什么不先INSERT订单再扣库存?—— 防止订单创建成功但库存扣减失败,导致数据不一致。
- 高并发下如何优化?—— 使用Redis预扣库存 + 异步队列同步DB,或使用
SELECT ... FOR UPDATE SKIP LOCKED实现无阻塞队列。
3.6 视图、存储过程、触发器
- 视图:封装复杂查询,简化权限管理
- 存储过程:适合批处理,但调试困难,生产慎用
- 触发器:容易造成隐式逻辑,不建议在高并发中使用
3.7 DCL权限管理
-- 最小权限原则
CREATE USER 'app_read'@'192.168.1.%' IDENTIFIED BY 'password';
GRANT SELECT ON shop.* TO 'app_read'@'192.168.1.%';
REVOKE DELETE ON shop.* FROM 'app_read'@'192.168.1.%';
FLUSH PRIVILEGES;
3.8 Binlog核心体系(2026后端必考)
3.8.1 Binlog三种格式
- STATEMENT:记录SQL语句,节省空间,数据不一致风险,淘汰
- ROW:记录行变更,精准、无坑、8.0默认,生产唯一推荐
- MIXED:混合模式,已淘汰
3.8.2 Binlog核心用途
- 主从复制
- 数据恢复
- 增量同步(Canal、DataX)
- 审计日志
3.8.3 8.0 Binlog新特性
- Binlog 持久化 crash-safe(崩溃不丢日志)
- 支持压缩、事务分组
3.9 分区表(日志型大表首选)
适用场景:日志表、订单流水、统计数据(写多读少,按时间清理归档)
注意:高并发写入下存在全局锁竞争,业务高频交易表(如订单主表)仍推荐应用层分库分表。
常用分区:
- 按时间范围分区(RANGE)
- 按ID哈希分区(HASH)
优势:
- 清理历史数据 DROP PARTITION 秒删(不用delete)
- 查询只扫描对应分区,性能暴涨
示例:
CREATE TABLE order_log (
id BIGINT UNSIGNED AUTO_INCREMENT,
order_id BIGINT UNSIGNED NOT NULL,
log_time DATETIME NOT NULL,
content TEXT,
PRIMARY KEY (id, log_time)
) ENGINE=InnoDB
PARTITION BY RANGE (TO_DAYS(log_time)) (
PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')),
PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01')),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
3.10 SQL执行顺序终极完整版(面试天花板)
FROM → JOIN → WHERE → GROUP BY → 聚合计算 → HAVING →
WITH/窗口函数 → SELECT → DISTINCT → ORDER BY → LIMIT
📌 注意:窗口函数在HAVING之后、SELECT之前执行,CTE在FROM之前解析。
模块4:运维·架构·面试
4.1 慢查询与日志
-- 开启慢查询日志(生产建议长期开启)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2; -- 超过2秒记录
SET GLOBAL log_queries_not_using_indexes = ON;
4.2 备份恢复
# 逻辑备份(mysqldump)
mysqldump -u root -p --single-transaction --routines --triggers shop > shop_backup.sql
# 物理备份(xtrabackup,适合大数据量)
xtrabackup --backup --target-dir=/backup/mysql/
4.3 主从复制与读写分离
-- 从库配置(my.cnf)
server-id=2
relay-log=mysql-relay-bin
read-only=1
-- 主库创建复制用户
CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
-- 从库执行
CHANGE MASTER TO MASTER_HOST='master_ip', MASTER_USER='repl', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154;
START SLAVE;
4.4 分库分表基础
- 垂直分表:将不常用字段或大字段拆分到扩展表
- 水平分表:按用户ID哈希或时间范围分片
- ShardingSphere:透明化分库分表,应用层无需感知
4.5 性能压测与监控指标
常用性能指标
| 指标 | 命令/查询 | 正常范围 |
|---|---|---|
| QPS/TPS | SHOW GLOBAL STATUS LIKE 'Questions'; | 视硬件而定 |
| 慢查询比例 | SHOW GLOBAL STATUS LIKE 'Slow_queries'; | 占比<1% |
| 缓存命中率 | SHOW STATUS LIKE 'Innodb_buffer_pool_reads%'; | 命中率>95% |
| 锁等待次数 | SHOW STATUS LIKE 'Innodb_row_lock_waits'; | 越低越好 |
| 活跃连接数 | SHOW PROCESSLIST; | 小于max_connections的80% |
使用sysbench进行压测
# 安装
apt install sysbench
# 准备数据(16个表,每个表100万行)
sysbench /usr/share/sysbench/oltp_read_write.lua --table-size=1000000 --tables=16 prepare
# 运行压测(64个线程,持续60秒)
sysbench /usr/share/sysbench/oltp_read_write.lua --threads=64 --time=60 run
4.6 高频面试题库(精选+场景题)
Q1:MySQL为什么用B+树而不是B树?
A:B+树非叶子节点不存数据,可存放更多索引,树高更低;叶子节点有序链表,范围查询效率极高。
Q2:什么是MVCC?如何实现可重复读?
A:MVCC通过Undo Log保存数据快照,事务开始时生成Read View。可重复读下,每次查询使用同一个Read View,保证看到的数据一致。
Q3:间隙锁是什么?如何解决幻读?
A:间隙锁锁定一个区间,防止其他事务插入新数据。在RR级别下,InnoDB使用Next-Key Lock(行锁+间隙锁)彻底解决幻读。
Q4:如何优化大分页查询?
A:使用延迟关联(先查主键再JOIN原表)或游标分页(仅限上一页/下一页场景)。
Q5:count(*)、count(1)、count(字段)的区别?
A:count(*)统计所有行,包括NULL,MySQL对其做了专门优化,速度最快;count(1)本质相同;count(字段)忽略NULL值。
Q6(场景题):假设有一个订单表,每天新增100万数据,如何设计分表方案?
A:可按用户ID哈希分1024张表(取模),或按时间按月分表。查询时需带上分片键,否则会遍历所有表。可使用ShardingSphere或MyCat透明化分片。
Q7(场景题):线上突然出现大量Waiting for table metadata lock,如何处理?
A:通常是DDL操作或长时间未提交的事务阻塞了后续查询。应先找到阻塞源头(SHOW PROCESSLIST),杀掉长时间未提交的事务或DDL会话,然后优化业务代码,避免在高峰期执行DDL。
Q8(场景题):如何在不重启数据库的情况下,给一个10亿行的表加索引?
A:MySQL 8.0支持Online DDL,ALTER TABLE ... ADD INDEX ... ALGORITHM=INPLACE, LOCK=NONE,允许并发DML。但依然建议在低峰期执行,并使用pt-online-schema-change工具控制负载。
Q9:为什么禁止业务用物理外键?
- 高并发下锁范围扩大,极易死锁
- 主从延迟导致外键校验异常
- 分库分表后外键彻底失效
- 微服务架构跨库无法外键
✅ 现代架构:业务逻辑一致性替代物理外键。(单库小型后台管理可酌情使用)
Q10:DELETE、TRUNCATE、DROP 三兄弟完整区别(终极版)
- DELETE:DML、可回滚、不删结构、自增不重置
- TRUNCATE:DDL、不可回滚、清空数据、重置自增(8.0下若表有外键约束则执行失败)
- DROP:DDL、删表结构+数据、释放空间
Q11:MVCC 到底解决了什么?
- 解决:脏读、不可重复读
- 解决不了:写幻读(必须靠锁)
Q12:大表加索引如何不锁表?
8.0:ALTER TABLE t ADD INDEX idx_xxx(col) ALGORITHM=INPLACE, LOCK=NONE;
Q13:线上慢SQL突然暴涨,排查链路?
- 慢查询日志
- EXPLAIN / EXPLAIN ANALYZE 看type、key、rows、实际耗时
- 检查是否索引失效(结合直方图判断数据分布)
- 检查是否大分页、大事务
- 检查是否锁等待、元数据锁
4.7 2026生产禁止清单(超级实用)
| 禁止项 | 原因 | 替代方案 |
|---|---|---|
| ❌ TIMESTAMP | 2038年溢出 | 使用DATETIME |
| ❌ utf8字符集 | 不支持emoji、生僻字 | 使用utf8mb4 |
| ❌ MyISAM引擎 | 不支持事务、崩溃不安全 | 使用InnoDB |
| ❌ 物理外键(微服务/分布式) | 锁竞争、迁移困难 | 逻辑外键(业务保证) |
| ❌ SELECT * | 浪费IO、无法覆盖索引 | 只查需要的字段 |
| ❌ 无索引JOIN多表 | 全表扫描、性能灾难 | 关联字段建索引 |
| ❌ 线上大事务 | 锁持有时间长、死锁风险 | 拆分小事务 |
| ❌ DELETE全表/UPDATE全表 | 锁表、主从延迟 | 分批处理或TRUNCATE |
| ❌ FLOAT/DOUBLE存金额 | 精度丢失 | 使用DECIMAL |
附录:工具清单
| 工具 | 用途 | 推荐指数 |
|---|---|---|
| SQLAdvisor | 索引优化建议 | ⭐⭐⭐⭐ |
| pt-query-digest | 慢查询分析 | ⭐⭐⭐⭐⭐ |
| DBeaver | 免费可视化客户端 | ⭐⭐⭐⭐⭐ |
| Navicat | 商业级客户端 | ⭐⭐⭐⭐ |
| LeetCode SQL题库 | 面试刷题 | ⭐⭐⭐⭐⭐ |
| sysbench | 性能压测 | ⭐⭐⭐⭐⭐ |
结语:本教程覆盖了从零基础到大厂生产的完整路径,所有知识均基于MySQL 8.0 LTS,并充分考虑了2026年后的技术趋势。掌握这些内容,你将拥有5年内不过时的数据库能力。祝你学习愉快,早日成为数据库专家!
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/slyn_2004/article/details/162850074




