MySQL 常用运维指令手册

运维指令手册 53.3 KB 原文

MySQL 常用运维指令手册

适用版本:MySQL 5.7 / 8.0 / 8.4 LTS / 9.7 LTS / 26.x Innovation 约定:# root 或 sudo,$ 普通用户,mysql> SQL 提示符,<xxx> 为占位符。 差异标注:正文中用 > **版本差异** 引用块标注跨版本不一致之处,无标注表示通用。 更新时间:2026-11


目录

  1. 版本与支持周期
  2. 下载地址与安装
  3. 服务启停与初始化
  4. 客户端连接
  5. 配置文件与参数
  6. 账号、权限与安全
  7. 库表日常操作
  8. 备份与恢复
  9. binlog 与主从复制
  10. 日志体系
  11. 性能诊断与优化
  12. 空间与容量管理
  13. 高可用:MGR / InnoDB Cluster
  14. 升级与迁移
  15. 高频故障速查
  16. 危险命令与避坑清单
  17. 附录:常用 SQL 与参数速查

1. 版本与支持周期

1.1 发布模型

MySQL 8.0 之后 Oracle 把版本拆成两条线:

轨道 命名 特点 适合场景
LTS 8.4、9.7 功能冻结,只做安全与 Bug 修复,季度小版本 生产环境,默认选择
Innovation 9.0~9.6、26.x 新特性先落地,支持到下一个 Innovation 发布 预研、可快速升级的业务

版本差异:自 2026-07 的 26.7.0 起,Innovation 轨道改用日历版本号 YY.M.P(26 = 2026 年,7 = 7 月,0 = 补丁号)。所以看到 26.10.0 不要以为是"未来版本",它就是 2026 年 10 月版。LTS 线仍保留 8.4 / 9.7 的原有编号。

1.2 生命周期对照(2026-11 时点)

版本 GA 时间 Premier 支持至 Extended 支持至 状态
5.7 2015-10 2020-10-31 2023-10-31 已 EOL,禁止新部署
8.0 2018-04 2025-04-30 2026-04-30 已 EOL,仅剩延保
8.4 LTS 2024-04-30 2029-04-30 2032-04-30 活跃,推荐
9.7 LTS 2026-04-21 2031-04-30 2034-04-30 活跃,新版首选
9.0 ~ 9.6 2024-07 ~ 2026-01 至下一个 Innovation 同左 均已终止
26.x Innovation 2026-07 起 至下一个 Innovation 同左 活跃,预研用

三个必须记住的结论:

  1. 8.0 已经停止补丁(2026-04-30 走完最后一个 8.0.46)。还在跑 8.0 的实例,等于在裸奔,升级路径是 8.0 → 8.4 → 9.7,不能跨 LTS 直接跳。
  2. 8.4 是当前最稳的落点,生态(XtraBackup、云厂商 RDS、ORM、监控插件)适配最完整。
  3. 9.7 适合新项目,但升级前必须确认中间件、备份工具、驱动链已适配。

1.3 版本识别

mysql --version                  # mysql  Ver 8.4.11 for Linux on x86_64 (MySQL Community Server - GPL)
mysqld --version                 # 服务端版本
mysqladmin -uroot -p version     # 运行中实例的版本与 uptime
SELECT VERSION();                                    -- 8.4.11
SELECT @@version_comment;                            -- MySQL Community Server - GPL
SHOW VARIABLES LIKE 'version%';
SELECT @@version_compile_os, @@version_compile_machine;

批量盘点(多实例):

for p in 3306 3307 3308; do
  mysqladmin -uroot -p"$PASS" -h127.0.0.1 -P$p version 2>/dev/null | grep -E 'Server version|Uptime'
done

2. 下载地址与安装

2.1 官方入口总表

用途 地址
下载总入口 https://dev.mysql.com/downloads/
Community Server(各平台主包) https://dev.mysql.com/downloads/mysql/
YUM / DNF 仓库配置包(RHEL、Oracle Linux、CentOS、Fedora) https://dev.mysql.com/downloads/repo/yum/
APT 仓库配置包(Ubuntu、Debian) https://dev.mysql.com/downloads/repo/apt/
SLES / openSUSE 仓库 https://dev.mysql.com/downloads/repo/suse/
Docker 镜像(Oracle 官方仓库) https://container-registry.oracle.com/ → MySQL → community-server
MySQL Shell(含升级检查、备份工具) https://dev.mysql.com/downloads/shell/
MySQL Router https://dev.mysql.com/downloads/router/
MySQL Workbench(图形客户端) https://dev.mysql.com/downloads/workbench/
Connector/J、ODBC、Python 等驱动 https://dev.mysql.com/downloads/connector/
历史归档版本(找老版本必用) https://downloads.mysql.com/archives/community/
官方文档(8.4 / 9.7 / 26.x 分册) https://dev.mysql.com/doc/
版本发布公告(确认最新小版本) https://forums.mysql.com/ (Announcing ... Releases 帖)

国内加速镜像(路径与可用性以镜像站当前状态为准):

# 清华 TUNA
https://mirrors.tuna.tsinghua.edu.cn/mysql/
# 阿里云
https://mirrors.aliyun.com/mysql/

提醒:镜像只同步 yum/apt 仓库和部分 tarball,不一定同步最新小版本。生产环境建议从 dev.mysql.com 取包并用 MD5 + GnuPG 校验;下载页每个文件都提供 MD5 和 Signature 链接。

校验下载包:

md5sum mysql-8.4.11-linux-glibc2.28-x86_64.tar.xz
# 与页面 MD5 对比;GPG 校验:
gpg --keyserver pgp.mit.edu --recv-keys BCA43417C96213EE   # MySQL 构建签名密钥,以页面为准
gpg --verify mysql-8.4.11-linux-glibc2.28-x86_64.tar.xz.asc mysql-8.4.11-linux-glibc2.28-x86_64.tar.xz

2.2 RHEL / CentOS / Oracle Linux:YUM/DNF 安装

仓库配置包命名规则:mysql{84|97}-community-release-{el8|el9|el10}-{版本号}.noarch.rpm。包名前缀决定默认启用的系列。

# 1) 下载并安装仓库配置包(以 EL9 + 8.4 LTS 为例)
#    当前文件名可从 https://dev.mysql.com/downloads/repo/yum/ 获取,形如:
#    mysql84-community-release-el9-4.noarch.rpm
#    mysql97-community-release-el9-1.noarch.rpm
dnf install -y https://dev.mysql.com/get/mysql84-community-release-el9-4.noarch.rpm

# 2) 确认仓库已生效
dnf repolist enabled | grep mysql
# mysql-8.4-lts-community        MySQL 8.4 LTS Community Server
# mysql-tools-8.4-lts-community  MySQL Tools 8.4 LTS Community

# 3) EL8 系必须先禁用系统自带 mysql 模块,否则会屏蔽官方包
dnf module disable mysql -y

# 4) 安装
dnf install -y mysql-community-server

# 5) 启动
systemctl enable --now mysqld

切换版本系列(同一时间只能启用一个系列):

# 查看所有子仓库
dnf repolist all | grep mysql

# 从 8.4 切到 9.7
dnf config-manager --disable mysql-8.4-lts-community
dnf config-manager --enable  mysql-9.7-lts-community

# 或直接改配置文件,把目标段的 enabled 改成 1,其余改 0
vi /etc/yum.repos.d/mysql-community.repo

/etc/yum.repos.d/mysql-community.repo 典型片段(EL9,8.4):

[mysql-8.4-lts-community]
name=MySQL 8.4 LTS Community Server
baseurl=http://repo.mysql.com/yum/mysql-8.4-community/el/9/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2025

避坑:一旦启用 MySQL 官方仓库,一次普通的 dnf update 就可能把系统自带的 mariadb-libs 替换掉,进而影响依赖它的服务。上线前先 dnf update --assumeno 看清单。

安装组件:

dnf install -y mysql-community-client mysql-community-libs mysql-community-devel
dnf install -y mysql-shell mysql-router mysql-workbench-community
dnf --disablerepo='*' --enablerepo='mysql*-community*' list available   # 看仓库里都有什么

2.3 Ubuntu / Debian:APT 安装

# 1) 下载 APT 配置包(当前版本 mysql-apt-config_0.8.40-1_all.deb,路径以下载页为准)
wget https://dev.mysql.com/get/mysql-apt-config_0.8.40-1_all.deb

# 2) 安装,弹出界面里用方向键选择 mysql-server 版本(8.4 LTS / 9.7 LTS / Innovation)
sudo dpkg -i mysql-apt-config_0.8.40-1_all.deb

# 3) 更新索引并安装
sudo apt-get update
sudo apt-get install -y mysql-server

# 4) 服务管理(Debian 系服务名就是 mysql,不是 mysqld)
sudo systemctl enable --now mysql

非交互安装(自动化脚本用):

export DEBIAN_FRONTEND=noninteractive
sudo debconf-set-selections <<'EOF'
mysql-apt-config mysql-apt-config/select-server select mysql-8.4-lts
EOF
sudo -E dpkg -i mysql-apt-config_0.8.40-1_all.deb
sudo apt-get update && sudo apt-get install -y mysql-server

改完仓库版本后需要重新跑 apt-get update,必要时 apt-get install --only-upgrade mysql-server。

避坑:APT 配置包会写入 /etc/apt/sources.list.d/mysql.list,同时导入 MYSQL-GPG-KEY。如果 apt update 报 NO_PUBKEY,重新执行 dpkg-reconfigure mysql-apt-config 让脚本重导密钥,不要手工 --allow-unauthenticated。

2.4 通用二进制包(tar.xz)安装

适合无外网、不能装仓库、要跑多实例的场景。

# 1) 准备依赖
dnf install -y libaio ncurses-compat-libs numactl openssl

# 2) 解包到统一目录
tar -xf mysql-8.4.11-linux-glibc2.28-x86_64.tar.xz -C /usr/local
ln -s /usr/local/mysql-8.4.11-linux-glibc2.28-x86_64 /usr/local/mysql

# 3) 建用户与目录
groupadd -r mysql && useradd -r -g mysql -s /bin/false mysql
mkdir -p /data/mysql/{data,logs,tmp,binlog}
chown -R mysql:mysql /data/mysql

# 4) 配置文件
cat > /etc/my.cnf <<'EOF'
[mysqld]
user=mysql
basedir=/usr/local/mysql
datadir=/data/mysql/data
socket=/data/mysql/mysql.sock
pid-file=/data/mysql/mysqld.pid
port=3306
log-error=/data/mysql/logs/error.log
log-bin=/data/mysql/binlog/mysql-bin
server-id=1
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
innodb_buffer_pool_size=4G
innodb_redo_log_capacity=1G
max_connections=1000
EOF

# 5) 初始化(会生成临时 root 密码,写在 error log 里)
/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --initialize --user=mysql

# 6) 取临时密码
grep -i 'temporary password' /data/mysql/logs/error.log

# 7) 启动
/usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf --user=mysql &

配置 systemd 托管:

# /etc/systemd/system/mysqld.service
[Unit]
Description=MySQL Server
After=network.target

[Service]
User=mysql
Group=mysql
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
ExecReload=/bin/kill -HUP $MAINPID
LimitNOFILE=65535
Restart=on-failure
RestartSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target
systemctl daemon-reload && systemctl enable --now mysqld

版本差异:--initialize 生成随机临时密码;--initialize-insecure 生成空密码 root,仅限内网一次性初始化,绝对不要在生产使用。 8.4 的二进制包还提供 --initialize 的 --init-file 方式注入初始 SQL,用于自动化改密。

2.5 Docker 部署

官方推荐 Oracle Container Registry,镜像名 container-registry.oracle.com/mysql/community-server;Docker Hub 上也有 mysql 官方镜像,两者内容同源。

# 拉取(tag 可选 8.4 / 9.7 / 26.7 / latest;latest = 最新 Innovation)
docker pull container-registry.oracle.com/mysql/community-server:8.4

# 启动(数据落盘 + 自定义配置)
docker run -d --name mysql84 \
  --restart on-failure \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD='Str0ng#Pass' \
  -e MYSQL_DATABASE=appdb \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD='App#Pass' \
  -v /data/mysql84/data:/var/lib/mysql \
  -v /data/mysql84/conf/my.cnf:/etc/my.cnf \
  container-registry.oracle.com/mysql/community-server:8.4

# 看日志与健康状态
docker ps                        # STATUS 从 (health: starting) 变成 (healthy) 才算可用
docker logs -f mysql84
docker logs mysql84 2>&1 | grep GENERATED      # 未指定 ROOT_PASSWORD 时取随机密码

# 容器内连接
docker exec -it mysql84 mysql -uroot -p

# 进 shell 排障
docker exec -it mysql84 bash

常用环境变量:

变量 作用
MYSQL_ROOT_PASSWORD root 密码,必填之一
MYSQL_RANDOM_ROOT_PASSWORD=1 随机 root 密码,打印到日志
MYSQL_DATABASE 初始化时建库
MYSQL_USER / MYSQL_PASSWORD 建普通账号并授权该库
MYSQL_ONETIME_PASSWORD=1 首次登录强制改密(默认开启)
MYSQL_TCP_PORT 改监听端口

避坑:容器删了数据就没了,-v 挂载必须做;docker rm -v 会连卷一起删。升级镜像时不要 docker rm 旧容器,而是用新镜像 + 旧数据目录起新容器,确认无误再删旧的。

2.6 Windows 安装

形式 文件 说明
MSI 安装包 mysql-<version>-winx64.msi 带 Configurator 图形配置,推荐
ZIP 免安装 mysql-<version>-winx64.zip 解压后手工 mysqld --initialize + 注册服务
# ZIP 方式初始化并注册服务(管理员 PowerShell)
mysqld --initialize --console
mysqld --install MySQL84 --defaults-file="C:\mysql\my.ini"
net start MySQL84
mysql -uroot -p

2.7 初始化后的必做四步

# 1) 用临时密码登录并立即改密
mysql -uroot -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStr0ng#Pass';
# 2) 跑安全向导:删匿名用户、禁 root 远程、删 test 库、重载权限
mysql_secure_installation
-- 3) 确认字符集与时区(8.0+ 默认已是 utf8mb4)
SHOW VARIABLES LIKE 'character_set_server';
SHOW VARIABLES LIKE 'collation_server';
SELECT @@system_time_zone, @@time_zone, NOW();

-- 4) 确认认证插件(8.4 默认 caching_sha2_password)
SELECT user, host, plugin FROM mysql.user;

3. 服务启停与初始化

3.1 systemd 管理

systemctl start mysqld          # RHEL 系服务名 mysqld
systemctl stop mysqld
systemctl restart mysqld
systemctl reload mysqld         # 重载配置(仅部分参数可动态生效)
systemctl status mysqld
systemctl enable mysqld         # 开机自启
systemctl is-enabled mysqld
systemctl list-units --type=service | grep -i mysql

journalctl -u mysqld -n 200 --no-pager
journalctl -u mysqld --since "2026-11-05 09:00" --until "2026-11-05 10:00"

版本差异:Debian/Ubuntu 官方包服务名是 mysql,二进制安装习惯写 mysqld。写脚本时用 systemctl list-unit-files | grep -E '^(mysql|mysqld)\.service' 先判断。

3.2 启动参数与配置文件优先级

mysqld --verbose --help | head -30         # 看默认读取顺序与编译默认值
mysqld --print-defaults                   # 打印当前生效的配置项
mysqld --validate-config                  # 只校验配置语法,不启动(排障神器)
mysqld --defaults-file=/etc/my.cnf --initialize --user=mysql

配置文件读取顺序(后面覆盖前面):

/etc/my.cnf → /etc/mysql/my.cnf → $MYSQL_HOME/my.cnf → ~/.my.cnf → --defaults-file 指定文件(唯一)

关键:一旦命令行给了 --defaults-file,MySQL 只读这一个文件,其他路径全部忽略。多实例部署必须给每个实例独立的 defaults-file,否则会互相抢 socket 和 pid。

3.3 多实例(同一台机器跑 3 个 MySQL)

for i in 3306 3307 3308; do
  mkdir -p /data/mysql$i/{data,logs,tmp}
  chown -R mysql:mysql /data/mysql$i
  mysqld --defaults-file=/etc/my$i.cnf --initialize --user=mysql
done

/etc/my3307.cnf 关键差异只有四行:port、socket、datadir、pid-file。其余可共用。

3.4 优雅关闭与强制终止

mysqladmin -uroot -p shutdown                  # 推荐,等事务回滚完成
systemctl stop mysqld                          # 等价
mysqladmin -uroot -p shutdown --connect-timeout=60

# 进程无响应时(先尝试 TERM,再考虑 KILL)
kill -TERM $(cat /data/mysql/mysqld.pid)

危险:kill -9 mysqld 会跳过 InnoDB 正常 shutdown,下次启动要走崩溃恢复(redo 回放)。数据一般不会丢,但恢复时间可能很长,且未刷盘的 binlog 可能丢失。非到万不得已不要用。


4. 客户端连接

4.1 基本连接

mysql -uroot -p                                 # 本机 socket
mysql -uroot -p -h127.0.0.1 -P3306              # TCP
mysql -uroot -p -h192.168.1.10 -P3306
mysql -uroot -p -S /data/mysql/mysql.sock       # 指定 socket
mysql -uroot -p --protocol=TCP                  # 强制走 TCP
mysql -uroot -p -e "SELECT VERSION();"          # 执行单条 SQL 后退出
mysql -uroot -p dbname < init.sql               # 导入 SQL 文件
mysql -uroot -p -N -B -e "SELECT 1"             # 无表头、Tab 分隔,适合脚本

常见坑:-hlocalhost 走 socket,-h127.0.0.1 走 TCP。授权里 'user'@'localhost' 与 'user'@'127.0.0.1' 是两个不同账号,连不上时先确认这一点。

4.2 免密与多环境管理

# ~/.my.cnf  权限必须 600,否则 mysql 会忽略并告警
[client]
user=ops
password=Ops#2026
host=127.0.0.1
port=3306
default-character-set=utf8mb4

[mysql]
prompt="\\u@\\h [\\d]> "
chmod 600 ~/.my.cnf
mysql --print-defaults            # 确认读取到的凭据
mysql --login-path=prod           # 或用 mysql_config_editor 加密存储
mysql_config_editor set --login-path=prod --host=10.0.0.11 --user=ops --password
mysql --login-path=prod
mysql_config_editor print --all

4.3 连通性排查三步

# 1) 端口通不通
ss -lntp | grep 3306
telnet 10.0.0.11 3306
nc -zv 10.0.0.11 3306

# 2) 服务活不活
mysqladmin -uroot -p ping
mysqladmin -uroot -p status

# 3) 账号能不能登
mysql -uops -p -h10.0.0.11 -P3306 -e "SELECT CURRENT_USER(), USER();"
-- 服务端看到的连接来源,和客户端以为的不一定一样
SELECT USER(), CURRENT_USER(), @@hostname, @@port;
SHOW PROCESSLIST;
SELECT * FROM information_schema.processlist WHERE user='ops';

5. 配置文件与参数

5.1 参数查看

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW GLOBAL VARIABLES LIKE '%timeout%';
SHOW SESSION VARIABLES LIKE 'sql_mode';
SELECT @@global.max_connections, @@session.sql_mode;
SHOW STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';

5.2 参数修改的三种方式

方式 命令 生效范围 是否持久
运行时全局 SET GLOBAL x=y; 新连接 重启丢失
运行时持久化 SET PERSIST x=y; 新连接 写入 mysqld-auto.cnf,重启保留
只写配置不生效 SET PERSIST_ONLY x=y; 无 下次重启生效(适合只读参数)
改配置文件 vi /etc/my.cnf + 重启 全部 保留
SET GLOBAL max_connections = 2000;
SET PERSIST max_connections = 2000;
SET PERSIST_ONLY innodb_buffer_pool_size = 8589934592;
RESET PERSIST max_connections;              -- 撤销持久化
RESET PERSIST;                              -- 清空全部持久化项

版本差异:SET PERSIST 从 8.0 开始提供,5.7 只能改配置文件重启。持久化内容在 $DATADIR/mysqld-auto.cnf,不要手工编辑,用 RESET PERSIST 管理。

动态修改 buffer pool(8.0+ 支持在线扩缩,按 chunk 调整):

SELECT @@innodb_buffer_pool_size/1024/1024/1024 AS gb, @@innodb_buffer_pool_instances;
SET GLOBAL innodb_buffer_pool_size = 8589934592;
SHOW STATUS LIKE 'Innodb_buffer_pool_resize_status';

5.3 生产必调参数清单

参数 建议值 说明
innodb_buffer_pool_size 物理内存 50%~70% 单实例最大性能杠杆,别低于热数据量
innodb_buffer_pool_instances buffer pool ≥ 8G 时设 8 减少争用
innodb_redo_log_capacity 2~8G 8.0.30+ 用这一个参数替代 innodb_log_file_size
innodb_flush_log_at_trx_commit 1(金融级)/ 2(可容忍 1s 丢失) 1 最安全,2 快
sync_binlog 1(默认) 与上一项共同决定崩溃安全性
max_connections 按业务压测,500~3000 配合连接池,不要盲目调大
innodb_io_capacity / _max SSD 2000 / 4000 控制刷脏速度
binlog_expire_logs_seconds 604800(7 天) 默认 30 天,磁盘吃紧时调小
slow_query_log + long_query_time ON / 1 慢日志是排障基础,必须开
character_set_server utf8mb4 8.0+ 默认正确
transaction_isolation REPEATABLE-READ 默认,除非明确需要 RC
innodb_dedicated_server 专用机可 ON 自动按内存/CPU 推导 buffer pool 与 redo 大小

注意:innodb_dedicated_server=ON 会覆盖你手写的 innodb_buffer_pool_size、innodb_redo_log_capacity 等参数,调优冲突时先确认它是不是开着。

5.4 配置文件模板(8.4 LTS 生产参考)

[mysqld]
user=mysql
port=3306
basedir=/usr/local/mysql
datadir=/data/mysql/data
socket=/data/mysql/mysql.sock
pid-file=/data/mysql/mysqld.pid
tmpdir=/data/mysql/tmp

# 网络
bind-address=0.0.0.0
max_connections=2000
max_connect_errors=10000
wait_timeout=600
interactive_timeout=600

# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
skip-character-set-client-handshake

# InnoDB
innodb_buffer_pool_size=32G
innodb_buffer_pool_instances=8
innodb_redo_log_capacity=4G
innodb_flush_log_at_trx_commit=1
innodb_flush_method=O_DIRECT
innodb_io_capacity=2000
innodb_io_capacity_max=4000
innodb_file_per_table=ON
innodb_print_all_deadlocks=ON

# 日志
log-error=/data/mysql/logs/error.log
slow_query_log=ON
slow_query_log_file=/data/mysql/logs/slow.log
long_query_time=1
log_queries_not_using_indexes=OFF
log_bin=/data/mysql/binlog/mysql-bin
binlog_format=ROW
binlog_expire_logs_seconds=604800
max_binlog_size=1G
sync_binlog=1

# 复制
server-id=1
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_row_image=FULL

# 时区
default-time-zone='+08:00'

[client]
socket=/data/mysql/mysql.sock
default-character-set=utf8mb4

6. 账号、权限与安全

6.1 账号管理

-- 建账号(8.0+ 默认 caching_sha2_password)
CREATE USER 'app'@'10.0.0.%' IDENTIFIED BY 'App#Str0ng2026';

-- 指定认证插件
CREATE USER 'legacy'@'10.0.0.%' IDENTIFIED WITH caching_sha2_password BY 'Legacy#2026';

-- 要求 SSL
CREATE USER 'sec'@'%' IDENTIFIED BY 'Sec#2026' REQUIRE SSL;

-- 改密码
ALTER USER 'app'@'10.0.0.%' IDENTIFIED BY 'New#Pass2026';
ALTER USER 'app'@'10.0.0.%' IDENTIFIED BY 'New#Pass2026' RETAIN CURRENT PASSWORD;

-- 锁账号 / 解锁
ALTER USER 'app'@'10.0.0.%' ACCOUNT LOCK;
ALTER USER 'app'@'10.0.0.%' ACCOUNT UNLOCK;

-- 强制下次登录改密(8.0.14+)
ALTER USER 'app'@'10.0.0.%' PASSWORD EXPIRE;

-- 有效期策略
ALTER USER 'app'@'10.0.0.%' PASSWORD EXPIRE INTERVAL 90 DAY;
ALTER USER 'app'@'10.0.0.%' PASSWORD EXPIRE NEVER;

-- 删除
DROP USER 'app'@'10.0.0.%';

-- 改用户名/主机
RENAME USER 'app'@'10.0.0.%' TO 'app'@'10.0.1.%';

版本差异:8.4 起 mysql_native_password 插件默认禁用,老客户端/老 ORM 若报 Authentication plugin 'mysql_native_password' cannot be loaded,要么升级客户端,要么在服务端显式开启: mysqld --mysql-native-password=ON(my.cnf 中写 mysql-native-password=ON)。9.0 起该插件已彻底移除,只能用 caching_sha2_password,此时只能升级客户端驱动。 另外 8.4 移除了 --default-authentication-plugin,改由 --authentication-policy 控制默认插件。

6.2 权限授予

-- 库级读写
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'10.0.0.%';

-- 只读账号
GRANT SELECT, SHOW VIEW ON appdb.* TO 'ro'@'%';

-- 单表
GRANT SELECT ON appdb.orders TO 'report'@'%';

-- 管理类权限
GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'monitor'@'%';
GRANT RELOAD, PROCESS, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'backup'@'%';

-- 全部权限(慎用,别给业务账号)
GRANT ALL PRIVILEGES ON appdb.* TO 'dba'@'10.0.0.%';

-- 只给列
GRANT SELECT (id, name) ON appdb.users TO 'api'@'%';

-- 收回
REVOKE DELETE ON appdb.* FROM 'app'@'10.0.0.%';

-- 查看
SHOW GRANTS FOR 'app'@'10.0.0.%';
SHOW GRANTS FOR CURRENT_USER();

FLUSH PRIVILEGES;      -- 仅当直接改 mysql 库授权表后才需要

关键:8.0 起 GRANT 不能隐式建用户,必须先 CREATE USER。5.7 的 GRANT ... IDENTIFIED BY 写法已失效。 FLUSH PRIVILEGES 在正常 GRANT/REVOKE 后是不需要的,它只用于人为修改 mysql.user 等表之后的刷新。

6.3 角色(8.0+)

CREATE ROLE 'role_ro', 'role_rw';
GRANT SELECT ON appdb.* TO 'role_ro';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'role_rw';

GRANT 'role_rw' TO 'app'@'10.0.0.%';
SET DEFAULT ROLE 'role_rw' TO 'app'@'10.0.0.%';

-- 会话内启用
SET ROLE 'role_rw';
SET ROLE ALL;
SET ROLE NONE;

SHOW GRANTS FOR 'app'@'10.0.0.%' USING 'role_rw';

6.4 密码策略组件

INSTALL COMPONENT 'file://component_validate_password';
SHOW VARIABLES LIKE 'validate_password%';

SET PERSIST validate_password.policy = MEDIUM;      -- LOW/MEDIUM/STRONG
SET PERSIST validate_password.length = 12;
SET PERSIST validate_password.mixed_case_count = 1;
SET PERSIST validate_password.special_char_count = 1;

版本差异:8.0 用 INSTALL PLUGIN validate_password SONAME 'validate_password.so';8.0.14 起推荐组件方式 INSTALL COMPONENT,参数前缀也从 validate_password_ 变成 validate_password.。

6.5 安全基线

# my.cnf
bind-address=10.0.0.11          # 只监听内网,别 0.0.0.0 裸奔
local_infile=OFF                # 禁 LOAD DATA LOCAL,防客户端任意文件读取
skip_name_resolve=ON            # 跳过反解,连接更快
require_secure_transport=ON     # 强制 TLS(确认客户端支持后再开)
-- 清理匿名账号与空密码账号
SELECT user, host, plugin FROM mysql.user WHERE user='' OR authentication_string='';
DROP USER ''@'localhost';

-- 确认 root 只能本地登录
SELECT user, host FROM mysql.user WHERE user='root';
-- 只应出现 root@localhost;若有 root@'%' 立即处理
# TLS 证书(8.0+ 启动时自动生成自签证书到 datadir)
mysql -uroot -p -e "SHOW VARIABLES LIKE 'have_ssl';"
mysql -uroot -p --ssl-mode=REQUIRED -e "SHOW STATUS LIKE 'Ssl_cipher';"

7. 库表日常操作

7.1 库操作

CREATE DATABASE appdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
CREATE DATABASE IF NOT EXISTS appdb CHARACTER SET utf8mb4;
DROP DATABASE appdb;                          -- 危险,无回收站
ALTER DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
SHOW DATABASES;
SHOW CREATE DATABASE appdb;
USE appdb;
SELECT DATABASE(), SCHEMA();

7.2 表操作

SHOW TABLES;
SHOW TABLES LIKE 'order%';
SHOW CREATE TABLE orders\G
DESC orders;
SHOW INDEX FROM orders;
SHOW TABLE STATUS LIKE 'orders'\G

CREATE TABLE orders (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  user_id BIGINT UNSIGNED NOT NULL,
  amount DECIMAL(12,2) NOT NULL DEFAULT '0.00',
  status TINYINT NOT NULL DEFAULT 0,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY idx_user_created (user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

RENAME TABLE orders TO orders_old;
TRUNCATE TABLE orders;                        -- 危险,不可回滚
DROP TABLE orders;                            -- 危险

7.3 在线 DDL 与算法选择

-- 加列:8.0.12+ 支持 INSTANT,秒级完成
ALTER TABLE orders ADD COLUMN remark VARCHAR(255) DEFAULT NULL, ALGORITHM=INSTANT;

-- 加索引:INPLACE,仍会占 IO,建议低峰
ALTER TABLE orders ADD INDEX idx_status (status), ALGORITHM=INPLACE, LOCK=NONE;

-- 改列类型:可能 COPY,大表必须用工具
ALTER TABLE orders MODIFY COLUMN amount DECIMAL(14,2) NOT NULL, ALGORITHM=INPLACE, LOCK=NONE;

-- 先试跑,不真正执行(8.0+)
ALTER TABLE orders ADD INDEX idx_x (x), ALGORITHM=INSTANT;
-- 报错就看提示,换 INPLACE 或 COPY

版本差异:8.0.12+ 支持 INSTANT ADD COLUMN;8.0.29+ 支持 INSTANT DROP COLUMN 及在任意位置加列。8.4 的 INSTANT 覆盖面更广,但改列类型、改字符集仍然要重建表。 百万行以上的表,优先用 pt-online-schema-change 或 gh-ost,不要直接 ALTER。

7.4 统计信息与碎片

ANALYZE TABLE orders;                          -- 更新统计信息,优化器依赖它
ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 32 BUCKETS;   -- 8.0+ 直方图
OPTIMIZE TABLE orders;                         -- InnoDB 会重建表,等于回收碎片,锁与 IO 大
ALTER TABLE orders ENGINE=InnoDB;              -- 等价的在线重建方式
CHECK TABLE orders;
CHECKSUM TABLE orders;
REPAIR TABLE orders;                           -- 仅 MyISAM 有意义

8. 备份与恢复

8.1 备份策略选型

方式 粒度 锁与影响 适用
mysqldump 库/表/全库 --single-transaction 下 InnoDB 几乎无锁 中小库、逻辑备份、跨版本迁移
MySQL Shell util dump 库/表 并行、压缩好 大库逻辑备份,8.0+ 推荐
Clone Plugin 整实例物理 需停写或加锁 快速搭从库、整实例搬迁
XtraBackup 整实例物理 热备 大数据量生产标准方案
文件系统快照 整实例 需短暂冻结点 云环境、LVM

3-2-1 原则:至少 3 份副本、2 种介质、1 份异地;并且每季度做一次真实恢复演练——没验证过的备份不算备份。

8.2 mysqldump

# 全库(含建库语句),InnoDB 一致性快照,不锁表
mysqldump -uroot -p \
  --single-transaction \
  --quick \
  --routines --triggers --events \
  --set-gtid-purged=OFF \
  --default-character-set=utf8mb4 \
  --all-databases > /backup/all_$(date +%F).sql

# 单库
mysqldump -uroot -p --single-transaction --databases appdb > /backup/appdb.sql

# 单表
mysqldump -uroot -p --single-transaction appdb orders > /backup/orders.sql

# 只要结构
mysqldump -uroot -p --no-data --databases appdb > /backup/appdb_schema.sql

# 只要数据
mysqldump -uroot -p --no-create-info appdb > /backup/appdb_data.sql

# 压缩 + 分库并行
mysqldump -uroot -p --single-transaction --databases appdb | gzip > /backup/appdb.sql.gz

# 一致性要求高(有 MyISAM 表时才需要)
mysqldump -uroot -p --lock-all-tables --all-databases > /backup/all_locked.sql

关键参数说明:

参数 作用
--single-transaction 开启可重复读事务,保证 InnoDB 一致性且不锁表
--quick 逐行取,避免大表撑爆内存
--routines --triggers --events 把存储过程、触发器、事件一起导出(默认不导)
--set-gtid-purged=OFF 导入到非 GTID 环境时必加,否则报错
--master-data=2 / --source-data=2 记录 binlog 位点(注释形式),搭从库用
--where="created_at>='2026-01-01'" 条件导出
--ignore-table=appdb.logs 排除大日志表

版本差异:--master-data 在 8.0.26+ 改名为 --source-data,旧名仍兼容。mysqldump 属于客户端工具,跨大版本使用要以低版本客户端导出、同版本或更高导入更稳。

恢复:

# 普通恢复
mysql -uroot -p < /backup/all_2026-11-05.sql

# 压缩包恢复
gunzip < /backup/appdb.sql.gz | mysql -uroot -p

# 恢复前临时关双写与唯一性检查(大幅提速,仅限导入窗口)
mysql -uroot -p -e "SET GLOBAL innodb_flush_log_at_trx_commit=2; SET GLOBAL sync_binlog=0;"
mysql -uroot -p appdb < /backup/appdb.sql
mysql -uroot -p -e "SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL sync_binlog=1;"

8.3 MySQL Shell 并行备份(大库推荐)

# 导出(并行 8 线程,zstd 压缩)
mysqlsh --uri root@localhost:3306 -- util dumpSchemas appdb \
  --outputDir=/backup/dump \
  --threads=8 \
  --compression=zstd

# 全实例导出
mysqlsh --uri root@localhost:3306 -- util dumpInstance /backup/dump_inst --threads=8

# 导入
mysqlsh --uri root@localhost:3306 -- util loadDump /backup/dump --threads=8

# 只导数据/只导结构
mysqlsh --uri root@localhost:3306 -- util dumpSchemas appdb --outputDir=/b --ddlOnly
mysqlsh --uri root@localhost:3306 -- util dumpSchemas appdb --outputDir=/b --dataOnly

8.4 Clone Plugin(整实例物理克隆)

-- 安装插件(也可写 my.cnf: plugin-load-add=mysql_clone.so)
INSTALL PLUGIN clone SONAME 'mysql_clone.so';
SELECT * FROM information_schema.plugins WHERE plugin_name='clone';

-- 本地克隆到目录(目录必须不存在且为空)
CLONE LOCAL DATA DIRECTORY = '/backup/clone_20261105';

-- 远程克隆(在目标实例执行,会覆盖目标数据,危险)
CLONE INSTANCE FROM 'backup'@'10.0.0.11':3306 IDENTIFIED BY 'Backup#2026';

-- 监控进度
SELECT * FROM performance_schema.clone_progress;
SELECT * FROM performance_schema.clone_status\G

危险:CLONE INSTANCE FROM 会清空目标实例数据再覆盖。执行前确认连的是从库而不是主库。

8.5 XtraBackup 热备

# 全量备份
xtrabackup --backup --user=backup --password='Backup#2026' \
  --target-dir=/backup/xtra/20261105 --parallel=4 --compress

# 准备(回放 redo,使数据一致)
xtrabackup --prepare --target-dir=/backup/xtra/20261105

# 恢复:停库 → 清空 datadir → 拷回 → 改权限 → 启库
systemctl stop mysqld
mv /data/mysql/data /data/mysql/data.bak
xtrabackup --copy-back --target-dir=/backup/xtra/20261105
chown -R mysql:mysql /data/mysql/data
systemctl start mysqld

# 增量备份(基于上一次全备)
xtrabackup --backup --target-dir=/backup/xtra/inc1 \
  --incremental-basedir=/backup/xtra/20261105

版本必须对齐:XtraBackup 8.4 对应 MySQL 8.4,XtraBackup 8.0 对应 8.0。用错大版本会直接拒绝备份。MySQL 9.7 / 26.x 需使用支持对应版本的新版 XtraBackup,上线前先确认支持矩阵。

8.6 binlog 增量恢复(误删数据救火)

# 1) 找到误操作前的位置(全备文件里记录的位点)
head -30 /backup/all_2026-11-05.sql | grep -i 'CHANGE MASTER\|SOURCE_LOG'

# 2) 解析 binlog 定位误删语句的时间点
mysqlbinlog --base64-output=DECODE-ROWS -v \
  /data/mysql/binlog/mysql-bin.000123 | grep -n -i 'delete from orders'

# 3) 恢复到误操作之前(按时间点)
mysqlbinlog --start-datetime="2026-11-05 09:00:00" \
            --stop-datetime="2026-11-05 10:15:00" \
  /data/mysql/binlog/mysql-bin.000123 | mysql -uroot -p

# 4) 按位置恢复
mysqlbinlog --start-position=12345678 --stop-position=12349999 \
  /data/mysql/binlog/mysql-bin.000123 | mysql -uroot -p

建议:把 mysqlbinlog ... > /backup/binlog_restore.sql 先落地,人工检查一遍再执行。直接管道进 mysql 等于把二次事故的可能性一起灌进去。


9. binlog 与主从复制

9.1 binlog 基础

SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_format';            -- ROW 为生产标准
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
SHOW BINARY LOGS;
SHOW MASTER STATUS\G                            -- 当前写入位点
SHOW BINLOG EVENTS IN 'mysql-bin.000123' LIMIT 20;
FLUSH BINARY LOGS;                              -- 切一个新文件
PURGE BINARY LOGS TO 'mysql-bin.000120';        -- 删到此文件之前
PURGE BINARY LOGS BEFORE '2026-11-01 00:00:00';

版本差异:8.0.22+ 用 SHOW REPLICA STATUS / SHOW BINARY LOG STATUS 新语法,旧语法 SHOW MASTER STATUS / SHOW SLAVE STATUS 仍兼容但在逐步退场。写新脚本建议用新语法,兼容老实例时用旧语法。

9.2 主库配置

# 主库 my.cnf
server-id=1
log_bin=/data/mysql/binlog/mysql-bin
binlog_format=ROW
binlog_row_image=FULL
gtid_mode=ON
enforce_gtid_consistency=ON
sync_binlog=1
innodb_flush_log_at_trx_commit=1
binlog_expire_logs_seconds=604800
-- 复制账号
CREATE USER 'repl'@'10.0.0.%' IDENTIFIED BY 'Repl#2026';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.%';

9.3 搭建从库(GTID 方式,推荐)

# 1) 主库做一致性备份(带位点)
mysqldump -uroot -p --single-transaction --source-data=2 \
  --set-gtid-purged=ON --all-databases > /backup/full.sql

# 2) 从库导入
mysql -uroot -p < /backup/full.sql
-- 3) 从库指向主库
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='10.0.0.11',
  SOURCE_PORT=3306,
  SOURCE_USER='repl',
  SOURCE_PASSWORD='Repl#2026',
  SOURCE_AUTO_POSITION=1;

-- 4) 启动与检查
START REPLICA;
SHOW REPLICA STATUS\G

关键列判读:

列 期望值 异常含义
Replica_IO_Running Yes Connecting = 网络/账号/防火墙问题
Replica_SQL_Running Yes No = 回放报错,看 Last_SQL_Error
Seconds_Behind_Source 0~5 持续增长 = 大事务、无主键、从库性能不足
Last_IO_Error / Last_SQL_Error 空 有内容先读原文再动手
Retrieved_Gtid_Set / Executed_Gtid_Set 差值小 差集大说明回放积压

版本差异:5.7 与 8.0.22 之前用 CHANGE MASTER TO、START SLAVE、SHOW SLAVE STATUS;8.0.23+ 统一为 SOURCE/REPLICA 术语。新旧语法在 8.4 中都可用,但文档与新工具已全面转向新语法。

9.4 复制运维常用

STOP REPLICA;
START REPLICA;
STOP REPLICA SQL_THREAD;
START REPLICA SQL_THREAD;
SHOW REPLICA STATUS\G

-- 跳过一条错误(GTID 模式下)
SET GTID_NEXT='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:12345';
BEGIN; COMMIT;
SET GTID_NEXT='AUTOMATIC';
START REPLICA;

-- 传统位点模式下跳过 N 个事件
STOP REPLICA; SET GLOBAL sql_replica_skip_counter=1; START REPLICA;

-- 重置复制(换主库前)
STOP REPLICA;
RESET REPLICA ALL;

-- 只读从库
SET GLOBAL super_read_only=ON;
SET GLOBAL read_only=ON;

原则:跳过事务是最后手段。跳之前必须搞清楚这条事务改了什么,用 mysqlbinlog 把对应 GTID 的事件解出来人工比对,否则主从数据会静默不一致。

9.5 延迟与并行复制

SHOW VARIABLES LIKE 'replica_parallel_workers';     -- 建议 = CPU 核数
SHOW VARIABLES LIKE 'replica_parallel_type';        -- LOGICAL_CLOCK
SHOW VARIABLES LIKE 'replica_preserve_commit_order';
SHOW PROCESSLIST;                                   -- 看 SQL 线程在跑什么
replica_parallel_workers=8
replica_parallel_type=LOGICAL_CLOCK
replica_preserve_commit_order=ON

延迟三大根因:大事务(一次删百万行)、大表无主键导致 ROW 回放全表扫、从库单线程回放。前两个改业务,第三个加 replica_parallel_workers。


10. 日志体系

10.1 日志类型与位置

日志 默认位置 开启方式
错误日志 $DATADIR/*.err(RHEL 包为 /var/log/mysqld.log) 默认开启,log_error 指定
慢查询日志 $DATADIR/*-slow.log slow_query_log=ON
通用日志 $DATADIR/*.log general_log=ON,生产禁开
binlog $DATADIR/*-bin.* log_bin
relay log $DATADIR/*-relay-bin.* 从库自动
SHOW VARIABLES LIKE 'log_error';
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'general_log%';
SHOW VARIABLES LIKE 'log_bin';

10.2 错误日志

tail -f /var/log/mysqld.log
grep -i -E 'error|warning|aborted' /var/log/mysqld.log | tail -50
grep -i 'temporary password' /var/log/mysqld.log
journalctl -u mysqld -p err --no-pager

常见关键字对照:

关键字 含义
[ERROR] InnoDB: Unable to lock ./ibdata1 已有实例在跑,或 pid 文件残留
[ERROR] Do you already have another mysqld server running on port 端口冲突
[Warning] IP address ... could not be resolved DNS 反解慢,加 skip_name_resolve
[ERROR] Too many connections 连接数打满
[ERROR] Table './db/t' is marked as crashed 表损坏,需 REPAIR/恢复
Aborted connection ... (Got an error reading communication packets) 客户端异常断开或超时

10.3 慢查询日志

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = OFF;    -- 开了会刷爆日志
SET GLOBAL slow_query_log_file = '/data/mysql/logs/slow.log';

SHOW VARIABLES LIKE 'slow_query_log_file';
# 自带工具统计
mysqldumpslow -s t -t 20 /data/mysql/logs/slow.log        # 按总耗时 TOP20
mysqldumpslow -s c -t 20 /data/mysql/logs/slow.log        # 按次数

# 业界标准:pt-query-digest
pt-query-digest /data/mysql/logs/slow.log > /tmp/slow_report.txt
pt-query-digest --since '2026-11-01' --until '2026-11-05' /data/mysql/logs/slow.log

10.4 日志清理与轮转

# 用 logrotate 管理错误日志与慢日志
cat > /etc/logrotate.d/mysql <<'EOF'
/data/mysql/logs/*.log {
    daily
    rotate 14
    missingok
    compress
    delaycompress
    notifempty
    copytruncate
}
EOF
logrotate -d /etc/logrotate.d/mysql      # 干跑验证
-- binlog 用 SQL 清理,不要 rm 文件(会破坏索引文件)
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);

危险:手工 rm binlog 文件而不走 PURGE,会让 mysql-bin.index 与实际文件不一致,可能导致启动失败。清理一律用 PURGE BINARY LOGS。


11. 性能诊断与优化

11.1 一眼看现状

SHOW GLOBAL STATUS LIKE 'Uptime';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Threads_running';
SHOW GLOBAL STATUS LIKE 'Questions';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
SHOW GLOBAL STATUS LIKE 'Aborted_connects';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';

SHOW ENGINE INNODB STATUS\G

QPS / TPS 手算:

-- QPS ≈ Questions / Uptime
SELECT ROUND(Questions / Uptime, 2) AS qps FROM
  (SELECT VARIABLE_VALUE+0 AS Questions FROM performance_schema.global_status WHERE VARIABLE_NAME='Questions') a,
  (SELECT VARIABLE_VALUE+0 AS Uptime    FROM performance_schema.global_status WHERE VARIABLE_NAME='Uptime') b;

11.2 当前连接与正在执行的 SQL

SHOW FULL PROCESSLIST;
SELECT id, user, host, db, command, time, state, LEFT(info,120) AS sql
FROM information_schema.processlist
WHERE command <> 'Sleep'
ORDER BY time DESC;

-- 8.0+ 更细:当前语句、等待事件、事务年龄
SELECT t.PROCESSLIST_ID, t.PROCESSLIST_USER, t.PROCESSLIST_TIME,
       t.PROCESSLIST_STATE, e.EVENT_NAME, LEFT(t.PROCESSLIST_INFO,100) AS sql
FROM performance_schema.threads t
LEFT JOIN performance_schema.events_waits_current e ON e.THREAD_ID=t.THREAD_ID
WHERE t.PROCESSLIST_ID IS NOT NULL AND t.PROCESSLIST_COMMAND <> 'Sleep';
# 杀连接 / 杀语句
mysqladmin -uroot -p processlist
mysqladmin -uroot -p kill 12345
KILL 12345;            -- 杀连接
KILL QUERY 12345;      -- 只杀当前语句,连接保留

注意:KILL 不是立即生效,MySQL 在安全点才中断。大事务回滚可能比执行更久,杀完要观察 SHOW PROCESSLIST 里是否还处于 Rolling back。

11.3 锁与阻塞

-- 谁在等谁(8.0+)
SELECT * FROM sys.innodb_lock_waits\G

SELECT
  r.trx_id AS waiting_trx, r.trx_mysql_thread_id AS waiting_thread,
  r.trx_query AS waiting_query,
  b.trx_id AS blocking_trx, b.trx_mysql_thread_id AS blocking_thread,
  b.trx_query AS blocking_query
FROM performance_schema.data_lock_waits w
JOIN information_schema.innodb_trx b ON b.trx_id = w.BLOCKING_ENGINE_TRANSACTION_ID
JOIN information_schema.innodb_trx r ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID;

-- 当前锁明细
SELECT * FROM performance_schema.data_locks;
SELECT * FROM information_schema.innodb_trx\G

版本差异:5.7 用 information_schema.innodb_locks + innodb_lock_waits(8.0 已移除),8.0+ 改用 performance_schema.data_locks / data_lock_waits。老监控脚本升级时会直接报"表不存在"。

11.4 执行计划

EXPLAIN SELECT * FROM orders WHERE user_id=100 AND status=1;
EXPLAIN FORMAT=JSON SELECT ...;
EXPLAIN ANALYZE SELECT ...;      -- 8.0.18+,真实执行并给出实际耗时/行数

SHOW WARNINGS;                   -- 看优化器改写后的 SQL
SHOW PROFILES;                   -- 已弃用,用 performance_schema 代替

EXPLAIN 关键列判读:

列 危险信号
type ALL(全表扫)、index(全索引扫)需优化;ref/range/eq_ref 较好
rows 估算行数远大于返回行数,统计信息可能过期,跑 ANALYZE TABLE
Extra Using temporary、Using filesort、Using join buffer 是需要关注的三项
key NULL 表示没走索引

11.5 InnoDB 关键指标

-- buffer pool 命中率,应 > 99%
SELECT
  ROUND(100 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status
               WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') * 100 /
        (SELECT VARIABLE_VALUE FROM performance_schema.global_status
         WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests'), 4) AS hit_pct;

-- 脏页比例,持续接近 100% 说明刷脏跟不上
SELECT
  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_dirty') /
  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_total') * 100 AS dirty_pct;

SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_wait_free';   -- 非 0 说明 buffer pool 不够
SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_time_avg';
SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_waits';
SHOW GLOBAL STATUS LIKE 'Innodb_deadlocks';
-- 表级 IO 热点
SELECT * FROM sys.schema_table_statistics ORDER BY total_latency DESC LIMIT 10;
SELECT * FROM sys.io_global_by_file_by_bytes ORDER BY total DESC LIMIT 10;
SELECT * FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 10;

11.6 服务器层排障

top -H -p $(pgrep -x mysqld)          # 看哪个线程吃 CPU
pidstat -p $(pgrep -x mysqld) 1
iostat -x 1 5                         # 看 %util、await
vmstat 1 5
free -h
dmesg | grep -i -E 'oom|killed process'

OOM 判定:dmesg 出现 Out of memory: Killed process ... (mysqld),说明被内核 OOM Killer 干掉,不是 MySQL 自己崩。要么降 innodb_buffer_pool_size,要么加内存,要么限制容器 memory limit。


12. 空间与容量管理

12.1 库表大小排行

SELECT table_schema, table_name,
       ROUND((data_length+index_length)/1024/1024, 2) AS total_mb,
       ROUND(data_length/1024/1024, 2) AS data_mb,
       ROUND(index_length/1024/1024, 2) AS index_mb,
       table_rows
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','sys','performance_schema','information_schema')
ORDER BY (data_length+index_length) DESC
LIMIT 20;

-- 按库汇总
SELECT table_schema,
       ROUND(SUM(data_length+index_length)/1024/1024/1024, 2) AS total_gb
FROM information_schema.tables
GROUP BY table_schema
ORDER BY total_gb DESC;

table_rows 是估算值,InnoDB 下可能偏差很大;要精确行数用 SELECT COUNT(*),或在 8.0+ 用 information_schema.tables 配合 ANALYZE TABLE 刷新。

12.2 磁盘占用盘点

df -h
du -sh /data/mysql/* | sort -h
du -sh /data/mysql/binlog/*
ls -lh /data/mysql/data/ | sort -k5 -h | tail -20

常见"磁盘莫名满了"的元凶,按概率排序:

  1. binlog 堆积:SHOW BINARY LOGS; 看总量,binlog_expire_logs_seconds 是否被调大;从库长期断开时主库 binlog 无法清理。
  2. undo 表空间膨胀:长事务不提交,ibdata/undo_001 疯长。
  3. 慢日志/通用日志:general_log 误开,一天几十 G。
  4. relay log:从库回放卡住后持续堆积。
  5. ibtmp1:大查询临时表空间,重启才回收。
  6. 大表碎片:OPTIMIZE TABLE 可回收,但需要额外等量空间。

12.3 空间回收

-- 查看 undo 与临时表空间
SELECT * FROM information_schema.innodb_tablespaces WHERE name LIKE '%undo%';
SELECT @@innodb_undo_tablespaces, @@innodb_undo_log_truncate;

-- 查看长事务(undo 无法回收的头号原因)
SELECT trx_id, trx_state, trx_started,
       TIMESTAMPDIFF(MINUTE, trx_started, NOW()) AS minutes,
       trx_rows_locked, LEFT(trx_query,100) AS sql
FROM information_schema.innodb_trx
ORDER BY trx_started;
-- 回收表空间(会重建表,务必低峰)
ALTER TABLE big_logs ENGINE=InnoDB;
OPTIMIZE TABLE big_logs;
# 收缩 ibdata(需重新初始化,成本高,仅在无 file-per-table 时才值得)
# 正常情况请保持 innodb_file_per_table=ON,让每表独立表空间

危险操作顺序:DROP TABLE / TRUNCATE 后磁盘不降是正常现象(file-per-table 会释放,但 ibdata 不会缩)。不要为了省空间去动 ibdata1。


13. 高可用:MGR / InnoDB Cluster

13.1 三种方案对比

方案 一致性 自动故障切换 复杂度 适用
传统主从(异步/半同步) 最终一致 需外部组件(MHA、Orchestrator) 中 读扩展、已有架构
MGR(Group Replication) 多数派确认 内建 中高 3 节点金融级
InnoDB Cluster(MGR + Shell + Router) 多数派确认 内建 + 路由 高 新建生产集群推荐

13.2 MGR 最小配置(三节点)

# 每个节点,server-id 与 loose-group_replication_local_address 各不相同
server-id=1
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_replica_updates=ON
binlog_format=ROW

plugin-load-add=group_replication.so
group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_start_on_boot=OFF
group_replication_local_address="10.0.0.11:33061"
group_replication_group_seeds="10.0.0.11:33061,10.0.0.12:33061,10.0.0.13:33061"
group_replication_bootstrap_group=OFF
group_replication_single_primary_mode=ON
group_replication_enforce_update_everywhere_checks=OFF
-- 首节点引导
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;
SELECT * FROM performance_schema.replication_group_member_stats\G

顺序不能错:group_replication_bootstrap_group=ON 只能在第一个节点、且只执行一次。两个节点同时 bootstrap 会脑裂成两个单节点组,恢复非常麻烦。

13.3 InnoDB Cluster(MySQL Shell)

# 1) 校验并配置实例(会提示改配置、必要时重启)
mysqlsh --uri root@10.0.0.11:3306 --js
dba.configureInstance('root@10.0.0.11:3306')
dba.configureInstance('root@10.0.0.12:3306')
dba.configureInstance('root@10.0.0.13:3306')
// 2) 创建集群
var c = dba.createCluster('prodCluster')
c.status()

// 3) 加节点(自动 clone)
c.addInstance('root@10.0.0.12:3306', {recoveryMethod:'clone'})
c.addInstance('root@10.0.0.13:3306', {recoveryMethod:'clone'})

// 4) 日常
c.status({extended:true})
c.describe()
c.removeInstance('root@10.0.0.13:3306')
c.setPrimaryInstance('root@10.0.0.12:3306')     // 手动切主
c.rescan()
dba.rebootClusterFromCompleteOutage()            // 全组宕机后拉起
# Router 引导(应用连 Router 的 6446 读写 / 6447 只读)
mysqlrouter --bootstrap root@10.0.0.11:3306 --user=mysqlrouter
systemctl enable --now mysqlrouter

14. 升级与迁移

14.1 升级路径

5.7 ──► 8.0 ──► 8.4 LTS ──► 9.7 LTS ──► 26.x(Innovation,可选)

规则:

  1. 不能跨 LTS 大跳。8.0 → 9.7 必须经 8.4,官方明确不支持一步到位。
  2. 不支持降级。升级前必须留可回退的物理备份 + 旧版本二进制。
  3. 小版本内可以直接升(8.4.10 → 8.4.11)。
  4. 从 8.0 升 8.4 前,先处理认证插件问题(mysql_native_password 在 8.4 默认禁用),否则老应用全部登录失败。

14.2 升级前检查(必做)

# MySQL Shell 官方升级检查器,输出兼容性问题清单
mysqlsh --uri root@localhost:3306 -- util checkForServerUpgrade
mysqlsh --uri root@localhost:3306 -- util checkForServerUpgrade --outputFormat=JSON --outputFile=/tmp/upgrade.json

# 手工补充检查
mysql -uroot -p -e "SELECT user,host,plugin FROM mysql.user;"
mysql -uroot -p -e "SELECT * FROM information_schema.tables WHERE table_collation='utf8mb3_general_ci';"
mysql -uroot -p -e "SHOW VARIABLES LIKE '%innodb_file_format%';"       # 8.0 已移除
mysql -uroot -p -e "SHOW VARIABLES LIKE 'sql_mode';"

util checkForServerUpgrade 会检查:废弃/移除的参数、保留字冲突、utf8mb3 用法、过长的索引、不支持的分区、认证插件、GTID 一致性等。

14.3 就地升级(in-place)

# 1) 全量物理备份(这一步不能省)
xtrabackup --backup --target-dir=/backup/pre_upgrade_$(date +%F) --user=backup -p

# 2) 记录当前版本与参数快照
mysql -uroot -p -e "SHOW VARIABLES;" > /backup/vars_before.txt
mysql -uroot -p -e "SELECT VERSION();" >> /backup/vars_before.txt

# 3) 停库
systemctl stop mysqld

# 4) 替换二进制(RPM 方式:先把仓库切到 8.4 系列,再升级已安装包)
dnf config-manager --disable mysql-8.0-community 2>/dev/null || true
dnf config-manager --enable  mysql-8.4-lts-community
dnf update -y mysql-community-server mysql-community-client mysql-community-common
# 或二进制方式:软链指向新目录

# 5) 启动,8.0.16+ 会自动执行数据字典升级
systemctl start mysqld

# 6) 验证
mysql -uroot -p -e "SELECT VERSION();"
tail -100 /var/log/mysqld.log | grep -i -E 'error|upgrade'

版本差异:mysql_upgrade 命令在 8.0.16 起已废弃、8.4 中已移除。8.0.16+ 由服务端启动时自动升级数据字典;若需强制重跑,用 mysqld --upgrade=FORCE(8.0.16+)。老教程里的 mysql_upgrade -uroot -p 在 8.4 上会直接 command not found。

14.4 逻辑迁移(跨云/跨大版本最稳)

# 源库导出
mysqlsh --uri root@old:3306 -- util dumpInstance /backup/migrate --threads=8

# 目标库导入
mysqlsh --uri root@new:3306 -- util loadDump /backup/migrate --threads=8

跨版本迁移建议用 MySQL Shell dump/load 而不是 mysqldump:它会自动处理字符集、GTID、并行导入,并且对 8.0→9.7 的兼容性更好。

14.5 回退

# 回退前提:旧版二进制还在、旧数据目录的物理备份还在
systemctl stop mysqld
rm -rf /data/mysql/data
xtrabackup --copy-back --target-dir=/backup/pre_upgrade_2026-11-05
chown -R mysql:mysql /data/mysql/data
# 把软链指回旧版本
ln -sfn /usr/local/mysql-8.0.46 /usr/local/mysql
systemctl start mysqld

关键:数据字典升级后低版本无法启动(会报 "data dictionary is of a higher version")。所以回退只能靠升级前的物理备份,不能只换回二进制。这就是为什么第 1 步不能省。


15. 高频故障速查

15.1 连不上数据库

现象 排查顺序
Can't connect to MySQL server (111) 服务是否起 → ss -lntp | grep 3306 → 防火墙/安全组 → bind-address
Can't connect ... (2) socket 不存在 socket 路径不对 → SHOW VARIABLES LIKE 'socket' → 用 -S 指定
Access denied for user 账号是否存在 → host 匹配(localhost vs 127.0.0.1)→ 密码 → 插件
Too many connections 连接泄漏 → 连接池配置 → max_connections → 临时救火
Host 'x' is blocked max_connect_errors 触发 → FLUSH HOSTS
SSL 相关报错 --ssl-mode=DISABLED 临时验证,再查证书
-- 连接数救火
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';
SET GLOBAL max_connections = 2000;
FLUSH HOSTS;
# 客户端插件不匹配时临时绕过(仅排障,不要写进生产配置)
mysql -uroot -p --ssl-mode=DISABLED
mysql -uroot -p --get-server-public-key

15.2 忘记 root 密码

systemctl stop mysqld
mysqld_safe --skip-grant-tables --skip-networking &
mysql -uroot
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'New#Pass2026';
mysqladmin -uroot -p shutdown
systemctl start mysqld

注意:--skip-grant-tables 期间任何本地用户都能无密登录,必须同时加 --skip-networking 并尽快完成。改完必须重启,不要用 FLUSH PRIVILEGES 直接放开。

15.3 表损坏 / 无法启动

# 先看错误日志,确认是哪张表、什么错
grep -i -E 'corrupt|crashed|checksum' /var/log/mysqld.log | tail -30
CHECK TABLE appdb.orders;
REPAIR TABLE appdb.orders;                -- 仅 MyISAM 有效

InnoDB 损坏时逐级放开恢复模式(1 最轻,6 最危险):

[mysqld]
innodb_force_recovery=1
# 能启动就立刻 mysqldump 导出,再重建实例导入
mysqldump -uroot -p --single-transaction --all-databases > /backup/emergency.sql

危险:innodb_force_recovery >= 4 时表变为只读且可能丢失数据,6 会跳过 redo 回放。只在"数据不要了也要把库拉起来"的场景用,用完立刻改回 0。

15.4 主从延迟/中断

SHOW REPLICA STATUS\G
Last_SQL_Error 关键字 处理
Duplicate entry 主从数据已不一致,先比对再决定跳过
Cannot add or update a child row 外键冲突,检查 foreign_key_checks
Unknown database 从库缺库,补建或从主库同步
Error_code: 1062 同上,唯一键冲突
The slave is connecting using CHANGE MASTER TO ... 正常连接中,等待
Got fatal error 1236 binlog 被清理,需重新做全量

15.5 磁盘满

df -h
du -sh /data/mysql/* | sort -h | tail
ls -lh /data/mysql/binlog/ | tail
SHOW BINARY LOGS;
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 3 DAY);

急救顺序:清 binlog(用 PURGE)→ 清慢日志/错误日志 → 清 relay log(RESET REPLICA ALL 谨慎)→ 扩盘。不要在磁盘满时执行 OPTIMIZE/ALTER,它们需要额外空间。

15.6 时区不对

SELECT @@system_time_zone, @@global.time_zone, @@session.time_zone, NOW(), UTC_TIMESTAMP();
# my.cnf 固定偏移(不需要时区表)
default-time-zone='+08:00'
# 需要 named timezone 时导入时区表
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -uroot -p mysql
SET GLOBAL time_zone='Asia/Shanghai';
SELECT CONVERT_TZ(NOW(),'UTC','Asia/Shanghai');

注意:default-time-zone 只影响新连接;已有连接需 SET SESSION time_zone。另外 TIMESTAMP 列会随会话时区转换,DATETIME 不会——这是两列类型最本质的区别。

15.7 字符集乱码

SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
SHOW FULL COLUMNS FROM appdb.orders;      -- 看列的 Collation

修复已乱码数据(先备份,再改):

ALTER TABLE appdb.orders CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

判断技巧:SHOW VARIABLES 里 character_set_client/connection/results 三者都应是 utf8mb4。若列是 latin1 而连接是 utf8mb4,双写转换就会出问题。改字符集前必须备份,CONVERT TO 不可逆。


16. 危险命令与避坑清单

16.1 绝对不能在生产随手敲的命令

命令 后果 防护
DROP DATABASE <db> 整库消失,无回收站 生产禁用;sql_safe_updates 无法拦截,只能靠权限
DROP TABLE / TRUNCATE 数据即刻清空,不可回滚 收回业务账号 DDL 权限
DELETE FROM t;(无 WHERE) 全表删,大事务撑爆 undo SET sql_safe_updates=1 强制带条件
UPDATE t SET c=1;(无 WHERE) 全表改,锁表+主从延迟 同上
PURGE BINARY LOGS 误删 从库断链,只能重做全量 先确认从库位点
kill -9 mysqld 崩溃恢复、binlog 可能丢 用 mysqladmin shutdown
innodb_force_recovery=6 跳过 redo,数据可能丢 先备份再调,用完归 0
FLUSH TABLES WITH READ LOCK 全库只读,业务全停 备份工具会自动加,手工别乱用
ALTER TABLE 大表直接跑 长时间锁表/IO 打满 用 ALGORITHM=INSTANT 或 gh-ost
rm 数据目录/binlog 文件 实例报废 清理只走 PURGE,删除只走 DROP

16.2 sql_safe_updates

SET SESSION sql_safe_updates = 1;
-- 之后 UPDATE/DELETE 必须带索引条件,否则报错
UPDATE orders SET status=0 WHERE id=1;         -- OK
DELETE FROM orders;                            -- 报错,被拦住

16.3 变更前检查清单

# 1) 确认连的是哪个实例(防止手滑连生产)
mysql -uroot -p -e "SELECT @@hostname, @@port, @@datadir;"

# 2) 备份(DDL 前至少做结构+数据快照)
mysqldump -uroot -p --single-transaction --databases <db> > /backup/pre_change.sql

# 3) 看表多大(超过 500 万行就要评估在线 DDL 方案)
mysql -uroot -p -e "SELECT table_name, table_rows, ROUND((data_length+index_length)/1024/1024) mb FROM information_schema.tables WHERE table_schema='<db>';"

# 4) 看有没有长事务与锁等待
mysql -uroot -p -e "SELECT * FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 5;"

# 5) 低峰执行 + 观察主从延迟
mysql -uroot -p -e "SHOW REPLICA STATUS\G" | grep Seconds_Behind

17. 附录:常用 SQL 与参数速查

17.1 一分钟巡检脚本

#!/usr/bin/env bash
# 用法:./mysql_health.sh  (依赖 ~/.my.cnf 免密)
set -euo pipefail

mysql -N -B -e "
SELECT '版本', VERSION()
UNION ALL SELECT '运行时长(小时)', ROUND(VARIABLE_VALUE/3600,1) FROM performance_schema.global_status WHERE VARIABLE_NAME='Uptime'
UNION ALL SELECT 'QPS', ROUND((SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Questions')/
                              (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Uptime'),1)
UNION ALL SELECT '当前连接', VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Threads_connected'
UNION ALL SELECT '活跃线程', VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Threads_running'
UNION ALL SELECT '最大连接数', VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Max_used_connections'
UNION ALL SELECT '慢查询数', VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Slow_queries'
UNION ALL SELECT '锁等待次数', VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_row_lock_waits'
UNION ALL SELECT '死锁次数', VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_deadlocks'
UNION ALL SELECT '数据总量(GB)', ROUND(SUM(data_length+index_length)/1024/1024/1024,2) FROM information_schema.tables;
"

echo "--- 最大的 3 张表 ---"
mysql -e "SELECT table_schema, table_name, ROUND((data_length+index_length)/1024/1024/1024,2) AS gb
          FROM information_schema.tables
          ORDER BY (data_length+index_length) DESC LIMIT 3;"

echo "--- 非 Sleep 会话 ---"
mysql -e "SELECT id,user,host,db,command,time,LEFT(info,80) sql FROM information_schema.processlist WHERE command<>'Sleep' ORDER BY time DESC LIMIT 10;"

echo "--- 长事务 ---"
mysql -e "SELECT trx_id,TIMESTAMPDIFF(MINUTE,trx_started,NOW()) min,trx_rows_locked,LEFT(trx_query,60) sql FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 5;"

17.2 常用单行命令

mysqladmin -uroot -p status                  # 一行看 QPS/TPS
mysqladmin -uroot -p extended-status | grep -E 'Threads_|Innodb_row_lock'
mysqladmin -uroot -p variables | grep -E 'max_connections|buffer_pool'
mysqladmin -uroot -p ping
mysqladmin -uroot -p processlist
mysqladmin -uroot -p kill <id>
mysqladmin -uroot -p flush-logs
mysqladmin -uroot -p flush-hosts
mysqladmin -uroot -p shutdown

17.3 常用 SQL 速查

-- 当前库表数量与大小
SELECT table_schema, COUNT(*) tables, ROUND(SUM(data_length+index_length)/1024/1024,1) mb
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','sys','performance_schema','information_schema')
GROUP BY table_schema ORDER BY mb DESC;

-- 找没有主键的表(复制延迟重灾区)
SELECT table_schema, table_name FROM information_schema.tables t
WHERE table_schema NOT IN ('mysql','sys','performance_schema','information_schema')
  AND NOT EXISTS (SELECT 1 FROM information_schema.statistics s
                  WHERE s.table_schema=t.table_schema AND s.table_name=t.table_name AND s.index_name='PRIMARY');

-- 找冗余/重复索引(索引名不同但列相同)
SELECT table_schema, table_name, GROUP_CONCAT(index_name) idx, GROUP_CONCAT(column_name ORDER BY seq_in_index) cols
FROM information_schema.statistics
WHERE table_schema NOT IN ('mysql','sys','performance_schema','information_schema')
GROUP BY table_schema, table_name, cols HAVING COUNT(DISTINCT index_name) > 1;

-- 找最大的 10 个索引
SELECT table_schema, table_name, index_name, ROUND(stat_value*@@innodb_page_size/1024/1024,1) mb
FROM mysql.innodb_index_stats
WHERE stat_name='size' AND database_name NOT IN ('mysql','sys')
ORDER BY stat_value DESC LIMIT 10;

-- 表碎片率
SELECT table_schema, table_name,
       ROUND(data_free/1024/1024,1) free_mb,
       ROUND(data_free/(data_length+index_length+1)*100,1) frag_pct
FROM information_schema.tables
WHERE data_free > 100*1024*1024
ORDER BY data_free DESC;

-- 账号清单
SELECT user, host, plugin, account_locked, password_expired FROM mysql.user ORDER BY user, host;

-- 权限反查:谁有这个库的权限
SELECT GRANTEE, PRIVILEGE_TYPE FROM information_schema.schema_privileges WHERE TABLE_SCHEMA='appdb';

17.4 参数默认值速查(8.4)

参数 默认值
max_connections 151
innodb_buffer_pool_size 134217728(128M)
innodb_redo_log_capacity 104857600(100M)
innodb_flush_log_at_trx_commit 1
sync_binlog 1
binlog_expire_logs_seconds 2592000(30 天)
character_set_server utf8mb4
collation_server utf8mb4_0900_ai_ci
transaction_isolation REPEATABLE-READ
default_authentication_plugin(8.0)/ authentication_policy(8.4) caching_sha2_password
sql_mode ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
local_infile OFF
innodb_file_per_table ON
slow_query_log OFF(生产必须手动开)

17.5 常用端口

端口 用途
3306 MySQL 默认端口
33060 X Protocol(MySQL Shell / X DevAPI)
33061 MGR 组成员通信
6446 / 6447 MySQL Router 读写 / 只读

17.6 关键目录

路径 内容
/var/lib/mysql RHEL 包默认 datadir
/var/lib/mysql-files secure_file_priv 默认目录
/var/log/mysqld.log RHEL 包默认错误日志
/etc/my.cnf RHEL 系主配置
/etc/mysql/my.cnf Debian 系主配置
/etc/mysql/mysql.conf.d/ Debian 系分片配置目录
/etc/yum.repos.d/mysql-community.repo YUM 仓库配置
/etc/apt/sources.list.d/mysql.list APT 仓库配置

附:与工作区其他手册的配合

  • 操作系统层(服务、磁盘、防火墙、SELinux):见 CentOS-7-10-常规运维指令手册.md / Ubuntu-常用运维指令手册.md
  • MySQL 装完后必须放行的端口:firewall-cmd --add-service=mysql --permanent(RHEL)或 ufw allow 3306/tcp(Ubuntu)
  • SELinux 开启时 datadir 变更需 semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?" + restorecon -Rv /data/mysql