MySQL完整学习手册(赵老师视频精华版)
基于赵老师8个MySQL教学视频 + 数据库运维XMind笔记 + G盘教案
标注说明:【面试必问】 = 面试高频考点 | 【教案补充】 = 教案文档细节补充 | 【知识拓展】 = 视频外MySQL必备知识
第1章:MySQL概述与安装部署
1.1 数据库核心概念
数据库(Database,简称DB)是按照特定格式存储数据的文件集合,本质上是存储在磁盘上的一堆文件。数据库管理系统(DBMS)是专门管理数据库中数据的软件,MySQL、Oracle、PostgreSQL等都属于DBMS。
SQL(Structured Query Language)是操作关系型数据库的标准语言,由IBM于1970年代提出,目前所有主流关系型数据库都支持SQL。
常见关系型数据库:
| 数据库 | 特点 |
|---|---|
| MySQL / MariaDB | 开源、社区活跃、互联网首选 |
| Oracle | 商业数据库、金融/电信核心系统 |
| PostgreSQL | 开源、功能最丰富、GIS支持强 |
| SQL Server | 微软生态、Windows集成 |
| TiDB | 国产分布式关系型数据库 |
【知识拓展】 MySQL和MariaDB的关系:MariaDB是MySQL创始人Monty Widenius在MySQL被Oracle收购后(2009年)创建的MySQL分支,与MySQL高度兼容。MySQL 8.0之后两者差异逐渐增大。
1.2 关系型数据库模型
【面试必问】 关系模型建立在严格的数学基础上,核心三要素:
实体(Entity):现实世界中可区分的对象,对应数据库中的"表"(Table)。
- 例如:学生、课程、教师
属性(Attribute):实体的特征,对应表中的"列"(Column/Field)。
- 例如:学生的姓名、学号、年龄
联系(Relationship):实体之间的关系,通过外键实现。
- 一对一(1:1):一个部门对应一个负责人
- 一对多(1:N):一个教师教授多门课程,每门课程只有一个教师
- 多对多(M:N):一个学生选修多门课程,每门课程有多个学生选修(需要中间表)
表结构术语对照:
| 术语 | 含义 |
|---|---|
| 库(Database) | 存储数据的容器,包含多张表 |
| 表(Table) | 由行列组成的二维表格 |
| 行(Row/Record) | 表中的一条记录,代表一个实体实例 |
| 列(Column/Field) | 表中的一列,代表一个属性 |
| 主键(Primary Key) | 唯一标识一行记录的列或列组合 |
1.3 MySQL版本与生态
【知识拓展】 MySQL版本演进关键节点:
- MySQL 5.5(2010):InnoDB成为默认引擎
- MySQL 5.6(2013):GTID复制、Online DDL
- MySQL 5.7(2015):JSON支持、sys schema、多源复制
- MySQL 8.0(2018):窗口函数、CTE、原子DDL、caching_sha2_password默认认证【当前企业主流】
- MySQL 8.4 LTS(2024):长期支持版本
MySQL 8.0核心新特性:
- 窗口函数(ROW_NUMBER/RANK/DENSE_RANK/LAG/LEAD)
- 公用表表达式CTE(WITH RECURSIVE)
- 原子DDL(CREATE/ALTER/DROP不可部分执行)
- 默认字符集utf8mb4、默认排序规则utf8mb4_0900_ai_ci
- 隐藏索引(Invisible Index)、降序索引
- 克隆插件(Clone Plugin)
1.4 RPM安装MySQL
四要素:
- 机器:MySQL服务器(任意Linux主机,如 192.168.100.165)
- 做什么:通过yum/dnf安装MySQL 8.0服务端
- 完整命令:如下
- 为什么:RPM方式自动处理依赖,适合快速标准化部署
bash
# 步骤1:安装MySQL Yum仓库
rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
# 步骤2:安装MySQL服务端和客户端
yum install -y mysql-server mysql
# 步骤3:启动并设置开机自启
systemctl enable --now mysqld
# 步骤4:获取初始root密码(MySQL 8.0首次启动生成随机密码)
grep 'temporary password' /var/log/mysqld.log
# 步骤5:安全初始化(修改root密码、移除匿名用户、禁止远程root登录、删除test库)
mysql_secure_installation
# 步骤6:使用新密码登录验证
mysql -uroot -p
为什么需要mysql_secure_installation:MySQL安装后默认存在安全风险(空密码root、匿名用户、test数据库可被任何人访问),此命令交互式地加固初始安全配置。
MariaDB安装(替代方案):
bash
yum install -y mariadb-server mariadb
systemctl enable --now mariadb
1.5 编译安装MySQL
为什么需要编译安装:需要对MySQL进行定制化编译(如指定安装路径、启用/禁用特定存储引擎、优化编译参数)时使用编译安装。适合对性能有极致要求或需要自定义功能的场景。
四要素:
- 机器:MySQL服务器(OpenEuler 24.03 或 CentOS/RHEL 8+)
- 做什么:源码编译安装MySQL 8.0
- 完整命令:如下
- 为什么:编译安装可定制编译参数、安装路径,获得最佳性能
bash
# === 环境准备 ===
# 安装编译依赖
dnf install -y gcc gcc-c++ cmake ncurses-devel openssl-devel \
bison libtirpc-devel rpcgen make
# 创建mysql用户(不允许登录,仅用于运行服务)
useradd -r -s /sbin/nologin mysql
# === 下载并编译 ===
# 下载Boost库(MySQL 8.0编译需要)
wget https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.gz
tar xf boost_1_77_0.tar.gz -C /usr/local/
# 下载MySQL源码
wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.36.tar.gz
tar xf mysql-8.0.36.tar.gz
cd mysql-8.0.36
# 创建编译目录(out-of-source build)
mkdir build && cd build
# CMake配置
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
-DMYSQL_DATADIR=/data/mysql \
-DSYSCONFDIR=/etc \
-DWITH_BOOST=/usr/local/boost_1_77_0 \
-DDEFAULT_CHARSET=utf8mb4 \
-DDEFAULT_COLLATION=utf8mb4_0900_ai_ci \
-DWITH_SSL=system \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_ARCHIVE_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DENABLED_LOCAL_INFILE=1
# 编译(-j 根据CPU核心数调整)
make -j$(nproc)
# 安装
make install
# === 初始化与配置 ===
# 创建数据目录
mkdir -p /data/mysql
chown -R mysql:mysql /usr/local/mysql /data/mysql
# 初始化数据库(生成root随机密码,记下日志中输出的密码)
/usr/local/mysql/bin/mysqld --initialize --user=mysql --datadir=/data/mysql
# 添加环境变量
echo 'export PATH=/usr/local/mysql/bin:$PATH' >> /etc/profile
source /etc/profile
# 创建systemd服务文件
cat > /usr/lib/systemd/system/mysqld.service << 'EOF'
[Unit]
Description=MySQL Server
After=network.target
[Service]
Type=forking
User=mysql
Group=mysql
ExecStart=/usr/local/mysql/bin/mysqld --daemonize
ExecStop=/usr/local/mysql/bin/mysqladmin -uroot -p shutdown
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now mysqld
cmake关键参数说明:
CMAKE_INSTALL_PREFIX:安装根目录MYSQL_DATADIR:数据文件存放目录(生产环境建议独立磁盘)DEFAULT_CHARSET=utf8mb4:默认字符集(支持emoji和所有Unicode字符)WITH_BOOST:指定Boost库路径(MySQL 8.0必选)
1.6 安装后验证
bash
# 查看版本
mysql -V
# MySQL Ver 8.0.36 for Linux on x86_64 (Source distribution)
# 查看服务状态
systemctl status mysqld
# 查看监听端口(默认3306)
ss -tlnp | grep 3306
# 连接数据库
mysql -uroot -p
第2章:MySQL配置、默认数据库与SQL语言基础
2.1 MySQL配置文件与目录结构
配置文件my.cnf
安装后MySQL的配置文件在 /etc/my.cnf,使用分组结构 [mysqld]、[client]、[mysql] 等。配置优先级(从高到低):
/etc/my.cnf → /etc/mysql/my.cnf → ~/.my.cnf
配置示例与核心参数解读:
ini
[client]
port=3306
socket=/var/lib/mysql/mysql.sock
[mysqld]
# === 基础设置 ===
port=3306 # 监听端口
datadir=/data/mysql # 数据文件目录
socket=/var/lib/mysql/mysql.sock # 本地连接socket
pid-file=/var/run/mysqld/mysqld.pid # PID文件路径
log-error=/var/log/mysqld.log # 错误日志路径
basedir=/usr # MySQL安装目录
# === 字符集 ===
character-set-server=utf8mb4 # 服务端默认字符集
collation-server=utf8mb4_0900_ai_ci # 排序规则
# === 连接 ===
max_connections=500 # 最大连接数
max_connect_errors=100 # 最大连接错误数(防暴力破解)
wait_timeout=600 # 非交互连接超时(秒)
# === InnoDB ===
innodb_buffer_pool_size=2G # 缓冲池大小(设为物理内存的50%-70%)
innodb_log_file_size=512M # 单个Redo Log文件大小
innodb_flush_log_at_trx_commit=1 # 事务提交时刷盘策略
innodb_file_per_table=ON # 每表独立表空间
# === binlog ===
log-bin=mysql-bin # 开启二进制日志(主从复制必需)
binlog_format=ROW # binlog格式(STATEMENT/ROW/MIXED)
expire_logs_days=7 # binlog保留天数
max_binlog_size=1G # 单个binlog文件上限
# === slow log ===
slow_query_log=ON # 开启慢查询日志
slow_query_log_file=/var/log/mysql-slow.log
long_query_time=2 # 超过2秒计为慢查询
【面试必问】 innodb_flush_log_at_trx_commit 的三个值:
| 值 | 行为 | 性能 | 安全性 |
|---|---|---|---|
| 0 | 每秒刷一次redo log(OS刷盘) | 最快 | 可能丢失1秒数据 |
| 1 | 每次提交都刷盘(fsync) | 最慢 | 最安全,ACID保证 |
| 2 | 每次提交写OS缓存,每秒刷盘 | 中等 | 机器宕机不丢,OS宕机丢1秒 |
生产环境建议值:1(金融)或 2(互联网,可接受1秒内数据丢失)。
目录结构
RPM安装后的关键目录:
/usr/bin/ # 客户端工具 (mysql, mysqladmin, mysqldump...)
/usr/sbin/ # 服务端程序 (mysqld)
/var/lib/mysql/ # 数据目录 (datadir)
/etc/my.cnf # 配置文件
/var/log/mysqld.log # 错误日志
/var/run/mysqld/ # socket和pid文件
2.2 MySQL四大默认数据库
SHOW DATABASES; 可以看到4个(MariaDB则为5个)默认数据库:
| 数据库 | 作用 | 存储内容 |
|---|---|---|
information_schema |
元数据信息库 | 68个视图,记录所有库/表/列/索引/权限等元数据 |
mysql |
系统核心库 | user表(用户权限)、db表(库级权限)、tables_priv、columns_priv等 |
performance_schema |
性能监控库 | 服务器运行时状态、等待事件、内存使用、SQL执行统计 |
sys |
诊断辅助库(5.7+) | 基于performance_schema的易读视图,如sys.schema_unused_indexes |
为什么需要理解这四个库 :排错时经常需要查
information_schema.PROCESSLIST看连接、查mysql.user看权限、查performance_schema分析性能瓶颈。
关键查询示例:
sql
-- 查看所有数据库
SHOW DATABASES;
-- 查看当前连接
SELECT * FROM information_schema.PROCESSLIST;
-- 查看所有用户和主机
SELECT user, host FROM mysql.user;
-- 查找未使用的索引
SELECT * FROM sys.schema_unused_indexes;
2.3 SQL语言四大分类
【面试必问】 SQL语句按功能分为四大类:
| 分类 | 全称 | 核心命令 | 作用 |
|---|---|---|---|
| DDL | Data Definition Language | CREATE, ALTER, DROP, TRUNCATE, RENAME | 定义库/表/索引等数据库对象结构 |
| DML | Data Manipulation Language | INSERT, UPDATE, DELETE | 操作表中数据 |
| DQL | Data Query Language | SELECT | 查询数据 |
| DCL | Data Control Language | GRANT, REVOKE | 控制用户权限 |
| TCL | Transaction Control Language | COMMIT, ROLLBACK, SAVEPOINT | 事务控制 |
DDL vs DML 关键区别:
- DDL操作自动提交(不可回滚),执行后立即生效
- DML操作在事务中可回滚(需显式开启事务)
TRUNCATE属于DDL,速度快但不可回滚;DELETE属于DML,可回滚但逐行删除较慢
2.4 MySQL常用数据类型
数值类型
| 类型 | 范围(有符号) | 存储 | 适用场景 |
|---|---|---|---|
| TINYINT | -128~127 | 1字节 | 状态码、布尔值(0/1) |
| SMALLINT | -32768~32767 | 2字节 | 年龄、数量 |
| INT / INTEGER | -21亿~21亿 | 4字节 | 主键ID、计数 |
| BIGINT | -9.2E18~9.2E18 | 8字节 | 大表主键、时间戳 |
| FLOAT(M,D) | 单精度浮点 | 4字节 | 科学计算(不精确) |
| DOUBLE(M,D) | 双精度浮点 | 8字节 | 高精度科学计算 |
| DECIMAL(M,D) | 精确小数 | 变长 | 金额(必须用这个) |
【面试必问】 为什么金额不用FLOAT/DOUBLE而用DECIMAL?因为浮点数在二进制中无法精确表示十进制小数,例如 0.1+0.2=0.30000000000000004,会导致金额计算误差。DECIMAL以字符串形式存储,精确无误差。
字符串类型
| 类型 | 最大长度 | 特点 |
|---|---|---|
| CHAR(N) | 255字符 | 定长,用空格填充,查询最快 |
| VARCHAR(N) | 65535字节 | 变长,按实际长度+1~2字节存储,节省空间 |
| TEXT | 65535字符 | 大文本,不能有默认值 |
| MEDIUMTEXT | 16MB | 中型文本 |
| LONGTEXT | 4GB | 超长文本 |
| BLOB | 65535字节 | 二进制大对象(存文件/图片) |
| ENUM | 65535个值 | 枚举类型(选一) |
CHAR vs VARCHAR:CHAR适合固定长度的短字符串(如MD5哈希32位、身份证号18位),VARCHAR适合变长字符串(姓名、邮箱)。CHAR读取速度快但浪费空间,VARCHAR省空间但需要额外字节记录长度。
日期时间类型
| 类型 | 格式 | 范围 | 存储 | 时区 |
|---|---|---|---|---|
| DATE | YYYY-MM-DD | 1000-01-01 ~ 9999-12-31 | 3字节 | 无关 |
| TIME | HH:MM:SS | -838:59:59 ~ 838:59:59 | 3字节 | 无关 |
| DATETIME | YYYY-MM-DD HH:MM:SS | 1000~9999 | 5字节(旧) 可变(新) | 无关 |
| TIMESTAMP | 1970-01-01 00:00:01 ~ 2038-01-19 | Unix时间戳 | 4字节 | 随系统时区自动转换 |
| YEAR | YYYY | 1901~2155 | 1字节 | --- |
TIMESTAMP vs DATETIME:TIMESTAMP受2038年问题限制(32位),但会自动根据时区转换,适合记录"发生时间";DATETIME不随时区变,适合记录"日历时间"(如生日)。
2.5 JSON数据类型(MySQL 5.7+)
【教案补充】
sql
-- 创建含JSON列的表
CREATE TABLE configs (
id INT AUTO_INCREMENT PRIMARY KEY,
settings JSON
);
-- 插入JSON数据
INSERT INTO configs (settings) VALUES ('{"lang": "zh", "theme": "dark"}');
-- JSON查询(-> 返回JSON,->> 返回文本)
SELECT settings->>'$.lang' AS lang FROM configs;
-- JSON更新
UPDATE configs SET settings = JSON_SET(settings, '$.theme', 'light') WHERE id=1;
第3章:数据库与表操作、用户权限管理
3.1 数据库操作(DDL)
sql
-- 创建数据库(指定字符集和排序规则)
CREATE DATABASE school
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_0900_ai_ci;
-- 查看所有数据库
SHOW DATABASES;
-- 查看建库语句
SHOW CREATE DATABASE school;
-- 选择/切换数据库
USE school;
-- 修改数据库字符集
ALTER DATABASE school CHARACTER SET utf8mb4;
-- 删除数据库(不可回滚!)
DROP DATABASE school;
3.2 表操作(DDL)
创建表
sql
CREATE TABLE student (
id INT AUTO_INCREMENT COMMENT '学号',
name VARCHAR(20) NOT NULL COMMENT '姓名',
gender ENUM('男','女') DEFAULT '男' COMMENT '性别',
birth DATE COMMENT '出生日期',
phone CHAR(11) COMMENT '手机号',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
修改表结构
sql
-- 添加列
ALTER TABLE student ADD COLUMN email VARCHAR(50);
-- 修改列类型
ALTER TABLE student MODIFY COLUMN email VARCHAR(100);
-- 重命名列(同时改类型)
ALTER TABLE student CHANGE COLUMN email mail_addr VARCHAR(80);
-- 删除列
ALTER TABLE student DROP COLUMN mail_addr;
-- 添加索引
ALTER TABLE student ADD INDEX idx_name (name);
-- 重命名表
RENAME TABLE student TO stu;
-- 查看表结构
DESC stu;
删除表
sql
-- 逐行删除数据,可回滚(DML)
DELETE FROM stu;
-- 清空表,DDL,不可回滚,重置AUTO_INCREMENT
TRUNCATE TABLE stu;
-- 删除表结构+数据(DDL)
DROP TABLE stu;
【面试必问】TRUNCATE vs DELETE vs DROP:
| 操作 | 类型 | 可回滚 | 速度 | AUTO_INCREMENT | WHERE |
|---|---|---|---|---|---|
| DELETE | DML | ✅(事务中) | 慢(逐行+写undo) | 保留当前值 | ✅ |
| TRUNCATE | DDL | ❌ | 极快(重建数据文件) | 重置为1 | ❌ |
| DROP | DDL | ❌ | --- | --- | ❌ |
3.3 数据操作(DML / DQL)
插入数据
sql
-- 单行插入
INSERT INTO student (name, gender, birth, phone)
VALUES ('张三', '男', '2000-01-15', '13800138001');
-- 批量插入(性能更优)
INSERT INTO student (name, gender, birth, phone) VALUES
('李四', '女', '2001-03-20', '13800138002'),
('王五', '男', '1999-12-10', '13800138003');
-- 从查询结果插入
INSERT INTO student_archive SELECT * FROM student WHERE birth < '2000-01-01';
更新数据
sql
-- 更新单行(务必加WHERE!)
UPDATE student SET phone='13900139001' WHERE id=1;
-- 批量更新
UPDATE student SET gender='女' WHERE name LIKE '张%';
-- ⚠️ 不加WHERE会更新全表!
删除数据
sql
-- 删除指定行
DELETE FROM student WHERE id=3;
-- 删除全表数据(逐行删,大表很慢)
DELETE FROM student;
查询数据(DQL)
sql
-- 全表查询
SELECT * FROM student;
-- 指定列
SELECT id, name, phone FROM student;
-- 条件查询
SELECT * FROM student WHERE gender='男' AND birth > '2000-01-01';
-- 模糊查询
SELECT * FROM student WHERE name LIKE '张%'; -- 以"张"开头
SELECT * FROM student WHERE name LIKE '%三'; -- 以"三"结尾
-- 排序
SELECT * FROM student ORDER BY birth DESC;
-- 分页
SELECT * FROM student LIMIT 10 OFFSET 20; -- 第3页,每页10条
-- 聚合
SELECT gender, COUNT(*) AS cnt FROM student GROUP BY gender;
-- 聚合后过滤(HAVING vs WHERE:WHERE在分组前过滤,HAVING在分组后过滤)
SELECT gender, COUNT(*) AS cnt FROM student
GROUP BY gender HAVING cnt > 2;
3.4 用户权限管理
用户管理
四要素:
- 机器:MySQL服务器
- 做什么:创建用户并授予最小必要权限
- 完整命令:如下
- 为什么:生产环境严禁用root远程连接,应按需创建专用账号并遵循最小权限原则
sql
-- 登录(root本地)
mysql -uroot -p
-- 创建用户(MySQL 8.0默认caching_sha2_password)
CREATE USER 'appuser'@'%' IDENTIFIED BY 'StrongP@ss123';
-- 创建用户(指定mysql_native_password,兼容老旧客户端)
CREATE USER 'appuser'@'192.168.100.%' IDENTIFIED WITH mysql_native_password BY 'StrongP@ss123';
-- 查看所有用户
SELECT user, host, plugin FROM mysql.user;
-- 修改密码
ALTER USER 'appuser'@'%' IDENTIFIED BY 'NewP@ss456';
-- 锁定/解锁用户
ALTER USER 'appuser'@'%' ACCOUNT LOCK;
ALTER USER 'appuser'@'%' ACCOUNT UNLOCK;
-- 删除用户
DROP USER 'appuser'@'%';
【知识拓展】 MySQL 8.0默认认证插件从mysql_native_password改为caching_sha2_password,老版本客户端(如Navicat 11以下)连接会报错。解决方案:①升级客户端;②创建用户时指定WITH mysql_native_password。
权限授予与回收
sql
-- 授予权限
-- 给appuser授予school库所有权限
GRANT ALL PRIVILEGES ON school.* TO 'appuser'@'%';
-- 只给SELECT查询权限
GRANT SELECT ON school.* TO 'readonly'@'%';
-- 给特定表的特定列权限
GRANT SELECT (name, phone) ON school.student TO 'minuser'@'%';
-- 授予SUPER权限(需要全局级别)
GRANT SUPER, REPLICATION SLAVE ON *.* TO 'repl'@'%';
-- 查看用户权限
SHOW GRANTS FOR 'appuser'@'%';
-- 回收权限
REVOKE DELETE ON school.* FROM 'appuser'@'%';
-- 刷新权限(修改权限表后执行)
FLUSH PRIVILEGES;
常见授权场景
| 角色 | 权限 | 命令示例 |
|---|---|---|
| DBA | ALL PRIVILEGES ON . | GRANT ALL ON *.* TO 'dba'@'localhost'; |
| 应用读写 | SELECT, INSERT, UPDATE, DELETE | GRANT SELECT,INSERT,UPDATE,DELETE ON appdb.* TO 'app'@'%'; |
| 只读用户 | SELECT | GRANT SELECT ON appdb.* TO 'reader'@'%'; |
| 主从复制 | REPLICATION SLAVE | GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; |
| 备份用户 | SELECT, RELOAD, LOCK TABLES, PROCESS | GRANT SELECT,RELOAD,LOCK TABLES,PROCESS ON *.* TO 'backup'@'localhost'; |
最小权限原则:每个用户只授予其完成任务所必需的权限。例如:备份用户不需要DELETE,只读用户不需要INSERT/UPDATE/DELETE。
第4章:MySQL备份与恢复
4.1 备份策略基础
备份的目的是灾难恢复(Disaster Recovery),核心指标:
- RPO(Recovery Point Objective):恢复点目标------能容忍丢失多少数据(越小越好)
- RTO(Recovery Time Objective):恢复时间目标------多长时间能恢复服务
| 备份类型 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 逻辑备份 | 导出SQL语句 | 跨版本兼容、可读可编辑 | 慢、备份时锁表 |
| 物理备份 | 复制数据文件 | 快、不锁表(热备份) | 跨平台/版本兼容性差 |
| 全量备份 | 备份全部数据 | 恢复最简单 | 耗时长、空间大 |
| 增量备份 | 只备份上次备份以来变化的数据 | 空间小、速度快 | 恢复需叠加多次备份 |
| 热备份 | 数据库运行时备份 | 不影响业务 | 需要特定工具支持 |
| 冷备份 | 停库后备份 | 最安全最一致 | 需要停机窗口 |
4.2 逻辑备份:mysqldump
【面试必问】mysqldump原理 :通过SQL语句逐条读取数据并生成INSERT语句。备份期间对表加读锁(FLUSH TABLES WITH READ LOCK),InnoDB可通过--single-transaction避免锁表。
bash
# === 全库备份 ===
# 四要素:备份服务器 / 全库备份 / 以下命令 / 用于灾难恢复
mysqldump -uroot -p --all-databases --single-transaction --routines --triggers \
> /backup/full_$(date +%Y%m%d).sql
# === 单库备份 ===
mysqldump -uroot -p --databases school --single-transaction \
> /backup/school_$(date +%Y%m%d).sql
# === 单表备份(只备份结构和数据)===
mysqldump -uroot -p school student \
> /backup/student_$(date +%Y%m%d).sql
# === 只导出表结构 ===
mysqldump -uroot -p --no-data school > /backup/school_schema.sql
# === 只导出数据(不含建表语句)===
mysqldump -uroot -p --no-create-info school student > /backup/student_data.sql
关键参数:
| 参数 | 作用 |
|---|---|
--single-transaction |
InnoDB热备份关键参数,开启事务保证一致性读,不发锁表 |
--routines |
导出存储过程和函数 |
--triggers |
导出触发器 |
--master-data=2 |
在备份SQL中记录binlog位置(注释形式),恢复后可用于搭建主从 |
--flush-logs |
备份前刷新日志,生成新binlog文件 |
--all-databases |
备份所有库(含mysql系统库) |
4.3 物理备份:XtraBackup
【面试必问】XtraBackup vs mysqldump:
| 维度 | XtraBackup | mysqldump |
|---|---|---|
| 原理 | 直接复制InnoDB数据文件 + redo log | 逐行读数据生成INSERT SQL |
| 锁表 | ❌ 不锁表(热备份) | ⚠️ MyISAM表会锁(加--single-transaction后InnoDB不锁) |
| 速度 | 快(与数据文件大小成正比) | 慢(需逐行导出) |
| 恢复速度 | 快(直接复制回) | 慢(逐行INSERT回放) |
| 增量备份 | ✅ 原生支持 | ❌ 需结合binlog |
| 兼容性 | 同版本/同平台 | 跨版本跨平台 |
| 适用场景 | 大库(>10GB)、生产环境 | 小库、逻辑迁移、跨版本升级 |
XtraBackup安装
bash
# RHEL/CentOS 8+
dnf install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm
percona-release enable-only tools release
dnf install -y percona-xtrabackup-80
XtraBackup全量备份
四要素:
- 机器:MySQL服务器(对数据文件有读取权限)
- 做什么:创建全量物理热备份
- 完整命令:如下
- 为什么:不锁表、速度快,适合大库生产环境
bash
# 创建备份
xtrabackup --backup \
--user=root --password='YourP@ss' \
--target-dir=/backup/full_$(date +%Y%m%d) \
--datadir=/data/mysql
# 准备备份(Apply Redo Log,使备份数据一致)
xtrabackup --prepare --target-dir=/backup/full_20260607
# 恢复(先停MySQL,清空原数据目录)
systemctl stop mysqld
rm -rf /data/mysql/*
xtrabackup --copy-back --target-dir=/backup/full_20260607
chown -R mysql:mysql /data/mysql
systemctl start mysqld
XtraBackup增量备份
bash
# 在全量备份基础上做增量
xtrabackup --backup \
--user=root --password='YourP@ss' \
--target-dir=/backup/inc_$(date +%Y%m%d) \
--incremental-basedir=/backup/full_20260607
# 恢复增量:先prepare全量(--apply-log-only),再合并增量,最后--prepare完成
xtrabackup --prepare --apply-log-only --target-dir=/backup/full_20260607
xtrabackup --prepare --target-dir=/backup/full_20260607 \
--incremental-dir=/backup/inc_20260607
# 最后 --copy-back 恢复
4.4 binlog时间点恢复
binlog记录了所有数据变更,可实现任意时间点恢复。
bash
# 查看当前binlog文件和位置
mysql -uroot -p -e "SHOW MASTER STATUS;"
# 解析binlog(确认误操作位置)
mysqlbinlog --base64-output=DECODE-ROWS -v /data/mysql/mysql-bin.000003 | less
# 恢复到误操作前一刻
mysqlbinlog --stop-datetime="2026-06-07 14:30:00" /data/mysql/mysql-bin.000003 | mysql -uroot -p
# 跳过误操作,恢复之后的数据
mysqlbinlog --start-datetime="2026-06-07 14:31:00" /data/mysql/mysql-bin.000003 | mysql -uroot -p
# 按位置点恢复(更精确)
mysqlbinlog --stop-position=123456 /data/mysql/mysql-bin.000003 | mysql -uroot -p
4.5 生产环境备份策略推荐
| 频率 | 类型 | 工具 | 保留 |
|---|---|---|---|
| 每天凌晨 | 全量 | XtraBackup | 7天 |
| 每4小时 | 增量 | XtraBackup | 3天 |
| 实时 | binlog | 系统自带 | 7天(expire_logs_days=7) |
| 每周日 | mysqldump | mysqldump | 30天(异地) |
为什么需要两种备份并存:XtraBackup快速恢复,mysqldump跨版本可读。全量+增量+binlog组合可满足RPO≈0(丢失<1秒)的需求。
4.6 压缩备份与自动化脚本
bash
# 压缩备份(节省空间)
mysqldump -uroot -p --all-databases --single-transaction | gzip > /backup/full.sql.gz
# 解压恢复
gunzip < /backup/full.sql.gz | mysql -uroot -p
定时备份脚本示例
bash
#!/bin/bash
# 保存为 /usr/local/bin/mysql_backup.sh,crontab 定时执行
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=7
mkdir -p $BACKUP_DIR
# XtraBackup全量
xtrabackup --backup --user=backup --password='xxx' \
--target-dir=$BACKUP_DIR/full_$DATE
# 清理旧备份
find $BACKUP_DIR -name "full_*" -type d -mtime +$KEEP_DAYS -exec rm -rf {} \;
bash
# 添加到crontab(每天凌晨2点执行)
# crontab -e
0 2 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1
第5章:数据库高级特性------主键、外键、索引、Check约束
5.1 主键PRIMARY KEY
主键是表中唯一标识每一行记录的列或列组合。一张表只能有一个主键,主键值不能为NULL且不能重复。
创建主键:
sql
-- 建表时指定
CREATE TABLE course (
id INT AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (id)
);
-- 建表后添加
ALTER TABLE course ADD PRIMARY KEY (id);
-- 修改自增主键起始值
ALTER TABLE course AUTO_INCREMENT=1000;
【面试必问】主键与InnoDB聚簇索引的关系:InnoDB中,主键就是聚簇索引(Clustered Index),表数据按主键B+Tree的叶子节点物理存储。如果没有显式定义主键,InnoDB会选择第一个UNIQUE NOT NULL索引作为聚簇索引;如果都没有,InnoDB会自动生成一个6字节的隐藏row_id作为聚簇索引。
为什么自增INT是最佳主键:自增ID按顺序插入B+Tree,减少页分裂(Page Split),保证了聚簇索引的插入效率和存储密度。UUID作为主键会导致随机插入、频繁页分裂、索引碎片化。
5.2 外键FOREIGN KEY
外键用于建立表与表之间的关系,保证引用完整性。
sql
CREATE TABLE score (
id INT AUTO_INCREMENT PRIMARY KEY,
stu_id INT NOT NULL,
course_id INT NOT NULL,
score DECIMAL(5,2),
-- 外键约束
CONSTRAINT fk_score_student FOREIGN KEY (stu_id) REFERENCES student(id)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
级联操作:
| 选项 | 说明 |
|---|---|
| CASCADE | 父表更新/删除时,子表同步更新/删除 |
| SET NULL | 父表删除时,子表外键列置NULL |
| RESTRICT / NO ACTION | 如果子表有引用,拒绝父表的更新/删除 |
| SET DEFAULT | 父表更新/删除时,子表外键设为默认值 |
【知识拓展】生产环境中外键的争议:互联网公司通常不在数据库层面使用外键约束(在应用层保证引用完整性),原因是外键在高并发写入时会带来额外的锁开销和性能损耗。
5.3 索引(Index)
【面试必问------全章重点】
5.3.1 索引分类
按数据结构分
| 类型 | 底层结构 | 特点 | 适用场景 |
|---|---|---|---|
| B+Tree | 多路平衡搜索树 | 有序、支持范围查询 | InnoDB默认、最常用 |
| Hash | 哈希表 | O(1)等值查询 | MEMORY引擎、等值查询 |
| FullText | 倒排索引 | 全文搜索 | 大文本搜索(如文章内容) |
【面试必问】B+Tree为什么是MySQL索引的最佳选择:
- 高度低:B+Tree的扇出率(fanout)极高,三层即可索引千万级数据。查找一个值只需3次磁盘I/O
- 叶子节点形成有序链表 :所有数据存在叶子节点,叶子节点之间用双向链表连接,天然支持范围查询和排序(
ORDER BY/BETWEEN) - 非叶子节点只存索引键:不存数据,一个节点能容纳更多索引键,进一步降低树高
- 与B树的区别:B树所有节点都存数据,B+树只在叶子节点存数据;B+树叶子节点有链表,支持高效范围扫描
- 为什么不选红黑树/AVL树:二叉树在磁盘场景下高度太大(百万数据约20层→20次I/O),B+树只需要3层
按物理存储分
| 类型 | 说明 | InnoDB中 |
|---|---|---|
| 聚簇索引 | 数据按索引顺序物理存储 | 主键就是聚簇索引 |
| 二级索引(辅助索引) | 叶子节点存储主键值 | 回表查询 |
【面试必问】聚簇索引 vs 二级索引:
- 聚簇索引的叶子节点存的是完整行数据
- 二级索引的叶子节点存的是主键值
- 通过二级索引查询时,先找到主键值,再回表(去聚簇索引中)查完整数据
- 覆盖索引:如果查询的列都在二级索引中,不需要回表,性能最高
按字段特性分
| 类型 | 特点 |
|---|---|
| 主键索引 | 唯一、非空、每表一个 |
| 唯一索引(UNIQUE) | 值唯一,允许NULL(多个NULL不冲突) |
| 普通索引 | 无约束,加速查询 |
| 前缀索引 | 对列的前N个字符建索引(节省空间) |
| 全文索引 | 用于全文搜索 |
5.3.2 索引操作
sql
-- 创建普通索引
CREATE INDEX idx_name ON student(name);
-- 创建唯一索引
CREATE UNIQUE INDEX uk_phone ON student(phone);
-- 创建联合索引
CREATE INDEX idx_name_age ON student(name, age);
-- 创建前缀索引(前10个字符)
CREATE INDEX idx_email_prefix ON student(email(10));
-- 查看表的所有索引
SHOW INDEX FROM student;
-- 删除索引
DROP INDEX idx_name ON student;
ALTER TABLE student DROP INDEX idx_name;
5.3.3 联合索引与最左匹配原则
【面试必问】最左匹配原则:联合索引中,查询条件必须从索引的最左列开始匹配,且不能跳过中间的列,索引才能被有效使用。
sql
-- 联合索引: (a, b, c)
CREATE INDEX idx_abc ON test(a, b, c);
-- ✅ 使用索引:从最左a开始匹配
SELECT * FROM test WHERE a = 1;
SELECT * FROM test WHERE a = 1 AND b = 2;
SELECT * FROM test WHERE a = 1 AND b = 2 AND c = 3;
SELECT * FROM test WHERE a = 1 AND c = 3; -- 只用到a(c被跳过)
-- ❌ 不使用索引:未从最左开始
SELECT * FROM test WHERE b = 2;
SELECT * FROM test WHERE c = 3;
SELECT * FROM test WHERE b = 2 AND c = 3;
联合索引设计原则 :把区分度最高、最常用于等值查询的列放在最左边。例如
(status, create_time)vs(create_time, status)------如果status只有几个值,create_time区分度高,后者通常更好(前提是查询总带create_time)。
5.3.4 索引失效的常见场景
【面试必问】哪些情况会导致索引失效:
| 场景 | 示例 | 原因 |
|---|---|---|
| 前导通配符模糊查询 | LIKE '%abc' / LIKE '%abc%' |
B+Tree只能按前缀匹配 |
| 对索引列使用函数 | WHERE YEAR(birth)=2000 |
破坏了索引的有序性 |
| 对索引列进行运算 | WHERE id+1=100 |
同上 |
| 隐式类型转换 | WHERE phone=13800138001(phone是CHAR) |
字符串→数字转换导致索引失效 |
| OR连接非索引列 | WHERE a=1 OR b=2(b没有索引) |
需要全表扫描 |
| 联合索引不满足最左匹配 | 见上节 | 跳过了最左列 |
| 使用 != 或 <> | WHERE status != 'done' |
可能退化为全表扫描(取决于优化器) |
| IS NULL / IS NOT NULL | 取决于数据分布 | 索引不存NULL值(InnoDB存) |
LIKE 'abc%' 可以使用索引:因为这是以"abc"为前缀的匹配,B+Tree可以定位到"abc"开头的第一个位置,顺序扫描即可。
5.3.5 EXPLAIN执行计划
【面试必问】 EXPLAIN 是分析SQL性能的核心工具。
sql
EXPLAIN SELECT * FROM student WHERE name = '张三';
关键字段解读:
| 字段 | 含义 |
|---|---|
| id | 查询序号(数字越大越先执行,相同从上到下) |
| select_type | SIMPLE(简单查询) / PRIMARY(外层) / SUBQUERY(子查询) / DERIVED(派生表) |
| table | 访问的表名 |
| type | 访问类型(最重要!从优到劣) |
| possible_keys | 可能使用的索引 |
| key | 实际使用的索引 |
| key_len | 使用的索引长度(字节),可判断联合索引用了几个列 |
| ref | 与索引比较的列或常量 |
| rows | 估算需要扫描的行数 |
| filtered | 按条件过滤后的行数百分比 |
| Extra | 额外信息(Using index=覆盖索引/Using filesort=文件排序/Using temporary=临时表) |
【面试必问】type字段从优到劣:
system > const > eq_ref > ref > range > index > ALL
| type | 说明 | 示例 |
|---|---|---|
| const | 通过主键或唯一索引匹配到一行 | WHERE id = 1 |
| eq_ref | 关联查询中,每个关联键只匹配一行 | JOIN中ON主键 |
| ref | 非唯一索引等值查询 | WHERE name = '张三' |
| range | 索引范围扫描 | WHERE id > 100 AND id < 200 |
| index | 全索引扫描(比ALL好,比range差) | SELECT name FROM student(走name索引) |
| ALL | 全表扫描 | 无索引或优化器选择全表扫描 |
Extra中的关键提示:
Using index(覆盖索引):最优,查询列全在索引中,不需要回表Using index condition(ICP):索引条件下推,在引擎层过滤数据Using filesort:需要优化,数据在内存/磁盘排序Using temporary:需要优化,使用了临时表
5.4 Check约束
约束字段值必须满足指定条件(MySQL 8.0.16+ 真正支持)。
sql
-- 创建带Check约束的表
CREATE TABLE employee (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
age TINYINT CHECK (age >= 18 AND age <= 65),
salary DECIMAL(10,2) CHECK (salary > 0)
);
-- 查看约束
SELECT * FROM information_schema.CHECK_CONSTRAINTS;
-- 违反约束时会报错
-- INSERT INTO employee(name, age) VALUES ('张三', 15); -- ERROR: Check constraint violated
第6章:存储过程、触发器、事务与锁机制
6.1 存储过程
存储过程是一组预编译的SQL语句集合,存储在数据库服务端,可重复调用。减少了网络传输和SQL解析开销。
创建和调用:
sql
-- 修改分隔符(避免分号被解析为语句结束)
DELIMITER //
CREATE PROCEDURE get_students_by_gender(IN g ENUM('男','女'))
BEGIN
SELECT * FROM student WHERE gender = g;
END //
DELIMITER ;
-- 调用
CALL get_students_by_gender('男');
-- 带输出参数的存储过程
DELIMITER //
CREATE PROCEDURE count_by_gender(IN g ENUM('男','女'), OUT cnt INT)
BEGIN
SELECT COUNT(*) INTO cnt FROM student WHERE gender = g;
END //
DELIMITER ;
CALL count_by_gender('男', @result);
SELECT @result;
-- 删除
DROP PROCEDURE IF EXISTS get_students_by_gender;
6.2 触发器
触发器是在表上发生INSERT/UPDATE/DELETE时自动执行的SQL代码块。
触发器的六种类型(时机×操作):BEFORE INSERT / AFTER INSERT / BEFORE UPDATE / AFTER UPDATE / BEFORE DELETE / AFTER DELETE
sql
-- 创建日志表
CREATE TABLE stu_log (
id INT AUTO_INCREMENT PRIMARY KEY,
stu_id INT,
action VARCHAR(10),
old_name VARCHAR(50),
new_name VARCHAR(50),
change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建AFTER UPDATE触发器
DELIMITER //
CREATE TRIGGER trg_student_update
AFTER UPDATE ON student
FOR EACH ROW
BEGIN
INSERT INTO stu_log(stu_id, action, old_name, new_name)
VALUES (NEW.id, 'UPDATE', OLD.name, NEW.name);
END //
DELIMITER ;
-- NEW 和 OLD 关键字:
-- INSERT: 只有 NEW(新插入的行)
-- DELETE: 只有 OLD(被删除的行)
-- UPDATE: NEW=更新后的值,OLD=更新前的值
-- 查看触发器
SHOW TRIGGERS;
-- 删除
DROP TRIGGER IF EXISTS trg_student_update;
生产环境建议:触发器会增加数据库的隐性逻辑复杂度,调试困难,且大量触发时影响性能。现代架构倾向于在应用层处理这类逻辑。
6.3 事务(Transaction)
【面试必问------全章重点】
6.3.1 ACID特性
事务是数据库操作的最小执行单元,要么全部成功、要么全部失败。核心是ACID四大特性:
| 特性 | 含义 | InnoDB实现方式 |
|---|---|---|
| A 原子性 | 事务中的操作要么全做、要么全不做 | Undo Log(回滚日志) |
| C 一致性 | 事务执行前后,数据库都处于一致状态 | A+I+D共同保障 |
| I 隔离性 | 并发事务之间互不干扰 | MVCC + 锁机制 |
| D 持久性 | 事务提交后数据永久保存 | Redo Log(重做日志) |
6.3.2 事务控制命令
sql
-- 开启事务(三种方式等价)
START TRANSACTION;
-- 或
BEGIN;
-- 执行一系列操作
INSERT INTO student(name) VALUES('张三');
UPDATE student SET phone='13800001111' WHERE id=1;
-- 提交(持久化)
COMMIT;
-- 回滚(撤销)
ROLLBACK;
-- 设置回滚点
SAVEPOINT sp1;
-- ... 操作 ...
ROLLBACK TO SAVEPOINT sp1; -- 回滚到sp1,sp1之后的操作被撤销
-- 查看当前是否自动提交
SELECT @@autocommit; -- 默认ON(每条语句自动提交)
-- 关闭自动提交(会话级)
SET autocommit = 0;
6.3.3 事务并发问题
多个事务同时操作同一数据时会出现三种问题:
| 问题 | 描述 | 示例 |
|---|---|---|
| 脏读 | 读到其他事务未提交的修改 | 事务B读到事务A修改但未提交的数据 |
| 不可重复读 | 同一事务两次读取,值不一样 | 事务A两次读同一行,中间事务B修改并提交了 |
| 幻读 | 同一事务两次范围查询,行数不一样 | 事务A两次SELECT COUNT(*),中间事务B新增了数据 |
6.3.4 四种事务隔离级别
【面试必问------必考!】
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现 |
|---|---|---|---|---|
| READ UNCOMMITTED(读未提交) | ❌ | ❌ | ❌ | 无锁 |
| READ COMMITTED(读已提交) | ✅ | ❌ | ❌ | 每次SELECT创建新Read View |
| REPEATABLE READ(可重复读) | ✅ | ✅ | ✅(InnoDB中) | 事务开始时创建Read View + 间隙锁 |
| SERIALIZABLE(串行化) | ✅ | ✅ | ✅ | 所有SELECT自动加共享锁 |
MySQL默认隔离级别是REPEATABLE READ 。在InnoDB中,RR级别通过MVCC + 间隙锁(Gap Lock) 解决了幻读问题,这是MySQL独有的设计。
设置和查看隔离级别:
sql
-- 查看全局/会话隔离级别
SELECT @@global.transaction_isolation;
SELECT @@session.transaction_isolation;
-- 设置会话级隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局(重启后生效,永久设置需改my.cnf)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
6.4 MVCC(多版本并发控制)
【面试必问------MVCC是面试高频考点】
MVCC是InnoDB实现高并发的核心机制。核心思想:不对数据加锁,而是维护数据的多个版本,让读操作不阻塞写操作、写操作不阻塞读操作。
6.4.1 MVCC三要素
| 组件 | 作用 |
|---|---|
| 隐藏字段 | 每行数据包含 DB_TRX_ID(最后一次修改的事务ID)、DB_ROLL_PTR(回滚指针)、DB_ROW_ID(隐藏主键) |
| Undo Log | 记录数据的历史版本,通过 DB_ROLL_PTR 串联成版本链 |
| Read View | 事务开始时的"快照",记录当前活跃事务列表,用于判断版本可见性 |
6.4.2 Read View可见性判断
事务读取某行数据时,通过Read View判断应该看到哪个版本(沿Undo Log版本链往回找):
- 如果版本的事务ID = 当前事务ID → 可见(自己的修改)
- 如果版本的事务ID < 当前活跃事务的最小ID → 可见(该事务在Read View创建前已提交)
- 如果版本的事务ID > 当前活跃事务的最大ID → 不可见(该事务在Read View创建后开始)
- 如果版本的事务ID在活跃列表中 → 不可见(该事务未提交)
RC vs RR的Read View差异:
- RC(读已提交):每次SELECT都创建新的Read View,所以能读到其他事务已提交的修改
- RR(可重复读):事务中的第一次SELECT创建Read View,之后复用同一个,所以每次读到的数据一致
6.5 InnoDB锁机制
【面试必问】
6.5.1 锁的类型
| 锁类型 | 说明 | 互斥 |
|---|---|---|
| 共享锁(S锁 / 读锁) | 允许其他事务读,不允许写 | S与X互斥,S与S兼容 |
| 排他锁(X锁 / 写锁) | 不允许其他事务读或写 | X与S、X互斥 |
| 意向共享锁(IS) | 表级锁,表示打算在行上加S锁 | 表级 |
| 意向排他锁(IX) | 表级锁,表示打算在行上加X锁 | 表级 |
| 记录锁(Record Lock) | 锁定单行记录的索引 | 行级 |
| 间隙锁(Gap Lock) | 锁定索引记录之间的间隙 | 防止插入,RR级别特有 |
| 临键锁(Next-Key Lock) | 记录锁 + 间隙锁的组合 | InnoDB RR级别的默认行锁 |
sql
-- 显式加共享锁
SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; -- MySQL 8.0
-- 或
SELECT * FROM student WHERE id=1 FOR SHARE;
-- 显式加排他锁
SELECT * FROM student WHERE id=1 FOR UPDATE;
【面试必问】间隙锁(Gap Lock)什么时候触发:
- 仅在RR隔离级别下生效
- 对索引列进行等值查询时,如果值不存在,会在该值应该插入的位置加间隙锁
- 对索引列进行范围查询时,会在范围的边界加间隙锁
- 目的:防止幻读(阻止其他事务在间隙中INSERT)
6.5.2 死锁排查
sql
-- 查看当前锁等待
SELECT * FROM information_schema.INNODB_TRX;
-- 查看锁等待关系
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 查看当前持有的锁
SELECT * FROM information_schema.INNODB_LOCKS;
-- 查看最近一次死锁信息
SHOW ENGINE INNODB STATUS\G
-- 搜索 "LATEST DETECTED DEADLOCK" 部分
-- 查看当前所有连接
SHOW PROCESSLIST;
-- 杀掉指定连接
KILL <connection_id>;
避免死锁的实践:
- 不同事务按相同顺序访问表和行
- 事务尽量简短(减少持有锁的时间)
- 合理使用索引(避免锁升级为表锁)
- 大事务拆分为小事务
第7章:存储引擎、日志系统与性能优化基础
7.1 存储引擎概述
存储引擎是MySQL的插件式数据存储层,决定了表的存储方式、是否支持事务、锁粒度等。InnoDB自MySQL 5.5起成为默认引擎。
sql
-- 查看支持的引擎
SHOW ENGINES;
-- 查看某张表使用的引擎
SHOW TABLE STATUS LIKE 'student';
-- 修改表的引擎
ALTER TABLE student ENGINE=MyISAM;
7.2 InnoDB vs MyISAM
【面试必问】
| 特性 | InnoDB | MyISAM |
|---|---|---|
| 事务 | ✅ 支持(ACID) | ❌ 不支持 |
| 锁粒度 | 行级锁 | 表级锁 |
| 外键 | ✅ 支持 | ❌ 不支持 |
| 崩溃恢复 | ✅ Redo Log自动恢复 | ⚠️ 可能损坏,需手动修复 |
| MVCC | ✅ 支持高并发读写 | ❌ 写操作阻塞所有读 |
| 全文索引 | MySQL 5.6+ 支持 | ✅ 原生支持 |
| 数据存储 | 共享表空间(ibdata1)或独立表空间(.ibd) | .MYD(数据) + .MYI(索引) |
| 行数统计 | 需全表扫描COUNT(*) | 有计数器,即时返回 |
| 适用场景 | 需要事务、高并发、崩溃恢复的生产环境 | 只读/极少写的日志表、数据仓库 |
为什么MySQL 8.0彻底淘汰MyISAM:MyISAM不支持事务,崩溃后数据可能损坏且无法自动恢复。所有生产场景都应使用InnoDB(MySQL 8.0中系统表也已全部转为InnoDB)。
7.3 InnoDB内存结构
【面试必问】
| 内存区域 | 大小参数 | 作用 |
|---|---|---|
| Buffer Pool | innodb_buffer_pool_size |
缓存数据页和索引页,InnoDB最重要的内存区域,建议设为物理内存的50%-70% |
| Change Buffer | innodb_change_buffer_max_size |
缓存对二级索引的修改,延迟合并写入 |
| Adaptive Hash Index | 自动 | 对热点数据页自动建Hash索引,加速等值查询 |
| Log Buffer | innodb_log_buffer_size |
Redo Log的内存缓冲区,默认16MB |
为什么Buffer Pool设太大也不好 :过大导致OS可用内存不足,可能触发SWAP,性能断崖式下降。生产环境建议:
innodb_buffer_pool_size = 物理内存 × 0.6,并监控SHOW ENGINE INNODB STATUS中的 Buffer Pool 命中率(应 > 99%)。
7.4 Redo Log(重做日志)
【面试必问】
Redo Log是InnoDB实现**持久性(Durability)**的关键。记录的是"对数据页做了什么物理修改"。
写数据流程:
1. 修改Buffer Pool中的数据页
2. 写入Log Buffer(内存 → Redo Log Buffer)
3. 事务提交时:Redo Log Buffer → OS Cache → 磁盘(受innodb_flush_log_at_trx_commit控制)
4. 后台线程定期将Buffer Pool中的脏页刷回数据文件(Checkpoint)
WAL(Write-Ahead Logging):先写日志,后写数据。即使数据库崩溃,重启后通过Redo Log重放已提交但未写入数据文件的事务,保证数据不丢。
Redo Log vs Binlog:
| 维度 | Redo Log | Binlog |
|---|---|---|
| 所属 | InnoDB引擎层 | MySQL Server层 |
| 内容 | 物理日志(数据页修改) | 逻辑日志(SQL语句/行变更) |
| 写入方式 | 循环写入(ib_logfile0/1两个文件轮回) | 追加写入(可生成多个文件) |
| 用途 | 崩溃恢复(Crash Recovery) | 主从复制、时间点恢复 |
| 大小 | 固定(默认2×48MB) | 可配(max_binlog_size) |
【面试必问】两阶段提交(Two-Phase Commit):Redo Log和Binlog的一致性由两阶段提交保证:
- Prepare阶段:写入Redo Log(标记为prepare状态)
- Commit阶段:写入Binlog → 将Redo Log标记为commit
崩溃恢复时:如果Redo Log是prepare状态但Binlog中没有对应记录,事务回滚;如果Binlog中有记录,事务提交。这保证了主从数据一致。
7.5 Undo Log(回滚日志)
Undo Log记录的是"数据修改前的版本",用于事务回滚和MVCC。
- 回滚:执行ROLLBACK时,用Undo Log中的旧值覆盖
- MVCC:其他事务读数据时,通过Undo Log找到可见的历史版本
- Undo Log也记录Redo Log(Undo的修改也需要持久化)
7.6 慢查询日志与SQL优化
配置慢查询日志
sql
-- 查看慢查询配置
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
-- 动态开启(重启失效)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2; -- 超过2秒记录
ini
# my.cnf 永久配置
slow_query_log = ON
slow_query_log_file = /var/log/mysql-slow.log
long_query_time = 2
log_queries_not_using_indexes = ON # 记录未使用索引的查询
分析慢查询
bash
# mysqldumpslow 分析
# 按查询时间排序,取前10条
mysqldumpslow -s t -t 10 /var/log/mysql-slow.log
# 按返回行数排序
mysqldumpslow -s r -t 10 /var/log/mysql-slow.log
# 按出现次数排序
mysqldumpslow -s c -t 10 /var/log/mysql-slow.log
SQL优化思路
sql
-- 1. 先用EXPLAIN看执行计划
EXPLAIN SELECT * FROM student WHERE name = '张三';
-- 2. 查看实际执行成本
EXPLAIN FORMAT=JSON SELECT * FROM student WHERE name = '张三';
-- 3. 看是否使用了索引、type是否为ALL
-- 4. 检查Extra是否有 Using filesort / Using temporary
-- 5. 查看索引使用情况
SELECT * FROM sys.schema_unused_indexes; -- 未使用的索引(建议删除)
SELECT * FROM sys.schema_redundant_indexes; -- 冗余索引
通用SQL优化原则:
- 最左匹配:联合索引必须从最左列开始匹配
- 避免SELECT *:只查需要的列,争取覆盖索引
- 小表驱动大表:JOIN时把小结果集放在前面(驱动表)
- 避免前导通配符 :
LIKE '%abc'不走索引 - 用UNION ALL代替OR(非索引列场景)
- LIMIT分页优化 :大偏移量时用
WHERE id > last_id LIMIT n代替LIMIT m, n
7.7 性能监控命令
sql
-- MySQL 运行状态概览
SHOW GLOBAL STATUS;
-- 关键指标
SHOW GLOBAL STATUS LIKE 'Threads_connected'; -- 当前连接数
SHOW GLOBAL STATUS LIKE 'Threads_running'; -- 活跃连接数
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests'; -- 逻辑读
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; -- 物理读(从磁盘读)
-- 缓冲池命中率 = (Read_requests - Reads) / Read_requests (应 > 99%)
SHOW GLOBAL STATUS LIKE 'Innodb_rows_deleted';
SHOW GLOBAL STATUS LIKE 'Innodb_rows_inserted';
SHOW GLOBAL STATUS LIKE 'Innodb_rows_read';
SHOW GLOBAL STATUS LIKE 'Innodb_rows_updated';
-- QPS / TPS 估算(需两次采样计算差值)
-- QPS ≈ (Questions差值) / 时间间隔
-- TPS ≈ (Com_commit + Com_rollback 差值) / 时间间隔
第8章:主从复制与MaxScale读写分离
8.1 主从复制原理
【面试必问】主从复制完整流程:
Master(主库) Slave(从库)
│ │
│ ① 数据变更写入 binlog │
├─ binlog ──────────────────────────────► │
│ │ ② IO线程拉取binlog
│ │ 写入relay log
│ │
│ │ ③ SQL线程读取relay log
│ │ 重放SQL到从库
四步详解:
- Master 将数据变更写入
binlog(二进制日志) - Slave IO线程 连接到Master,从指定位置开始拉取binlog,写入本地的
relay log(中继日志) - Slave SQL线程 读取relay log中的事件,在从库上重放执行
- 复制持续进行,IO线程和SQL线程各有一个独立的状态
8.2 主从复制配置
四要素:
- 机器:Master (192.168.100.165) + Slave (192.168.100.166)
- 做什么:搭建MySQL异步主从复制
- 完整命令:如下
- 为什么:实现读写分离(读写压力分散)、数据备份(从库可做备份)、高可用基础
环境准备
bash
# === 两台机器都执行 ===
# 1. 时间同步(chrony)
systemctl enable --now chronyd
# 2. 关闭防火墙/SELinux(或开放3306端口)
systemctl stop firewalld; systemctl disable firewalld
setenforce 0
# 永久关闭:编辑 /etc/selinux/config → SELINUX=disabled
Master配置(192.168.100.165)
bash
# 编辑 /etc/my.cnf,[mysqld] 段添加:
cat >> /etc/my.cnf << 'EOF'
[mysqld]
server-id=1
log-bin=mysql-bin
binlog_format=ROW
log-slave-updates=ON # 启用后从库的更新也写binlog(级联复制需要)
expire_logs_days=7
EOF
systemctl restart mysqld
sql
-- Master创建复制专用用户
CREATE USER 'repl'@'192.168.100.%' IDENTIFIED WITH mysql_native_password BY 'ReplP@ss123';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.100.%';
FLUSH PRIVILEGES;
-- 查看Master状态(记录 File 和 Position,Slave配置时需要)
SHOW MASTER STATUS;
-- +------------------+----------+
-- | File | Position |
-- +------------------+----------+
-- | mysql-bin.000001 | 856 |
-- +------------------+----------+
Slave配置(192.168.100.166)
bash
# 编辑 /etc/my.cnf,[mysqld] 段添加:
[mysqld]
server-id=2
relay-log=relay-log-bin # 中继日志前缀
relay-log-index=relay-log-bin.index
read_only=ON # 从库只读(root和复制线程不受限制)
sql
-- 在Slave上执行配置复制
CHANGE MASTER TO
MASTER_HOST='192.168.100.165',
MASTER_USER='repl',
MASTER_PASSWORD='ReplP@ss123',
MASTER_LOG_FILE='mysql-bin.000001', -- 来自 SHOW MASTER STATUS
MASTER_LOG_POS=856; -- 来自 SHOW MASTER STATUS
-- 启动复制
START SLAVE;
-- 查看复制状态(\G 纵向显示)
SHOW SLAVE STATUS\G
关键状态字段
sql
SHOW SLAVE STATUS\G
| 字段 | 期望值 | 非正常状态原因 |
|---|---|---|
| Slave_IO_Running | Yes | Connecting→网络不通、用户名密码错误、防火墙 |
| Slave_SQL_Running | Yes | No→relay log重放出错(主键冲突/表不存在等) |
| Seconds_Behind_Master | 0或接近0 | 延迟过大→大事务、SQL线程单线程瓶颈 |
| Master_Log_File | 当前binlog文件 | --- |
| Read_Master_Log_Pos | 当前读取位置 | --- |
| Relay_Log_File | 当前relay log | --- |
| Last_IO_Errno | 0 | 非0→查看Last_IO_Error |
| Last_SQL_Errno | 0 | 非0→查看Last_SQL_Error |
8.3 复制常见问题与修复
IO线程连接失败
bash
# 错误:Last_IO_Errno: 2003 - Can't connect to MySQL server
# 原因:网络不通或防火墙
# 排查:
ping 192.168.100.165
telnet 192.168.100.165 3306
# 错误:Last_IO_Errno: 1045 - Access denied
# 原因:复制用户密码错误或权限不足
# 解决:在Master重新创建用户并授权
SQL线程出错
bash
# 常见错误:主键冲突(Slave上有重复数据)
# 解决:
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; -- 跳过一个事件
START SLAVE;
# 更安全的做法(MySQL 8.0.26+)
STOP SLAVE;
CHANGE REPLICATION SOURCE TO SOURCE_LOG_FILE='...', SOURCE_LOG_POS=...;
START SLAVE;
复制延迟排查
sql
-- 查看延迟
SHOW SLAVE STATUS\G -- 看 Seconds_Behind_Master
-- 原因分析:
-- 1. 大事务(一次性更新百万行)
-- 2. Slave硬件性能低于Master
-- 3. SQL线程单线程瓶颈(MySQL 5.6并行复制可缓解)
-- 4. 锁等待
-- 并行复制配置(MySQL 5.7+)
SET GLOBAL slave_parallel_type = LOGICAL_CLOCK;
SET GLOBAL slave_parallel_workers = 4;
8.4 半同步复制
【知识拓展】 默认复制是异步的,Master提交事务后不等待Slave确认。半同步复制保证至少一个Slave收到binlog后才返回客户端。
sql
-- Master安装插件
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
-- Slave安装插件
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
-- 启用
SET GLOBAL rpl_semi_sync_master_enabled = ON;
SET GLOBAL rpl_semi_sync_slave_enabled = ON;
-- 查看状态
SHOW STATUS LIKE 'Rpl_semi_sync%';
8.5 MaxScale读写分离
【教案补充】 MaxScale是MariaDB开发的数据库代理,实现读写分离和负载均衡。
四要素:
- 机器:MaxScale代理服务器(192.168.100.170)
- 做什么:部署MaxScale实现对MySQL主从集群的读写分离
- 完整命令:如下
- 为什么:应用层不需要关心读写路由,MaxScale自动分离读写请求,写走Master、读走Slave
安装MaxScale
bash
# OpenEuler 24.03 / CentOS 8+
dnf install -y maxscale
# 创建监控用户(在Master上执行)
# mysql -uroot -p
# CREATE USER 'maxmon'@'192.168.100.%' IDENTIFIED BY 'MonitorP@ss';
# GRANT SELECT ON mysql.* TO 'maxmon'@'192.168.100.%';
# GRANT SHOW DATABASES ON *.* TO 'maxmon'@'192.168.100.%';
配置MaxScale
bash
# 编辑 /etc/maxscale.cnf
cat > /etc/maxscale.cnf << 'EOF'
[maxscale]
threads=auto
# 定义Master服务器
[master-server]
type=server
address=192.168.100.165
port=3306
protocol=MariaDBBackend
# 定义Slave服务器
[slave-server]
type=server
address=192.168.100.166
port=3306
protocol=MariaDBBackend
# 监控模块
[MySQL-Monitor]
type=monitor
module=mariadbmon
servers=master-server,slave-server
user=maxmon
password=MonitorP@ss
monitor_interval=2000
# 读写分离路由
[Read-Write-Service]
type=service
router=readwritesplit
servers=master-server,slave-server
user=maxmon
password=MonitorP@ss
# 监听端口
[Read-Write-Listener]
type=listener
service=Read-Write-Service
protocol=MariaDBClient
port=3306
EOF
systemctl enable --now maxscale
验证读写分离
bash
# 连接MaxScale(3306端口)
mysql -h 192.168.100.170 -u appuser -p -P 3306
# 写操作:自动路由到Master
INSERT INTO student(name) VALUES('maxscale测试');
# 读操作:自动路由到Slave
SELECT * FROM student;
readwritesplit工作原理:
- 以
SELECT开头的语句 → 路由到SlaveINSERT/UPDATE/DELETE/CREATE/ALTER/...→ 路由到Master- 事务中的语句(
BEGIN...COMMIT)→ 全部路由到Master- 带
FOR UPDATE/LOCK IN SHARE MODE的SELECT → 路由到Master
第9章:MySQL高可用架构(Galera Cluster / MGR / TLS加密)
9.1 高可用架构概述
生产环境不能依赖单点MySQL,高可用(High Availability, HA)确保单节点故障时服务不中断。
| 架构 | 原理 | 一致性 | 多主写入 | 复杂度 | 适用 |
|---|---|---|---|---|---|
| 主从+手动切换 | binlog异步复制 | 弱 | 否 | 低 | 开发/测试 |
| MHA | 故障自动检测+切换 | 弱 | 否 | 中 | 互联网通用 |
| Galera Cluster | 同步复制(wsrep) | 强 | ✅ | 中 | 国产生态/需要多主 |
| 组复制MGR | Paxos协议 | 强 | 可选 | 高 | 金融级一致性 |
| Orchestrator + VIP | 自动故障切换 | 弱/半同步 | 否 | 高 | 大型互联网 |
9.2 Galera Cluster
【面试必问】Galera vs 主从复制:
| 维度 | 传统主从 | Galera Cluster |
|---|---|---|
| 复制方式 | binlog异步/半同步 | wsrep同步复制(基于写集) |
| 主从关系 | Master-Slave(单向) | 多主(Multi-Master),任意节点可读写 |
| 数据一致性 | 可能不一致 | 强一致性(所有节点确认后才提交) |
| 故障切换 | 需手动+MHA | 自动,无单点故障 |
| 扩容 | 增加Slave | 添加节点,自动同步 |
| 适用场景 | 读写分离、备份 | 多活、跨机房、需要多主写入 |
Galera原理(wsrep API):事务在本地执行后,将"写集"(Write Set)广播给集群中所有节点,每个节点验证并应用。只有所有节点确认后事务才提交(基于全局事务ID的certification机制)。
Galera Cluster部署
四要素:
- 机器:3台(最少)--- node1: 192.168.100.165 / node2: 192.168.100.166 / node3: 192.168.100.167
- 做什么:部署Galera Cluster多主MySQL集群
- 完整命令:如下
- 为什么:实现多主写入、自动故障切换、强一致性
bash
# === 三台机器都执行 ===
# 环境准备
systemctl stop firewalld; systemctl disable firewalld
setenforce 0
systemctl stop mysqld # 如果已有MySQL
# 安装 Galera(以MariaDB Galera为例)
dnf install -y mariadb-server-galera
# 编辑配置文件 /etc/my.cnf.d/galera.cnf
cat > /etc/my.cnf.d/galera.cnf << 'EOF'
[mysqld]
bind-address=0.0.0.0
# Galera Provider
wsrep_on=ON
wsrep_provider=/usr/lib64/galera-4/libgalera_smm.so
# 集群配置
wsrep_cluster_name="my_galera_cluster"
wsrep_cluster_address="gcomm://192.168.100.165,192.168.100.166,192.168.100.167"
# 节点信息(每个节点不同:node1=165, node2=166, node3=167)
wsrep_node_name="node1"
wsrep_node_address="192.168.100.165"
# 复制设置
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
innodb_flush_log_at_trx_commit=2
EOF
# === 首次启动(仅在node1执行)===
galera_new_cluster
# === 其他节点启动 ===
systemctl start mariadb
# === 验证集群 ===
mysql -uroot -p -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
# wsrep_cluster_size: 3 -- 集群节点数
mysql -uroot -p -e "SHOW STATUS LIKE 'wsrep_ready';"
# wsrep_ready: ON -- 集群就绪
mysql -uroot -p -e "SHOW STATUS LIKE 'wsrep_%';"
关键wsrep状态变量:
| 变量 | 含义 |
|---|---|
| wsrep_cluster_size | 集群中的节点数 |
| wsrep_ready | 节点是否就绪 |
| wsrep_connected | 是否连接到集群 |
| wsrep_local_state_comment | 节点状态(Synced/Donor/Joining) |
| wsrep_flow_control_paused | 流控暂停时间(应接近0) |
Galera注意事项
- 仅支持InnoDB引擎(MyISAM表无法同步)
- 所有表必须有主键(没有主键会导致DELETE出错)
- 不支持LOCK TABLES / GET_LOCK()
- 大事务影响性能(写集需要在所有节点验证)
- 最少3节点(2节点有脑裂风险)
9.3 MySQL组复制(MGR)
【知识拓展】 MySQL Group Replication(MGR)是MySQL 5.7.17+内置的高可用方案,基于Paxos协议实现。
| 模式 | 写入 | 故障切换 |
|---|---|---|
| 单主模式 | 只有Primary可写,其他只读 | Primary故障自动选新Primary |
| 多主模式 | 所有节点可写 | 任意节点故障不影响集群 |
sql
-- MGR搭建关键步骤(3节点)
-- 1. 安装组复制插件
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
-- 2. 配置my.cnf(关键参数)
-- loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
-- loose-group_replication_local_address="192.168.100.165:33061"
-- loose-group_replication_group_seeds="192.168.100.165:33061,192.168.100.166:33061,192.168.100.167:33061"
-- loose-group_replication_bootstrap_group=OFF
-- loose-group_replication_single_primary_mode=ON
-- 3. 创建复制用户 + 设置通道
-- CHANGE MASTER TO ... FOR CHANNEL 'group_replication_recovery';
-- 4. 启动组复制(首个节点需bootstrap)
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
-- 其他节点
START GROUP_REPLICATION;
-- 查看集群状态
SELECT * FROM performance_schema.replication_group_members;
9.4 MySQL TLS加密配置
四要素:
- 机器:MySQL服务器
- 做什么:配置MySQL TLS加密,强制所有连接使用SSL
- 完整命令:如下
- 为什么:防止数据在网络传输中被窃听,是安全合规的必备措施
生成证书
bash
# 1. 生成CA私钥和证书
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem \
-subj "/CN=MySQL_CA"
# 2. 生成服务端私钥和证书
openssl req -newkey rsa:2048 -days 3650 -nodes \
-keyout server-key.pem -out server-req.pem \
-subj "/CN=mysql-server"
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -days 3650 -in server-req.pem \
-CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert.pem
# 3. 生成客户端私钥和证书
openssl req -newkey rsa:2048 -days 3650 -nodes \
-keyout client-key.pem -out client-req.pem \
-subj "/CN=mysql-client"
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -days 3650 -in client-req.pem \
-CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \
-out client-cert.pem
# 4. 部署证书到MySQL数据目录
cp ca-cert.pem server-cert.pem server-key.pem /var/lib/mysql/
chown mysql:mysql /var/lib/mysql/*.pem
chmod 600 /var/lib/mysql/*.pem
配置MySQL启用SSL
ini
# my.cnf
[mysqld]
ssl-ca=/var/lib/mysql/ca-cert.pem
ssl-cert=/var/lib/mysql/server-cert.pem
ssl-key=/var/lib/mysql/server-key.pem
sql
-- 重启后验证SSL是否启用
SHOW VARIABLES LIKE '%ssl%';
-- have_ssl: YES
-- 创建必须使用SSL连接的用户
CREATE USER 'secure_user'@'%' IDENTIFIED BY 'StrongP@ss' REQUIRE SSL;
-- 创建使用X509证书认证的用户
CREATE USER 'cert_user'@'%' IDENTIFIED BY 'StrongP@ss' REQUIRE X509;
客户端SSL连接
bash
# 客户端使用SSL连接
mysql -h 192.168.100.165 -u secure_user -p \
--ssl-ca=client-cert.pem \
--ssl-cert=client-cert.pem \
--ssl-key=client-key.pem
# 验证当前连接是否使用SSL
mysql> \s
# SSL: Cipher in use is TLS_AES_256_GCM_SHA384
# 或
mysql> SHOW STATUS LIKE 'Ssl_cipher';
第10章:分库分表策略、大表DDL、故障排查与安全加固
10.1 分库分表概述
当单表数据量突破千万级或单库无法承载写入压力时,需要对数据进行拆分。
拆分方式
| 维度 | 垂直拆分 | 水平拆分 |
|---|---|---|
| 操作 | 按业务模块拆库/按字段拆分表 | 按行拆分到多个库/表 |
| 示例 | 用户库、订单库、商品库独立 | user_0, user_1, user_2... user_1023 |
| 解决问题 | 单库压力大、业务耦合 | 单表数据量过大、写入瓶颈 |
| 实现方式 | 应用层路由 | MyCat/ShardingSphere中间件 |
【面试必问】分库分表后面临的挑战:
| 挑战 | 解决方案 |
|---|---|
| 跨库Join | 应用层聚合(多次查询+代码合并)或冗余字段设计 |
| 分布式事务 | XA协议 / TCC / 最终一致性 + 消息队列 |
| 全局唯一ID | 雪花算法(Snowflake)/ 号段模式 / Redis自增 |
| 分片键选择 | 选择查询中最常带的条件作为分片键,避免跨片查询 |
| 扩容迁移 | 一致性Hash / 双写+灰度迁移 |
10.2 MyCat中间件分片
【教案补充】 MyCat是开源的数据库中间件,实现分库分表和读写分离。
MyCat核心配置
xml
<!-- schema.xml -->
<schema name="order_db" checkSQLschema="false" sqlMaxLimit="100">
<!-- 分片表:按user_id取模分片 -->
<table name="t_order" dataNode="dn1,dn2" rule="mod-long" />
</schema>
<dataNode name="dn1" dataHost="host1" database="order_db_0" />
<dataNode name="dn2" dataHost="host2" database="order_db_1" />
<dataHost name="host1" maxCon="100" minCon="10" balance="0">
<writeHost host="master1" url="192.168.100.165:3306" user="mycat" password="xxx" />
</dataHost>
xml
<!-- rule.xml 分片规则 -->
<tableRule name="mod-long">
<rule>
<columns>user_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">2</property> <!-- 2个分片 -->
</function>
分布式事务---XA协议
sql
-- XA事务示例(两阶段提交 2PC)
XA START 'xid1';
INSERT INTO order_db_0.t_order VALUES(...);
XA END 'xid1';
XA PREPARE 'xid1';
XA START 'xid2';
INSERT INTO order_db_1.t_order VALUES(...);
XA END 'xid2';
XA PREPARE 'xid2';
-- 第一阶段全部成功 → 第二阶段提交
XA COMMIT 'xid1';
XA COMMIT 'xid2';
XA的局限性:性能开销大(需要多次网络通信)、单点故障(协调者故障时参与者可能阻塞)。
10.3 大表DDL修改
【面试必问】 直接对亿级数据的大表执行 ALTER TABLE ... ADD COLUMN 会锁表(取决于操作类型),导致业务阻塞。
DDL类型与锁行为:
| 操作 | 早期MySQL | MySQL 5.6+ Online DDL | 8.0 原子DDL |
|---|---|---|---|
| ADD COLUMN | 锁表 | 不锁(允许并发DML) | 不锁+原子 |
| ADD INDEX | 锁表 | 不锁 | 不锁+原子 |
| MODIFY COLUMN(改类型) | 锁表 | 锁表(需重建) | 锁表+原子 |
| DROP COLUMN | 锁表 | 不锁(仅改元数据) | 不锁+原子 |
pt-online-schema-change
对于不支持Online DDL的操作(如修改列类型),使用Percona Toolkit的 pt-online-schema-change(pt-osc):
bash
# 安装 Percona Toolkit
dnf install -y percona-toolkit
# pt-osc 原理:创建新表 → 逐批复制数据 → 原子切换
pt-online-schema-change \
--user=root --password='xxx' \
--alter "MODIFY COLUMN content TEXT" \
D=school,t=articles \
--execute
pt-osc原理:
- 创建一张与原表结构相同的新表(含修改后的列定义)
- 在原表上创建触发器(INSERT/UPDATE/DELETE),确保新写入不丢失
- 分批次(chunk)将原表数据复制到新表
- 复制完成后,原子性地执行
RENAME TABLE交换新旧表 - 删除旧表和触发器
注意事项:需要有足够的磁盘空间(约原表大小的2倍)、原表必须有主键或唯一索引、操作期间不能对原表执行DDL。
10.4 MySQL故障排查
【教案补充】 常见MySQL故障类型及排查命令。
十大故障类型
| 故障类型 | 常见现象 | 排查命令 |
|---|---|---|
| 1. 连接故障 | Too many connections |
SHOW PROCESSLIST; SHOW VARIABLES LIKE 'max_connections'; |
| 2. 性能故障 | 查询慢、CPU高 | SHOW PROCESSLIST; EXPLAIN 慢查询 top/htop |
| 3. 复制故障 | Slave延迟/报错 | SHOW SLAVE STATUS\G |
| 4. 数据恢复故障 | binlog找不到 | SHOW MASTER STATUS; SHOW BINARY LOGS; |
| 5. 高可用故障 | 集群脑裂 | wsrep_cluster_status replication_group_members |
| 6. 存储引擎故障 | 表损坏 | CHECK TABLE xxx; REPAIR TABLE xxx;(MyISAM) |
| 7. 内存故障 | OOM、SWAP | free -h SHOW ENGINE INNODB STATUS\G |
| 8. 安全故障 | 暴力破解 | grep 'Access denied' /var/log/mysqld.log |
| 9. 备份故障 | 备份失败/恢复失败 | xtrabackup日志、mysqldump错误输出 |
| 10. 升级故障 | 升级后无法启动 | 错误日志 /var/log/mysqld.log |
通用排查流程
bash
# 1. 查看错误日志(第一优先)
tail -100 /var/log/mysqld.log
# 2. 查看当前连接
mysql -uroot -p -e "SHOW PROCESSLIST;"
# 3. 查看锁等待
mysql -uroot -p -e "SELECT * FROM information_schema.INNODB_TRX;"
# 4. 查看服务状态
systemctl status mysqld
# 5. 查看系统资源
top -bn1 | head -20
df -h # 磁盘空间
free -h # 内存
iostat -x 1 3 # 磁盘IO
常见故障解决方案
sql
-- 连接数满
SET GLOBAL max_connections = 1000;
-- 杀掉长时间运行的查询
SELECT CONCAT('KILL ', id, ';') FROM information_schema.PROCESSLIST
WHERE time > 60 AND command != 'Sleep';
-- 跳过复制错误
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;
-- 表损坏修复(MyISAM)
REPAIR TABLE table_name;
10.5 MySQL安全加固
【教案补充】安全基线检查清单:
认证安全
-
移除匿名用户:
DELETE FROM mysql.user WHERE user=''; -
删除test数据库:
DROP DATABASE IF EXISTS test; -
禁止root远程登录:
DELETE FROM mysql.user WHERE user='root' AND host!='localhost'; -
强制使用强密码策略:
sqlSET GLOBAL validate_password.length=12; SET GLOBAL validate_password.mixed_case_count=1; SET GLOBAL validate_password.number_count=1; SET GLOBAL validate_password.special_char_count=1;
网络安全
- 绑定本地IP:
bind-address = 192.168.100.165 - 修改默认端口(可选):
port = 3307 - 限制单用户连接数:
max_user_connections = 50 - 启用防火墙只开放必要IP访问3306
数据安全
- 启用TLS加密传输(见第9章)
-
local_infile = OFF(禁止LOAD DATA LOCAL) -
secure_file_priv = /backup(限制文件读写目录) - binlog加密(MySQL 8.0.14+):
binlog_encryption = ON
审计与日志
- 开启错误日志:
log_error = /var/log/mysqld.log - 开启慢查询日志
- 开启通用查询日志(仅在排查时,平时关闭):
general_log = OFF - 定期审计用户权限:
SELECT * FROM mysql.user;
10.6 灾难恢复流程
灾难发生
│
├─ 1. 止损:断开问题节点/切换备用节点
│
├─ 2. 评估:确认数据损坏范围和时间点
│
├─ 3. 恢复:
│ ├─ 有XtraBackup → 恢复最近全量 → 合并增量 → 应用binlog到指定时间点
│ └─ 无XtraBackup → mysqldump恢复 → 应用binlog
│
├─ 4. 验证:检查关键表数据完整性、行数
│
└─ 5. 回归:切换流量回原节点/新节点,监控运行
bash
# 完整恢复命令示例
# 恢复全量
xtrabackup --copy-back --target-dir=/backup/full_20260607
# 恢复增量
xtrabackup --prepare --target-dir=/backup/full_20260607 --incremental-dir=/backup/inc_20260607_12
# 应用binlog到误操作前一刻
mysqlbinlog --stop-datetime="2026-06-07 14:30:00" mysql-bin.000010 | mysql -uroot -p
# 启动MySQL
systemctl start mysqld
附录A:面试必问知识点速查
| 排序 | 知识点 | 章节 | 考察频率 |
|---|---|---|---|
| 1 | B+Tree索引原理、最左匹配、索引失效 | 第5章 | 11.18% |
| 2 | SQL优化、EXPLAIN执行计划 | 第5/7章 | 6.20% |
| 3 | 事务ACID与四种隔离级别 | 第6章 | 3.55% |
| 4 | InnoDB vs MyISAM | 第7章 | 2.73% |
| 5 | MVCC原理(Undo Log + Read View) | 第6章 | 1.97% |
| 6 | 锁机制(行锁/间隙锁/死锁) | 第6章 | 1.67% |
| 7 | 主从复制原理与状态 | 第8章 | 高频 |
| 8 | Redo Log vs Binlog、两阶段提交 | 第7章 | 高频 |
| 9 | 分库分表策略与挑战 | 第10章 | 中频 |
| 10 | Galera vs MGR高可用对比 | 第9章 | 中频 |
附录B:MySQL DBA学习路径
| 阶段 | 内容 | 来源 |
|---|---|---|
| 初级 | SQL语言、数据类型、库表操作、用户权限 | 第1-3章 |
| 中级 | 索引优化、事务锁、备份恢复、主从复制 | 第4-8章 |
| 高级 | 高可用(Galera/MGR)、分库分表、性能调优、故障排查、安全加固 | 第9-10章 |
| 专家 | 内核源码、InnoDB存储引擎深度、分布式数据库、全链路压测 | 进阶路线 |