05.项目五 MySQL数据库安全基础
05 项目五 MySQL 数据库安全基础
Section titled “05 项目五 MySQL 数据库安全基础”🎯 本项目学习目标
- 能在 Ubuntu 24.04 LTS 环境下完成 MySQL 8.0 的安装、启动与基础验证
- 能理解 Ubuntu 默认认证方式,并完成 root 初始登录与基础安全加固
- 能基于内网场景创建受限账号,并配置远程访问权限
- 能按最小权限原则完成账号授权、查询、撤销与删除
- 能识别错误日志、慢查询日志、通用查询日志和 binlog 的用途,并完成 binlog 开启与基础恢复思路演练
第 1 课 安装与验证:让 MySQL “能跑起来”
Section titled “第 1 课 安装与验证:让 MySQL “能跑起来””1.1 本课要解决的问题
Section titled “1.1 本课要解决的问题”把 MySQL 在 Ubuntu 24.04 上装起来,并能验证:服务在跑、能登录、能看到版本与数据库列表。
1.2 本课交付物
Section titled “1.2 本课交付物”mysql --version输出sudo systemctl status mysql --no-pager显示 active(running)- MySQL 内执行成功:
SELECT VERSION();SHOW DATABASES;
1.3 MySQL 简介
Section titled “1.3 MySQL 简介”MySQL 是世界上最流行的开源关系型数据库管理系统(RDBMS),大量 Web 系统使用它作为核心数据存储。安全运维从安装开始:安装 ≠ 安全,安装只是第一步。
MySQL 8.0 核心特性速览
Section titled “MySQL 8.0 核心特性速览”| 特性 | 说明 |
|---|---|
| 默认认证插件 | caching_sha2_password(取代旧版 mysql_native_password) |
| 默认字符集 | utf8mb4(支持 Emoji 等四字节字符) |
| 窗口函数 | ROW_NUMBER()、RANK() 等分析函数 |
| CTE(公共表表达式) | WITH ... AS 语法,提升复杂查询可读性 |
| 角色管理 | CREATE ROLE / GRANT role TO user,简化批量权限管理 |
| 数据字典 | 元数据存储在 InnoDB 表中,不再依赖 .frm 文件 |
1.4 安装前准备:拍快照 + 换源(Ubuntu 24.04)
Section titled “1.4 安装前准备:拍快照 + 换源(Ubuntu 24.04)”第 1 步:拍摄 VMware 快照(安全网)
Section titled “第 1 步:拍摄 VMware 快照(安全网)”第 2 步:换源加速
Section titled “第 2 步:换源加速”-
Ubuntu 24.04 LTS 换源(DEB822 格式)
Ubuntu 24.04 使用
/etc/apt/sources.list.d/ubuntu.sources(DEB822 格式)。Terminal window # 1) 备份原文件sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.bak# 2) 编辑配置文件sudo nano /etc/apt/sources.list.d/ubuntu.sources将文件内容全部替换为以下内容(阿里云镜像示例):
Types: debURIs: https://mirrors.aliyun.com/ubuntuSuites: noble noble-updates noble-backportsComponents: main restricted universe multiverseSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpgTypes: debURIs: https://mirrors.aliyun.com/ubuntuSuites: noble-securityComponents: main restricted universe multiverseSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg保存退出后更新索引:
Terminal window sudo apt update -
Ubuntu 22.04 LTS 换源(传统 sources.list 格式)
Ubuntu 22.04 使用传统的
/etc/apt/sources.list文件。Terminal window # 1) 备份原文件sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak# 2) 编辑配置文件sudo nano /etc/apt/sources.list将文件内容全部替换为以下内容(阿里云镜像示例):
deb https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiversedeb https://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiversedeb https://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiversedeb https://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse保存退出后更新索引:
Terminal window sudo apt update
第 3 步:检查系统时间与网络
Section titled “第 3 步:检查系统时间与网络”MySQL 安全运维会频繁涉及日志时间、远程连接和软件源访问。安装前建议先确认系统时间和网络状态:
# 查看系统时间与时区timedatectl
# 若时区不是 Asia/Shanghai,可设置为上海时区sudo timedatectl set-timezone Asia/Shanghai
# 测试网络连通性ping -c 4 mirrors.aliyun.com1.5 安装与验证
Section titled “1.5 安装与验证”# 0) 更新索引sudo apt update
# 1) 安装 MySQL Serversudo apt install -y mysql-server
# 2) 启动并设置开机自启sudo systemctl enable --now mysql
# 3) 验证版本与服务状态mysql --versionsudo systemctl status mysql --no-pager第 4 步:运行安全加固向导
Section titled “第 4 步:运行安全加固向导”sudo mysql_secure_installation第 5 步:首次登录验证
Section titled “第 5 步:首次登录验证”# 方式一:如果刚才已将 root 改为密码认证,用密码登录mysql -u root -p123456
# 方式二:如果还没改认证插件,用 sudo 登录sudo mysqlSELECT VERSION();SHOW DATABASES;exit;第 2 课 配置文件与安全基线:把”边界”和”默认坑”补齐
Section titled “第 2 课 配置文件与安全基线:把”边界”和”默认坑”补齐”2.1 与上一课的衔接(过渡)
Section titled “2.1 与上一课的衔接(过渡)”我们已经让 MySQL 能跑起来。下一步要让它在正确的边界内运行:远程访问是否允许、字符集是否正确、时间是否一致。否则会出现:远程连不上、乱码、时间错位等常见问题。
2.2 本课要解决的问题
Section titled “2.2 本课要解决的问题”- 找到 Ubuntu 24.04 的主配置文件位置
- 完成三件”必改且可验证”的基线配置:bind-address / utf8mb4 / time_zone
- 修改后能重启并验证变量生效
2.3 配置文件入口与结构
Section titled “2.3 配置文件入口与结构”MySQL 配置文件采用 INI 格式。Ubuntu 24.04 的配置文件分散在多个路径,按优先级从高到低排列:
| 路径 | 说明 | 建议 |
|---|---|---|
/etc/mysql/my.cnf | 全局入口,通常只做 !includedir | 不要动 |
/etc/mysql/mysql.conf.d/mysqld.cnf | 服务端主配置 | 主要编辑这个文件 |
/etc/mysql/conf.d/ | 自定义补充配置(.cnf 后缀) | 可新建文件做模块化配置 |
~/.my.cnf | 用户级客户端配置 | 可选,简化个人操作 |
配置文件中有两种配置段:
[mysqld] # 服务端配置(mysqld 进程读取)[client] # 客户端配置(mysql、mysqldump 等工具读取)2.4 三件必改
Section titled “2.4 三件必改”A. 远程访问边界:bind-address
Section titled “A. 远程访问边界:bind-address”MySQL 默认只监听 127.0.0.1(本机回环),意味着其他机器无法连接。
[mysqld]bind-address = 0.0.0.0port = 3306| 值 | 含义 | 安全性 | 适用场景 |
|---|---|---|---|
127.0.0.1 | 仅本机可连接 | 最高 | 单机开发、不需要远程访问 |
0.0.0.0 | 监听所有网卡 | 需配合防火墙 | 内网实验、生产环境 |
| 具体内网 IP | 只监听指定网卡 | 中等 | 多网卡服务器、精细化控制 |
B. 字符集:utf8mb4(避免乱码 / Emoji 问题)
Section titled “B. 字符集:utf8mb4(避免乱码 / Emoji 问题)”[mysqld]character-set-server = utf8mb4collation-server = utf8mb4_unicode_ci
[client]default-character-set = utf8mb4C. 时区:+08:00(避免时间错 8 小时)
Section titled “C. 时区:+08:00(避免时间错 8 小时)”[mysqld]default-time-zone = '+08:00'2.5 重启与验证
Section titled “2.5 重启与验证”修改配置后必须重启服务才能生效:
sudo systemctl restart mysqlsudo systemctl status mysql --no-pager用 SHOW VARIABLES 逐项验证配置是否生效:
-- 验证绑定地址SHOW VARIABLES LIKE 'bind_address';
-- 验证字符集SHOW VARIABLES LIKE 'character_set_server';SHOW VARIABLES LIKE 'collation_server';
-- 验证时区SHOW VARIABLES LIKE 'time_zone';SELECT NOW();第 3 课 账号与权限:远程能连,但不用 root(最小权限)
Section titled “第 3 课 账号与权限:远程能连,但不用 root(最小权限)”3.1 与上一课的衔接(过渡)
Section titled “3.1 与上一课的衔接(过渡)”我们已经允许 MySQL 监听内网(bind-address),接下来要让宿主机的 Navicat 能连接,同时遵循数据库安全核心原则:不用 root,按最小权限创建业务账号。
3.2 本课要解决的问题
Section titled “3.2 本课要解决的问题”- 理解 MySQL 账户识别方式:
'用户名'@'主机' - 理解权限系统的四层结构(全局 → 数据库 → 表 → 列)
- 创建一个只允许内网网段连接的用户
- 给用户授予某个数据库范围内的最小权限
- 从宿主机 Navicat 远程登录验证
- 掌握权限变更的生效机制,知道什么时候需要
FLUSH PRIVILEGES - 掌握账号生命周期管理(查看、修改、撤销、删除)
3.3 账号身份:先看 'user'@'host'
Section titled “3.3 账号身份:先看 'user'@'host'”在 MySQL 中,账号不是单独的用户名,而是 用户名 + 来源主机 的组合:
'user'@'host'例如:
| 写法 | 含义 | 安全建议 |
|---|---|---|
'app'@'localhost' | 只允许本机连接 | 适合本机脚本或维护任务 |
'app'@'192.168.100.1' | 只允许宿主机这一台机器连接 | 最精确,推荐用于固定客户端 |
'app'@'192.168.100.%' | 允许 192.168.100.x 网段连接 | 适合课堂内网实验 |
'app'@'%' | 允许任何来源连接 | 风险高,生产环境避免使用 |
同一个用户名搭配不同主机,会被视为不同的账号。例如 'app'@'%' 和 'app'@'192.168.100.%' 是两条独立账号记录,密码和权限也可以不同。
3.4 MySQL 权限体系:再看”能做什么”
Section titled “3.4 MySQL 权限体系:再看”能做什么””MySQL 的权限不是”一个用户一个开关”,而是分层逐级检查的:
第一层:连接验证 → 用户名 + 主机名 是否存在?密码对不对? → 失败直接拒绝,不进入下一层
第二层:全局权限(mysql.user 表) → GRANT ALL ON *.* → 超级管理员才给,普通业务账号跳过
第三层:数据库级权限(mysql.db 表) → GRANT SELECT ON mydb.* → 大多数业务账号授在这一层
第四层:表级 / 列级权限(mysql.tables_priv / mysql.columns_priv) → GRANT SELECT ON mydb.orders → 更精细的访问控制权限授权时通常按全局 → 数据库 → 表 → 列逐步收窄;执行某条 SQL 时,MySQL 会综合匹配这些层级中的权限,只要没有找到可用授权就会拒绝。
| 分类 | 权限 | 说明 | 常见场景 |
|---|---|---|---|
| 数据操作 | SELECT | 读取数据 | 只读报表账号 |
INSERT | 插入数据 | 应用写入 | |
UPDATE | 修改数据 | 应用更新 | |
DELETE | 删除数据 | 应用删除 | |
| 结构管理 | CREATE | 创建数据库/表 | 开发者 |
ALTER | 修改表结构 | 开发者 | |
DROP | 删除数据库/表 | 生产环境慎给 | |
INDEX | 创建/删除索引 | 性能优化 | |
| 管理权限 | GRANT OPTION | 允许该用户授权给别人 | 仅限管理员 |
SUPER | 管理服务器级操作 | 仅限 DBA | |
PROCESS | 查看所有连接 | 排查问题 | |
FILE | 读写服务器文件 | 高危,不建议给 |
3.5 远程连接三要素(排错顺序)
Section titled “3.5 远程连接三要素(排错顺序)”3.6 创建数据库与远程账号(完整流程)
Section titled “3.6 创建数据库与远程账号(完整流程)”下面以数据库
stusta为例,账号只允许192.168.100.%网段连接(覆盖宿主机所在网段)。
在虚拟机中登录 MySQL:
sudo mysql-- 0) 降低密码策略(课堂环境必须,否则 123456 无法通过 MEDIUM 策略)-- 先检查密码验证组件是否已安装SELECT COMPONENT_ID, COMPONENT_URN FROM mysql.component;-- 如果没有 component_validate_password 的记录,才执行下面这行安装:--INSTALL COMPONENT 'file://component_validate_password';
--SET GLOBAL validate_password.policy = LOW;--SET GLOBAL validate_password.length = 6;
-- 1) 准备示例库和示例表CREATE DATABASE IF NOT EXISTS stusta;USE stusta;
CREATE TABLE IF NOT EXISTS students ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, score DECIMAL(5,2), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
INSERT INTO students (name, score) VALUES ('张三', 88.5), ('李四', 92.0), ('王五', 76.0);
-- 2) 创建远程账号(网段限制,使用 mysql_native_password 确保 Navicat 兼容)CREATE USER IF NOT EXISTS 'app'@'192.168.100.%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 3) 最小权限授权(只对 stusta.* 的 CRUD)GRANT SELECT, INSERT, UPDATE, DELETE ON stusta.* TO 'app'@'192.168.100.%';
-- 4) 验证权限SHOW GRANTS FOR 'app'@'192.168.100.%';3.7 防火墙(内网实验环境建议)
Section titled “3.7 防火墙(内网实验环境建议)”若启用了 ufw,建议只允许宿主机或内网网段访问 3306,而不是向所有来源开放:
# 只允许宿主机访问 MySQL(更推荐,把 IP 替换为你宿主机的实际 IP)sudo ufw allow from 192.168.100.1 to any port 3306 proto tcp
# 或允许整个实验网段访问sudo ufw allow from 192.168.100.0/24 to any port 3306 proto tcp
sudo ufw status3.8 从宿主机 Navicat 远程登录验证
Section titled “3.8 从宿主机 Navicat 远程登录验证”Navicat 连接配置
Section titled “Navicat 连接配置”在宿主机打开 Navicat,新建 MySQL 连接,填写以下信息:
| 配置项 | 填写内容 | 说明 |
|---|---|---|
| 连接名 | MySQL-Lab(自定义) | 仅用于区分连接 |
| 主机 | 192.168.100.20 | 虚拟机的实际 IP |
| 端口 | 3306 | MySQL 默认端口 |
| 用户名 | app | 刚才创建的最小权限账号 |
| 密码 | 123456 | 课堂统一密码 |
点击”测试连接”,成功后保存并双击打开连接。
在 Navicat 中验证权限
Section titled “在 Navicat 中验证权限”连接成功后,在 Navicat 中执行以下验证:
① 确认能看到授权的数据库
在左侧数据库列表中,应该能看到 stusta,但不应该看到 mysql等系统库。
② 验证查询操作(应成功)
双击 stusta 数据库 → 双击 students 表 → 可以查看数据。
也可以在 Navicat 的查询窗口中执行 SQL:
-- 确认当前身份SELECT USER(), CURRENT_USER();
-- 验证查询SELECT * FROM stusta.students;
-- 验证写入INSERT INTO stusta.students (name, score) VALUES ('赵六', 85.0);③ 验证权限边界(应该报错)
在 Navicat 查询窗口中尝试越权操作:
-- 以下操作都应该失败CREATE DATABASE hackdb; -- ERROR 1044: 权限不足DROP TABLE stusta.students; -- ERROR 1142: 权限不足SELECT * FROM mysql.user; -- ERROR 1142: 权限不足3.9 权限变更何时生效:FLUSH PRIVILEGES 要不要执行?
Section titled “3.9 权限变更何时生效:FLUSH PRIVILEGES 要不要执行?”很多同学在修改账号权限后会习惯性执行:
FLUSH PRIVILEGES;这条语句的作用是:让 MySQL 重新加载权限表,把磁盘中的授权表重新读入内存。但并不是所有权限修改都需要它。
不需要 FLUSH PRIVILEGES 的情况(推荐做法)
Section titled “不需要 FLUSH PRIVILEGES 的情况(推荐做法)”只要使用 MySQL 官方账号权限语句,权限会自动刷新并立即生效:
CREATE USER 'app'@'192.168.100.%' IDENTIFIED BY '123456';GRANT SELECT ON stusta.* TO 'app'@'192.168.100.%';REVOKE SELECT ON stusta.* FROM 'app'@'192.168.100.%';ALTER USER 'app'@'192.168.100.%' IDENTIFIED BY '123456';DROP USER 'app'@'192.168.100.%';这些语句执行后,MySQL 会自动更新内存中的权限缓存,所以不用再手动刷新。
需要 FLUSH PRIVILEGES 的情况(不推荐新手这样做)
Section titled “需要 FLUSH PRIVILEGES 的情况(不推荐新手这样做)”如果直接修改了系统权限表,例如:
UPDATE mysql.user SET Host = '192.168.100.%' WHERE User = 'app';DELETE FROM mysql.user WHERE User = 'olduser';INSERT INTO mysql.user (...);这类操作绕过了 CREATE USER / GRANT / ALTER USER 等标准语句。MySQL 可能还在使用旧的内存权限缓存,此时才需要:
FLUSH PRIVILEGES;| 操作方式 | 是否需要 FLUSH PRIVILEGES | 原因 |
|---|---|---|
CREATE USER / ALTER USER / DROP USER | 不需要 | MySQL 自动刷新权限缓存 |
GRANT / REVOKE | 不需要 | MySQL 自动刷新权限缓存 |
UPDATE mysql.user / DELETE FROM mysql.user | 需要 | 直接改系统表,需手动让 MySQL 重新加载 |
mysql_secure_installation 最后选择刷新权限表 | 通常选择 Yes | 向导可能修改系统权限表,刷新可确保生效 |
3.10 账号生命周期管理
Section titled “3.10 账号生命周期管理”账号不是创建完就不管了。安全运维要求对账号进行完整生命周期管理:
查看现有账号
Section titled “查看现有账号”-- 查看所有账号SELECT user, host, plugin, account_locked FROM mysql.user;
-- 只看有密码的账号(排除 auth_socket)SELECT user, host, plugin FROM mysql.user WHERE plugin != 'auth_socket';-- 修改其他用户的密码(推荐方式,MySQL 8.0)ALTER USER 'app'@'192.168.100.%' IDENTIFIED BY '123456';
-- 修改自己的密码ALTER USER USER() IDENTIFIED BY '123456';-- 撤销 DELETE 权限(只保留 SELECT, INSERT, UPDATE)REVOKE DELETE ON stusta.* FROM 'app'@'192.168.100.%';
-- 验证SHOW GRANTS FOR 'app'@'192.168.100.%';锁定与解锁账号
Section titled “锁定与解锁账号”-- 临时锁定账号(禁止登录,但不删除)ALTER USER 'app'@'192.168.100.%' ACCOUNT LOCK;
-- 解锁ALTER USER 'app'@'192.168.100.%' ACCOUNT UNLOCK;-- 删除账号(不可逆,先确认没有业务在使用)DROP USER 'app'@'192.168.100.%';3.11 密码策略与安全加固
Section titled “3.11 密码策略与安全加固”MySQL 8.0 内置了密码验证组件 validate_password,可以强制密码复杂度。
密码策略参数说明
Section titled “密码策略参数说明”-- 查看当前策略SHOW VARIABLES LIKE 'validate_password%';| 参数 | 含义 | 默认值 | 课堂值 | 生产建议值 |
|---|---|---|---|---|
validate_password.policy | 策略等级 | MEDIUM | LOW | MEDIUM |
validate_password.length | 密码最短长度 | 8 | 6 | 8+ |
validate_password.mixed_case_count | 大小写字母最少各几个 | 1 | 0(LOW 模式不检查) | 1 |
validate_password.number_count | 数字最少几个 | 1 | 0(LOW 模式不检查) | 1 |
validate_password.special_char_count | 特殊字符最少几个 | 1 | 0(LOW 模式不检查) | 1 |
策略等级说明:
LOW:只检查长度(课堂环境使用)MEDIUM:长度 + 大小写 + 数字 + 特殊字符(生产环境推荐)STRONG:MEDIUM + 字典检查(高安全要求场景)
第 4 课 日志:会排错、会定位慢、会恢复(重点:binlog)
Section titled “第 4 课 日志:会排错、会定位慢、会恢复(重点:binlog)”4.1 与上一课的衔接(过渡)
Section titled “4.1 与上一课的衔接(过渡)”我们已经实现”远程可用 + 最小权限”。最后要补上安全运维闭环:出了问题能查、误操作能追溯、必要时能恢复——这就靠日志。
4.2 本课要解决的问题
Section titled “4.2 本课要解决的问题”- 知道四类日志分别在什么场景用
- 能查看错误日志、能开启/查看慢查询日志
- 能开启 binlog,并理解”全量备份 + binlog 回放”的时间点恢复(PITR)思路
4.3 四种主要日志(先会选,再会做)
Section titled “4.3 四种主要日志(先会选,再会做)”| 日志类型 | 记录内容 | 主要用途 | 性能影响 | 建议 |
|---|---|---|---|---|
| 错误日志(Error Log) | 启动/关闭/运行错误 | 故障排查第一入口 | 无 | 默认开启,必须会看 |
| 通用查询日志(General Log) | 所有 SQL 语句 | 审计/调试 | 高 | 仅排错时临时开启 |
| 二进制日志(binlog) | 所有写操作事件 | 复制 + PITR 恢复 | 低-中 | 建议开启(重点) |
| 慢查询日志(Slow Log) | 超过阈值的 SQL | 性能定位 | 低 | 建议开启(会用即可) |
4.4 错误日志:服务异常先看这里
Section titled “4.4 错误日志:服务异常先看这里”错误日志是排查 MySQL 故障的第一入口。MySQL 启动失败、崩溃、权限错误等都会记录在这里。
# 查看最近 100 行错误日志tail -100 /var/log/mysql/error.log
# 实时跟踪(排查启动问题时特别有用)sudo tail -f /var/log/mysql/error.log-- 在 MySQL 内查看错误日志路径SHOW VARIABLES LIKE 'log_error';常见错误日志场景
Section titled “常见错误日志场景”| 日志关键字 | 含义 | 排查方向 |
|---|---|---|
[ERROR] Can't start server | 服务启动失败 | 检查端口冲突、配置文件语法、磁盘空间 |
[ERROR] Access denied | 认证失败 | 检查用户名/密码/主机权限 |
[ERROR] Disk full | 磁盘满 | 清理日志、扩展磁盘 |
[Warning] IP address 'x.x.x.x' could not be resolved | DNS 反解失败 | 设置 skip_name_resolve |
4.5 慢查询日志:系统慢了怎么找 SQL
Section titled “4.5 慢查询日志:系统慢了怎么找 SQL”慢查询日志记录执行时间超过阈值的 SQL 语句,是定位”系统为什么慢”的关键工具。
-- 查看当前状态SHOW VARIABLES LIKE 'slow_query%';SHOW VARIABLES LIKE 'long_query_time';
-- 开启慢查询日志(临时生效,重启后失效)SET GLOBAL slow_query_log = 1;SET GLOBAL long_query_time = 1; -- 超过 1 秒的 SQL 记录到慢查询日志SET GLOBAL log_queries_not_using_indexes = 1; -- 没用索引的也记录-- 在配置文件中永久生效-- 编辑 /etc/mysql/mysql.conf.d/mysqld.cnf-- [mysqld] 段添加:-- slow_query_log = 1-- slow_query_log_file = /var/log/mysql/slow.log-- long_query_time = 1-- log_queries_not_using_indexes = 1查看慢查询日志
Section titled “查看慢查询日志”# 查看慢查询日志sudo tail -50 /var/log/mysql/slow.log-- 统计慢查询数量SHOW GLOBAL STATUS LIKE 'Slow_queries';用 mysqldumpslow 分析
Section titled “用 mysqldumpslow 分析”MySQL 自带了慢查询日志分析工具:
# 按执行次数排序,显示前 10 条sudo mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
# 按平均执行时间排序sudo mysqldumpslow -s at -t 10 /var/log/mysql/slow.log
# 按锁定时间排序sudo mysqldumpslow -s t -t 10 /var/log/mysql/slow.log| 参数 | 含义 |
|---|---|
-s c | 按查询次数(count)排序 |
-s at | 按平均查询时间(avg time)排序 |
-s t | 按总锁定时间排序 |
-t N | 只显示前 N 条 |
4.6 通用查询日志:临时排错利器
Section titled “4.6 通用查询日志:临时排错利器”通用查询日志记录每一条到达 MySQL 的 SQL,包括连接、断开、查询。由于日志量巨大,只在排错时临时开启。
-- 查看当前状态SHOW VARIABLES LIKE 'general_log%';
-- 临时开启(排错时使用)SET GLOBAL general_log = 1;SET GLOBAL general_log_file = '/var/log/mysql/general.log';
-- ... 执行需要排查的操作 ...
-- 排查完毕,立即关闭SET GLOBAL general_log = 0;4.7 Binlog(重点):开启 + 验证 + PITR 思路
Section titled “4.7 Binlog(重点):开启 + 验证 + PITR 思路”binlog 是 MySQL 最重要的日志之一。它记录了所有修改数据的操作(INSERT / UPDATE / DELETE / DDL),用途有两个:
- 主从复制:从服务器通过读取主服务器的 binlog 来同步数据(下一章会用到)
- 数据恢复:配合全量备份实现时间点恢复(PITR)
开启 binlog
Section titled “开启 binlog”编辑配置文件:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf在 [mysqld] 段添加(或确认存在):
[mysqld]log_bin = /var/lib/mysql/mysql-binbinlog_format = ROWserver_id = 1binlog_expire_logs_seconds = 604800max_binlog_size = 100M| 参数 | 含义 | 建议值 |
|---|---|---|
log_bin | binlog 文件路径前缀 | 保持默认即可 |
binlog_format | 记录格式 | ROW(推荐,最安全) |
server_id | 服务器唯一标识 | 单机可随意,主从必须不同 |
binlog_expire_logs_seconds | 自动清理时间,单位秒 | 604800(7 天)- 2592000(30 天) |
max_binlog_size | 单个 binlog 文件最大体积 | 100M-256M |
重启并验证:
sudo systemctl restart mysql-- 验证 binlog 是否开启SHOW VARIABLES LIKE 'log_bin'; -- 应该是 ON
-- 查看 binlog 格式SHOW VARIABLES LIKE 'binlog_format'; -- 应该是 ROW
-- 查看所有 binlog 文件SHOW BINARY LOGS;
-- 查看当前正在写入的 binlog 文件及位置SHOW MASTER STATUS;查看 binlog 内容
Section titled “查看 binlog 内容”binlog 是二进制格式,不能直接用 cat 查看,需要使用 mysqlbinlog 工具:
# 解码查看第一个 binlog 文件的内容sudo mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/mysql-bin.000001 | less参数说明:
| 参数 | 作用 |
|---|---|
--base64-output=DECODE-ROWS | 将 base64 编码解码为可读格式 |
-v(或 --verbose) | 显示行变更的详细信息(ROW 格式必需) |
按时间范围筛选:
sudo mysqlbinlog \ --start-datetime="2026-04-21 08:00:00" \ --stop-datetime="2026-04-21 10:00:00" \ /var/lib/mysql/mysql-bin.000001 | less按位置范围筛选(更精确):
sudo mysqlbinlog \ --start-position=154 \ --stop-position=1024 \ /var/lib/mysql/mysql-bin.000001 | less时间点恢复(PITR)实战演示
Section titled “时间点恢复(PITR)实战演示”第 1 步:用 Navicat 做全量备份 + 记录 binlog 位置
Section titled “第 1 步:用 Navicat 做全量备份 + 记录 binlog 位置”全量备份是 PITR 的起点。我们用宿主机 Navicat 图形化完成备份,同时在虚拟机中记录备份时的 binlog 位置。
1a. 记录当前 binlog 位置(备份前)
在虚拟机 MySQL 中执行,切换到新的 binlog 文件并记下位置:
-- 切换 binlog,让备份后的写操作记录到新文件FLUSH BINARY LOGS;
-- 记录当前 binlog 文件名和位置(务必记下来!)SHOW MASTER STATUS;输出类似:
+------------------+----------+| File | Position |+------------------+----------+| mysql-bin.000002 | 157 |+------------------+----------+把 File 和 Position 记录下来(例如记在记事本里),后续恢复时要用。
1b. 在 Navicat 中导出备份
- 在宿主机打开 Navicat,连接到虚拟机 MySQL(使用 root 账号或有备份权限的账号)
- 右键点击
stusta数据库 → 转储 SQL 文件 → 结构和数据 - 选择保存路径,例如保存为
D:\backup\full_backup_stusta.sql - 等待导出完成
第 2 步:模拟备份后的正常业务 + 误操作
Section titled “第 2 步:模拟备份后的正常业务 + 误操作”-- 备份之后,业务正常运行,产生了新数据INSERT INTO students (name, gender, age, major, gpa, enrollment_date, email, phone, address)VALUES ('新生甲', '男', 18, '网络安全', 3.70, '2025-09-01', 'newA@stu.edu', '13800008001', '北京市朝阳区');
INSERT INTO students (name, gender, age, major, gpa, enrollment_date, email, phone, address)VALUES ('新生乙', '女', 19, '软件工程', 3.85, '2025-09-01', 'newB@stu.edu', '13800008002', '上海市徐汇区');
-- ⚠️ 10:00 有人误操作,清空了整张表!DELETE FROM students;SELECT COUNT(*) FROM students; -- 结果:0 条第 3 步:用 Navicat 恢复全量备份
Section titled “第 3 步:用 Navicat 恢复全量备份”发现误操作后,第一步是恢复全量备份,把数据回到备份时的状态:
- 在 Navicat 中右键点击
stusta数据库 → 运行 SQL 文件 - 选择之前导出的备份文件
D:\backup\full_backup_stusta.sql - 点击”开始”,等待执行完成
-- 验证:数据回来了,但缺少备份之后的新数据(新生甲、新生乙)SELECT COUNT(*) FROM students;第 4 步:从 binlog 定位误操作的位置
Section titled “第 4 步:从 binlog 定位误操作的位置”现在需要找到 DELETE FROM students 这条误操作在 binlog 中的确切位置,以便回放时在它之前停下来:
# 查看备份之后的 binlog,找到 DELETE 操作的时间和位置sudo mysqlbinlog --base64-output=DECODE-ROWS -v \ /var/lib/mysql/mysql-bin.000002 | grep -B 10 "DELETE FROM"输出中关注 # at 行和时间戳:
# at 1800 ← 事件起始位置#260421 10:00:15 server id 1 end_log_pos 1856 ← 事件结束位置### DELETE FROM `stusta`.`students`# at 1800:DELETE 事件的起始位置(--stop-position要用这个值)end_log_pos 1856:事件的结束位置- 时间:
2026-04-21 10:00:15(误操作发生时间)
也可以用 --start-position 从备份点开始查看,更精确地浏览事件:
# 从备份记录的位置 157 开始查看所有事件sudo mysqlbinlog --base64-output=DECODE-ROWS -v \ --start-position=157 \ /var/lib/mysql/mysql-bin.000002 | less第 5 步:回放 binlog 到误操作前一刻
Section titled “第 5 步:回放 binlog 到误操作前一刻”确定了误操作的时间或位置后,回放 binlog 中”备份之后、误操作之前”的所有正常操作:
方式 A:按时间回放(简单但不够精确)
sudo mysqlbinlog \ --start-position=157 \ --stop-datetime="2026-04-21 10:00:14" \ --database=stusta \ /var/lib/mysql/mysql-bin.000002 | mysql -u root方式 B:按位置回放(精确推荐)
# 假设通过上一步确认 DELETE 事件的起始位置是 1800sudo mysqlbinlog \ --start-position=157 \ --stop-position=1800 \ --database=stusta \ /var/lib/mysql/mysql-bin.000002 | mysql -u root| 参数 | 作用 |
|---|---|
--start-position=157 | 从备份记录的 binlog 位置开始(跳过备份前的旧事件) |
--stop-datetime / --stop-position | 在误操作之前停止 |
--database=stusta | 只回放 stusta 数据库的事件 |
第 6 步:验证恢复结果
Section titled “第 6 步:验证恢复结果”-- 确认数据完整恢复(包含备份后的正常操作)SELECT COUNT(*) AS '恢复后学生总数' FROM students;-- 应包含新生甲、新生乙
SELECT * FROM students WHERE name IN ('新生甲', '新生乙', '张三');-- 张三的 GPA 应该是 3.90(备份后 UPDATE 的结果)binlog 管理
Section titled “binlog 管理”-- 手动切换到新的 binlog 文件(当前文件写满或手动切换)FLUSH BINARY LOGS;
-- 手动清理 7 天前的 binlogPURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);
-- 清理指定文件之前的所有 binlogPURGE BINARY LOGS TO 'mysql-bin.000005';项目总结(一张表复盘)
Section titled “项目总结(一张表复盘)”| 课时 | 核心能力 | 验收点(可检查) |
|---|---|---|
| 第 1 课:安装验证 | 装得起来 | 服务 active + SELECT VERSION() |
| 第 2 课:配置基线 | 配得安全 | bind-address / utf8mb4 / time_zone 生效 |
| 第 3 课:账号权限 | 管得住人 | 宿主机 Navicat 能用最小权限账号远程登录,且无权执行越权操作 |
| 第 4 课:日志与恢复 | 能追溯/能恢复 | 能查看错误/慢查询;binlog 开启并能解释 PITR 三步走 |
忘记 root 密码:重置方法(Windows / Linux)
Section titled “忘记 root 密码:重置方法(Windows / Linux)”方法一:Linux / Ubuntu 重置 root 密码
Section titled “方法一:Linux / Ubuntu 重置 root 密码”思路:停止 MySQL → 用 --skip-grant-tables 跳过权限验证启动 → 手动改密 → 正常重启服务。
为什么要用
--skip-grant-tables?因为跳过授权表时,validate_password组件也不会加载,任何密码都能设置成功。如果用--init-file方法,遇到 MEDIUM 密码策略会导致123456被拒绝,服务器直接退出。
# 1) 停止 MySQL 服务sudo systemctl stop mysql
# 2) 跳过授权表启动(--skip-networking 禁止远程连接,确保安全)sudo mysqld --skip-grant-tables --skip-networking --user=mysql &等待几秒让 MySQL 启动完成,然后无密码登录:
# 3) 无密码连接 MySQLmysql -u root在 MySQL 中执行改密(必须先 FLUSH PRIVILEGES 重新加载权限表):
FLUSH PRIVILEGES;ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';exit关闭临时实例并正常启动:
# 4) 关闭临时实例(--skip-grant-tables 下无需密码)sudo mysqladmin -u root shutdown
# 5) 正常启动 MySQL 服务sudo systemctl start mysqlsudo systemctl status mysql --no-pager验证新密码:
mysql -u root -p# 输入 123456方法二:Windows 重置 root 密码
Section titled “方法二:Windows 重置 root 密码”以下示例以 MySQL 8.0 默认服务名 MySQL80 为例。若服务名不同,可在”服务”管理器中查看真实名称。
- 以管理员身份打开 CMD 或 PowerShell,停止 MySQL 服务:
net stop MySQL80- 跳过授权表启动 MySQL(
--skip-networking禁止远程连接,确保安全):
"C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld.exe" --skip-grant-tables --skip-networking --console看到 MySQL 启动成功后,新开一个管理员 CMD / PowerShell 窗口,无密码连接并改密:
"C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u rootFLUSH PRIVILEGES;ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';exit- 回到第一个窗口按
Ctrl + C关闭临时 MySQL,然后正常启动服务:
net start MySQL80验证新密码:
"C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root -p# 输入 123456附录 A:MySQL 与 SQL Server 主要差异(对比表)
Section titled “附录 A:MySQL 与 SQL Server 主要差异(对比表)”| 特性 | MySQL | SQL Server |
|---|---|---|
| 许可证 | 开源(GPL)/ 商业双轨 | 商业(微软授权) |
| 运行平台 | Linux、Windows、macOS | 主要 Windows(2017+ 支持 Linux) |
| 主要生态 | LAMP、Web 应用、云原生 | 企业级 Windows 应用 |
| 存储引擎 | 多引擎(InnoDB / MyISAM 等) | 单一引擎 |
| SQL 方言 | 标准 SQL + MySQL 扩展 | T-SQL(Transact-SQL) |
| 价格 | 免费(社区版) | 收费(企业版较贵) |
附录 B:安装场景与方法对比(课堂不展开)
Section titled “附录 B:安装场景与方法对比(课堂不展开)”| 场景 | 说明 | 推荐安装方式 |
|---|---|---|
| 课程实验 / 个人学习 | VMware 虚拟机中练习 | apt 安装(本项目推荐) |
| Web 项目开发 | 本地或测试服务器 | apt 安装 或 Docker |
| 生产环境部署 | 正式线上服务 | apt / 二进制包 + 加固脚本 |
| 快速验证 / CI 流水线 | 一次性测试环境 | Docker 容器(秒级启停) |
| 高性能调优 | 需要自定义编译参数 | 源码编译安装 |
附录 C:MySQL 安装后的目录结构与系统库(速查)
Section titled “附录 C:MySQL 安装后的目录结构与系统库(速查)”| 路径 | 说明 | 使用场景 |
|---|---|---|
/usr/bin/ | 客户端工具(mysql、mysqladmin、mysqldump 等) | 日常操作、备份 |
/usr/sbin/mysqld | MySQL 服务器进程 | 排查进程问题 |
/var/lib/mysql/ | 数据目录 | 备份、迁移、磁盘空间排查 |
/var/log/mysql/ | 日志目录(错误日志等) | 故障排查 |
/etc/mysql/mysql.conf.d/mysqld.cnf | 主配置文件 | 修改端口、绑定地址、日志等 |
| 数据库 | 说明 | 使用场景 |
|---|---|---|
| mysql | 用户账户、权限、插件配置 | 管理用户权限、排查认证问题 |
| information_schema | 元数据库(只读虚拟库) | 查询表结构、索引信息 |
| performance_schema | 性能监控数据 | 性能调优、慢查询分析 |
| sys | 高级视图 | 快速获取性能摘要 |
附录 D:密码管理多方式(旧系统兼容,课堂不作为主线)
Section titled “附录 D:密码管理多方式(旧系统兼容,课堂不作为主线)”- MySQL 8.0 推荐:
ALTER USER ... IDENTIFIED BY ...; SET PASSWORD ... = PASSWORD()为旧语法(8.0 已弃用 PASSWORD())mysqladmin适用于脚本化,但注意密码泄露风险(命令历史/进程列表)