16941 字
85 分钟
MySQL数据库指南(合集篇):从基础指令到生产实战

系统运维人员MySQL数据库完全掌握指南:从基础指令到生产实战#

作为一名系统运维工程师,无论你负责的是Web应用、微服务架构还是大数据平台,MySQL数据库几乎是绕不开的核心组件。根据DB-Engines 2026年最新排名,MySQL依然稳居全球最受欢迎的开源数据库榜首,市场份额超过40%。在生产环境中,MySQL的稳定性、性能和安全性直接决定了整个业务系统的可用性。

很多运维人员对MySQL的认知停留在”会安装、会重启、会备份”的基础层面,但在实际生产环境中,你可能会遇到各种复杂问题:数据库突然变慢、主从同步中断、数据意外删除、连接数打满、死锁频发等等。这些问题如果处理不当,轻则导致业务响应缓慢,重则造成数据丢失和服务长时间中断。

本文将系统梳理系统运维人员必须掌握的MySQL核心能力体系,并详细讲解生产环境中最常用的MySQL指令及其应用场景,帮助你从”MySQL使用者”成长为”MySQL运维专家”。

本文适用对象
  • 系统运维工程师(1-5年工作经验)
  • 数据库管理员(DBA)
  • DevOps工程师
  • 后端开发人员(需要了解数据库运维)

一、运维人员MySQL核心能力体系#

1.1 MySQL基础架构与原理#

在深入学习指令之前,你必须先理解MySQL的基础架构,这是所有运维操作的理论基础。

MySQL采用客户端/服务器架构,主要由以下几个部分组成:

连接层(Connection Layer)

  • 处理客户端连接请求
  • 身份验证和权限校验
  • 连接线程管理
  • 连接池维护

服务层(Service Layer)

  • 查询解析器(Parser):将SQL语句解析成语法树
  • 查询优化器(Optimizer):选择最优的执行计划
  • 查询缓存(Query Cache):缓存查询结果(MySQL 8.0已移除)
  • 存储过程、触发器、视图等功能模块

存储引擎层(Storage Engine Layer)

  • InnoDB:支持事务、外键、行级锁,默认引擎
  • MyISAM:不支持事务,表级锁,读性能好
  • Memory:数据存储在内存中,速度快但重启丢失
  • Archive:高度压缩,适合历史归档数据

文件系统层(File System Layer)

  • 数据文件(.ibd、.frm)
  • 日志文件(redo log、undo log、binlog、error log、slow log)
  • 配置文件(my.cnf/my.ini)
重点关注InnoDB

对于运维人员来说,最重要的是理解InnoDB存储引擎的架构,因为它是MySQL 5.5及以上版本的默认引擎,也是生产环境中使用最广泛的引擎。

InnoDB核心架构详解#

内存结构(In-Memory Structures)

  1. Buffer Pool(缓冲池):最重要的内存区域,用于缓存表数据和索引数据

    • 默认大小为128MB,生产环境建议设置为物理内存的50%-70%
    • 采用LRU算法进行页面淘汰
    • 包含数据页、索引页、undo页、插入缓冲、自适应哈希索引等
  2. Change Buffer(写缓冲):缓存对非唯一二级索引的DML操作

    • 减少随机I/O,提高写入性能
    • 默认占用Buffer Pool的25%
  3. Adaptive Hash Index(自适应哈希索引):InnoDB自动创建的内存哈希索引

    • 监控索引访问模式,自动为热点数据建立哈希索引
    • 可以通过innodb_adaptive_hash_index参数控制
  4. Log Buffer(日志缓冲区):存储即将写入redo log的数据

    • 默认大小为16MB
    • 通过innodb_log_buffer_size参数配置

磁盘结构(On-Disk Structures)

  1. System Tablespace(系统表空间):存储InnoDB数据字典、undo log、change buffer等

    • 文件名为ibdata1、ibdata2等
    • 可以通过innodb_data_file_path配置
  2. File-Per-Table Tablespaces(独立表空间):每个表一个.ibd文件

    • 通过innodb_file_per_table=ON启用(MySQL 5.6.6+默认开启)
    • 便于管理和空间回收
  3. Redo Log Files(重做日志):记录数据页的物理修改

    • 用于崩溃恢复,保证数据持久性
    • 默认有ib_logfile0和ib_logfile1两个文件
    • 循环写入,通过innodb_log_file_size和innodb_log_files_in_group配置
  4. Undo Log Files(回滚日志):记录事务修改前的数据

    • 用于事务回滚和MVCC
    • 存储在系统表空间或独立的undo表空间中

InnoDB事务与锁机制#

ACID特性实现

  • 原子性(Atomicity):通过undo log实现
  • 一致性(Consistency):通过数据库约束和应用层保证
  • 隔离性(Isolation):通过MVCC和锁机制实现
  • 持久性(Durability):通过redo log和doublewrite buffer实现

四种隔离级别

查看和设置隔离级别
-- 查看当前隔离级别
SELECT @@transaction_isolation;
SELECT @@global.transaction_isolation;
-- 设置会话级别隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
隔离级别脏读不可重复读幻读说明
READ UNCOMMITTED是是是最低级别,不推荐
READ COMMITTED否是是Oracle默认级别
REPEATABLE READ否否是(InnoDB通过间隙锁解决)MySQL默认级别
SERIALIZABLE否否否最高级别,性能最差
生产环境隔离级别选择

大多数生产环境使用MySQL默认的REPEATABLE READ隔离级别。如果对读一致性要求不高,可以使用READ COMMITTED来获得更好的并发性能。

锁的类型与应用

查看当前锁信息
-- 查看当前持有的锁
SELECT * FROM performance_schema.data_locks\G
-- 查看锁等待情况
SELECT * FROM performance_schema.data_lock_waits\G
-- 查看InnoDB锁状态(老版本)
SHOW ENGINE INNODB STATUS\G

1.2 安装部署与配置管理#

这是运维人员最基础的技能,但也是最容易出错的环节。

多种安装方式对比#

安装方式优点缺点适用场景
YUM/APT包管理器简单快速,自动解决依赖版本可能较旧快速测试环境
RPM/DEB包版本可控,便于管理需手动解决依赖标准化部署
二进制包无需编译,开箱即用体积较大生产环境推荐
源码编译可自定义配置,性能最优编译耗时,复杂度高特殊需求场景
Docker容器快速部署,环境隔离持久化需注意开发测试环境

YUM方式安装MySQL 8.0(CentOS/RHEL)#

安装MySQL 8.0社区版
# 1. 下载MySQL官方YUM仓库
wget https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm
# 2. 安装YUM仓库
sudo rpm -Uvh mysql80-community-release-el7-7.noarch.rpm
# 3. 查看可用的MySQL版本
yum repolist all | grep mysql
# 4. 安装MySQL服务器
sudo yum install mysql-community-server -y
# 5. 启动MySQL服务
sudo systemctl start mysqld
sudo systemctl enable mysqld
# 6. 查看初始root密码(MySQL 8.0会生成临时密码)
sudo grep 'temporary password' /var/log/mysqld.log
# 7. 使用临时密码登录并修改密码
mysql -uroot -p
# {8}
# 8. 修改root密码(需要满足密码强度要求)
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStrongPassword123!';
FLUSH PRIVILEGES;
初始密码安全

MySQL 8.0首次启动时会生成一个临时密码,必须立即修改。如果找不到临时密码,可以通过--skip-grant-tables选项跳过权限表启动MySQL,然后重置密码。

二进制包安装MySQL(推荐生产环境)#

二进制包安装MySQL 8.0
# 1. 创建mysql用户和组
groupadd mysql
useradd -r -g mysql -s /bin/false mysql
# 2. 下载并解压二进制包
cd /usr/local
wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.35-linux-glibc2.28-x86_64.tar.xz
tar xvf mysql-8.0.35-linux-glibc2.28-x86_64.tar.xz
ln -s mysql-8.0.35-linux-glibc2.28-x86_64 mysql
# 3. 创建数据目录
mkdir -p /data/mysql/{data,logs,tmp}
chown -R mysql:mysql /data/mysql
# 4. 创建配置文件
cat > /etc/my.cnf << 'EOF'
[mysqld]
# 基础配置
user = mysql
port = 3306
basedir = /usr/local/mysql
datadir = /data/mysql/data
socket = /tmp/mysql.sock
pid-file = /data/mysql/mysql.pid
# 字符集配置
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# InnoDB配置
innodb_buffer_pool_size = 2G
innodb_log_file_size = 512M
innodb_log_files_in_group = 2
innodb_flush_log_at_trx_commit = 2
innodb_file_per_table = ON
innodb_data_file_path = ibdata1:100M:autoextend
# 日志配置
log_error = /data/mysql/logs/error.log
slow_query_log = ON
slow_query_log_file = /data/mysql/logs/slow.log
long_query_time = 2
log_queries_not_using_indexes = ON
# 连接配置
max_connections = 500
max_connect_errors = 100
wait_timeout = 300
interactive_timeout = 300
# 其他优化
skip_name_resolve = ON
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
EOF
# 5. 初始化数据库
cd /usr/local/mysql
bin/mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql/data
# 6. 查看初始密码
grep 'temporary password' /data/mysql/logs/error.log
# 7. 配置systemd服务
cat > /etc/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 --defaults-file=/etc/my.cnf
ExecStop=/usr/local/mysql/bin/mysqladmin -uroot -p shutdown
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
# 8. 启动MySQL服务
systemctl daemon-reload
systemctl start mysqld
systemctl enable mysqld
# 9. 配置环境变量
echo 'export PATH=$PATH:/usr/local/mysql/bin' >> /etc/profile
source /etc/profile

my.cnf核心参数详解#

/etc/my.cnf 生产环境配置模板
[client]
port = 3306
socket = /tmp/mysql.sock
[mysqld]
# ============ 基础配置 ============
user = mysql
port = 3306
basedir = /usr/local/mysql
datadir = /data/mysql/data
socket = /tmp/mysql.sock
pid-file = /data/mysql/mysql.pid
tmpdir = /data/mysql/tmp
# ============ 字符集配置 ============
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'
# ============ 连接配置 ============
max_connections = 500 # 最大连接数
max_connect_errors = 100 # 最大错误连接数
max_allowed_packet = 64M # 最大数据包大小
wait_timeout = 300 # 连接空闲超时时间(秒)
interactive_timeout = 300 # 交互式连接超时时间(秒)
connect_timeout = 10 # 连接超时时间(秒)
# ============ InnoDB核心配置 ============
innodb_buffer_pool_size = 4G # 缓冲池大小,设置为物理内存的50%-70%
innodb_buffer_pool_instances = 4 # 缓冲池实例数,建议1G配置1个实例
innodb_log_file_size = 512M # 重做日志大小
innodb_log_files_in_group = 2 # 重做日志文件数量
innodb_log_buffer_size = 16M # 日志缓冲区大小
innodb_flush_log_at_trx_commit = 2 # 日志刷盘策略(0最快但不安全,1最安全但最慢,2折中)
innodb_flush_method = O_DIRECT # 文件刷盘方法
innodb_file_per_table = ON # 每个表使用独立表空间
innodb_data_file_path = ibdata1:100M:autoextend # 系统表空间配置
innodb_autoextend_increment = 64 # 自动扩展增量(MB)
innodb_open_files = 1000 # InnoDB最大打开文件数
innodb_io_capacity = 2000 # 磁盘IO能力
innodb_io_capacity_max = 4000 # 磁盘IO能力上限
innodb_read_io_threads = 4 # 读IO线程数
innodb_write_io_threads = 4 # 写IO线程数
# ============ 日志配置 ============
log_error = /data/mysql/logs/error.log
log_error_verbosity = 2 # 错误日志详细程度(1-3)
# 慢查询日志
slow_query_log = ON
slow_query_log_file = /data/mysql/logs/slow.log
long_query_time = 2 # 慢查询阈值(秒)
log_queries_not_using_indexes = ON # 记录未使用索引的查询
log_throttle_queries_not_using_indexes = 10 # 限制未使用索引日志的记录频率
# 二进制日志(用于主从复制和时间点恢复)
log_bin = /data/mysql/logs/mysql-bin
binlog_format = ROW # 二进制日志格式(ROW/STATEMENT/MIXED)
binlog_expire_logs_seconds = 604800 # binlog保留时间(秒,7天)
max_binlog_size = 1G # 单个binlog文件最大大小
sync_binlog = 1 # binlog同步频率(0最快,1最安全)
# ============ 复制配置 ============
server-id = 1 # 服务器ID,主从复制环境中必须唯一
gtid_mode = ON # 启用GTID
enforce_gtid_consistency = ON # 强制GTID一致性
log_slave_updates = ON # 从库是否记录复制的binlog
# ============ 性能优化配置 ============
skip_name_resolve = ON # 跳过DNS解析,提高连接速度
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION # SQL模式
transaction_isolation = REPEATABLE-READ # 事务隔离级别
query_cache_type = 0 # MySQL 8.0已移除Query Cache
table_open_cache = 2000 # 表缓存数量
table_open_cache_instances = 16 # 表缓存实例数
open_files_limit = 10000 # 最大打开文件数
thread_cache_size = 64 # 线程缓存大小
tmp_table_size = 64M # 内存临时表大小
max_heap_table_size = 64M # 内存表最大大小
# ============ 安全配置 ============
# local_infile = OFF # 禁用LOAD DATA LOCAL INFILE
# secure_file_priv = /data/mysql/secure # 限制导入导出文件路径
[mysql]
default-character-set = utf8mb4
[mysqldump]
max_allowed_packet = 64M
default-character-set = utf8mb4
关键参数说明
  • innodb_buffer_pool_size:最重要的参数,建议设置为物理内存的50%-70%
  • innodb_flush_log_at_trx_commit:控制数据安全性和性能的平衡
    • 0:每秒刷新一次(最快但可能丢失1秒数据)
    • 1:每次事务提交刷新(最安全但最慢,默认值)
    • 2:每次事务提交写入OS缓存,每秒刷盘(折中方案)
  • max_connections:根据实际并发量设置,过大会消耗过多内存

系统层面优化(Linux)#

Linux系统优化脚本
#!/bin/bash
# MySQL服务器系统层面优化脚本
# 1. 修改文件句柄限制
cat >> /etc/security/limits.conf << 'EOF'
mysql soft nofile 65535
mysql hard nofile 65535
mysql soft nproc 65535
mysql hard nproc 65535
EOF
# 2. 修改内核参数
cat >> /etc/sysctl.conf << 'EOF'
# MySQL优化参数
vm.swappiness = 10 # 降低swap使用
net.ipv4.ip_local_port_range = 10000 65535 # 扩大端口范围
net.ipv4.tcp_max_syn_backlog = 4096 # SYN队列长度
net.core.somaxconn = 4096 # socket监听队列长度
net.ipv4.tcp_fin_timeout = 30 # TIME_WAIT超时时间
net.ipv4.tcp_keepalive_time = 300 # TCP keepalive时间
net.ipv4.tcp_tw_reuse = 1 # 复用TIME_WAIT连接
EOF
sysctl -p
# 3. 设置IO调度算法(SSD使用noop或deadline,机械盘使用deadline)
echo deadline > /sys/block/sda/queue/scheduler
# 4. 关闭transparent_hugepage(透明大页)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 永久生效(添加到rc.local)
cat >> /etc/rc.local << 'EOF'
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
EOF
chmod +x /etc/rc.local
# 5. 配置numa(如果是多NUMA节点服务器)
numactl --interleave=all mysqld_safe &

1.3 用户权限与安全管理#

数据库安全是运维工作的重中之重,一旦数据库被入侵,后果不堪设想。

用户管理最佳实践#

用户创建和权限管理实战
-- 1. 创建应用专用用户(限制IP段访问)
CREATE USER 'appuser'@'192.168.1.%'
IDENTIFIED WITH mysql_native_password BY 'AppUser@2026!Strong';
-- 2. 创建只读用户(用于报表查询)
CREATE USER 'readonly'@'10.0.%.%'
IDENTIFIED BY 'ReadOnly@2026!';
-- 3. 创建备份专用用户
CREATE USER 'backup'@'localhost'
IDENTIFIED BY 'Backup@2026!';
-- 4. 授予应用用户基本权限(遵循最小权限原则)
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'appuser'@'192.168.1.%';
-- 5. 授予只读用户查询权限
GRANT SELECT ON myapp.* TO 'readonly'@'10.0.%.%';
-- 6. 授予备份用户必要权限
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER, RELOAD
ON *.* TO 'backup'@'localhost';
-- 7. 刷新权限表
FLUSH PRIVILEGES;
-- 8. 查看用户权限
SHOW GRANTS FOR 'appuser'@'192.168.1.%';
SHOW GRANTS FOR 'readonly'@'10.0.%.%';
-- 9. 撤销权限示例
REVOKE DELETE ON myapp.* FROM 'appuser'@'192.168.1.%';
-- 10. 修改用户密码
ALTER USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'NewPassword@2026!';
-- 11. 设置密码过期时间(90天)
ALTER USER 'appuser'@'192.168.1.%' PASSWORD EXPIRE INTERVAL 90 DAY;
-- 12. 锁定用户
ALTER USER 'suspicious_user'@'%' ACCOUNT LOCK;
-- 13. 解锁用户
ALTER USER 'suspicious_user'@'%' ACCOUNT UNLOCK;
-- 14. 删除用户
DROP USER 'olduser'@'%';

密码策略配置#

配置密码复杂度策略
-- 查看密码验证组件
SHOW VARIABLES LIKE 'validate_password%';
-- 设置密码策略
-- LOW: 只检查密码长度
-- MEDIUM: 检查长度、数字、大小写、特殊字符
-- STRONG: 检查长度、数字、大小写、特殊字符、字典文件
SET GLOBAL validate_password.policy = 'MEDIUM';
-- 设置密码最小长度
SET GLOBAL validate_password.length = 12;
-- 设置密码必须包含的数字个数
SET GLOBAL validate_password.number_count = 2;
-- 设置密码必须包含的小写字母个数
SET GLOBAL validate_password.mixed_case_count = 1;
-- 设置密码必须包含的特殊字符个数
SET GLOBAL validate_password.special_char_count = 1;
生产环境安全准则
  1. 禁止使用root用户直接连接应用:为每个应用创建专用数据库用户
  2. 禁止使用%通配符:精确限制访问来源IP或网段
  3. 启用SSL加密连接:保护数据传输安全
  4. 定期审计用户权限:每季度检查一次用户权限,删除无用账号
  5. 启用审计日志:记录所有敏感操作

SSL加密连接配置#

配置MySQL SSL加密
# 1. 生成SSL证书
cd /data/mysql/ssl
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
# 2. 在my.cnf中配置SSL
cat >> /etc/my.cnf << 'EOF'
[mysqld]
ssl_ca = /data/mysql/ssl/ca-cert.pem
ssl_cert = /data/mysql/ssl/server-cert.pem
ssl_key = /data/mysql/ssl/server-key.pem
require_secure_transport = ON
EOF
# 3. 重启MySQL
systemctl restart mysqld
强制用户使用SSL连接
-- 创建要求SSL连接的用户
CREATE USER 'secureuser'@'%'
IDENTIFIED BY 'SecurePass@2026!'
REQUIRE SSL;
-- 修改现有用户要求SSL
ALTER USER 'appuser'@'192.168.1.%' REQUIRE SSL;
-- 验证SSL连接状态
SHOW STATUS LIKE 'Ssl_cipher';
\s

1.4 备份与恢复#

备份是数据安全的最后一道防线,没有经过恢复测试的备份等于没有备份。

备份策略对比#

备份方式优点缺点适用场景
mysqldump逻辑备份灵活、可读、跨平台速度慢、恢复慢、锁表小型数据库、开发测试
xtrabackup物理备份快速、热备、增量依赖存储引擎、版本兼容性大型生产数据库
LVM快照备份快速、几乎无锁需要LVM、恢复复杂特定场景
云厂商快照备份简单、可靠依赖云平台云上数据库
主从复制实时、高可用不是真正的备份配合其他备份方式

mysqldump逻辑备份详解#

mysqldump备份脚本
#!/bin/bash
# MySQL自动备份脚本
# 作者: 运维团队
# 功能: 每日全量备份+自动清理过期备份
# 配置变量
BACKUP_DIR="/data/backup/mysql"
MYSQL_USER="backup"
MYSQL_PASSWORD="Backup@2026!"
MYSQL_HOST="localhost"
RETENTION_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/mysql_backup.log"
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份所有数据库(排除系统库)
echo "[$(date)] 开始备份MySQL数据库..." >> $LOG_FILE
mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASSWORD \
-e "SHOW DATABASES;" | grep -Ev "Database|information_schema|performance_schema|mysql|sys" | \
while read dbname; do
echo "[$(date)] 备份数据库: $dbname" >> $LOG_FILE
mysqldump -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASSWORD \
--single-transaction \
--routines \
--triggers \
--events \
--hex-blob \
--quick \
--max_allowed_packet=512M \
--default-character-set=utf8mb4 \
$dbname | gzip > $BACKUP_DIR/${dbname}_${DATE}.sql.gz
if [ $? -eq 0 ]; then
echo "[$(date)] 备份成功: $dbname" >> $LOG_FILE
else
echo "[$(date)] 备份失败: $dbname" >> $LOG_FILE
# 发送告警邮件或钉钉通知
fi
done
# 删除过期备份
echo "[$(date)] 清理过期备份文件..." >> $LOG_FILE
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "[$(date)] 备份任务完成" >> $LOG_FILE
恢复mysqldump备份
# 1. 解压备份文件
gunzip myapp_20260519_030000.sql.gz
# 2. 创建数据库(如果不存在)
mysql -uroot -p -e "CREATE DATABASE IF NOT EXISTS myapp DEFAULT CHARACTER SET utf8mb4;"
# 3. 恢复数据
mysql -uroot -p myapp < myapp_20260519_030000.sql
# 4. 验证恢复结果
mysql -uroot -p -e "USE myapp; SHOW TABLES; SELECT COUNT(*) FROM users;"

Percona XtraBackup物理备份(推荐)#

percona
/
percona-xtrabackup
Waiting for api.github.com...
00K
0K
0K
Waiting...
XtraBackup全量备份
#!/bin/bash
# XtraBackup全量备份脚本
BACKUP_DIR="/data/backup/xtrabackup"
MYSQL_USER="backup"
MYSQL_PASSWORD="Backup@2026!"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_DIR="$BACKUP_DIR/full/$DATE"
# 创建备份目录
mkdir -p $FULL_BACKUP_DIR
# 执行全量备份
xtrabackup --user=$MYSQL_USER --password=$MYSQL_PASSWORD \
--backup \
--target-dir=$FULL_BACKUP_DIR \
--parallel=4 \
--compress \
--compress-threads=4
# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "全量备份成功: $FULL_BACKUP_DIR"
# 可以在这里添加备份文件上传到对象存储的逻辑
else
echo "全量备份失败"
exit 1
fi
XtraBackup增量备份
#!/bin/bash
# XtraBackup增量备份脚本
BACKUP_DIR="/data/backup/xtrabackup"
MYSQL_USER="backup"
MYSQL_PASSWORD="Backup@2026!"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_DIR="/data/backup/xtrabackup/full/20260519_030000"
INC_BACKUP_DIR="$BACKUP_DIR/inc/$DATE"
# 创建增量备份目录
mkdir -p $INC_BACKUP_DIR
# 执行增量备份(基于最新的全量备份)
xtrabackup --user=$MYSQL_USER --password=$MYSQL_PASSWORD \
--backup \
--target-dir=$INC_BACKUP_DIR \
--incremental-basedir=$FULL_BACKUP_DIR \
--parallel=4 \
--compress \
--compress-threads=4
if [ $? -eq 0 ]; then
echo "增量备份成功: $INC_BACKUP_DIR"
else
echo "增量备份失败"
exit 1
fi
XtraBackup恢复流程
#!/bin/bash
# XtraBackup恢复脚本
FULL_BACKUP_DIR="/data/backup/xtrabackup/full/20260519_030000"
INC1_BACKUP_DIR="/data/backup/xtrabackup/inc/20260519_090000"
INC2_BACKUP_DIR="/data/backup/xtrabackup/inc/20260519_150000"
MYSQL_DATADIR="/data/mysql/data"
# 1. 停止MySQL服务
systemctl stop mysqld
# 2. 解压备份文件(如果使用了压缩)
xtrabackup --decompress --target-dir=$FULL_BACKUP_DIR
xtrabackup --decompress --target-dir=$INC1_BACKUP_DIR
xtrabackup --decompress --target-dir=$INC2_BACKUP_DIR
# 3. 准备全量备份
xtrabackup --prepare --apply-log-only --target-dir=$FULL_BACKUP_DIR
# 4. 应用第一个增量备份
xtrabackup --prepare --apply-log-only \
--target-dir=$FULL_BACKUP_DIR \
--incremental-dir=$INC1_BACKUP_DIR
# 5. 应用第二个增量备份(最后一个增量不加--apply-log-only)
xtrabackup --prepare \
--target-dir=$FULL_BACKUP_DIR \
--incremental-dir=$INC2_BACKUP_DIR
# 6. 备份当前数据目录
mv $MYSQL_DATADIR ${MYSQL_DATADIR}.old
# 7. 恢复数据
xtrabackup --copy-back --target-dir=$FULL_BACKUP_DIR
# 8. 修改权限
chown -R mysql:mysql $MYSQL_DATADIR
# 9. 启动MySQL
systemctl start mysqld
# 10. 验证恢复
mysql -uroot -p -e "SHOW DATABASES; SELECT NOW();"
备份恢复最佳实践
  1. 定期测试恢复流程:每月至少进行一次完整的恢复演练
  2. 异地存储备份:备份文件必须存储在与数据库不同的物理位置
  3. 监控备份任务:使用监控工具监控备份任务执行状态
  4. 保留多版本备份:至少保留最近7天的全量备份和30天的增量备份
  5. 记录备份日志:详细记录每次备份的时间、大小、位置等信息

时间点恢复(Point-In-Time Recovery)#

使用binlog进行时间点恢复
# 场景:误删除数据,需要恢复到删除前的状态
# 1. 确定binlog文件和位置
# 假设误删除发生在2026-05-19 14:30:00
# 2. 恢复最近的全量备份
mysql -uroot -p myapp < myapp_backup_20260519.sql
# 3. 查看binlog文件列表
mysqlbinlog --base64-output=decode-rows -v /data/mysql/logs/mysql-bin.000010 | less
# 4. 找到误删除语句的位置(假设是position 12345)
# 5. 恢复误删除之前的binlog
mysqlbinlog --start-position=4 --stop-position=12345 \
/data/mysql/logs/mysql-bin.000010 | mysql -uroot -p myapp
# 6. 如果跨越多个binlog文件
mysqlbinlog --start-position=4 /data/mysql/logs/mysql-bin.000009 | mysql -uroot -p myapp
mysqlbinlog --stop-position=12345 /data/mysql/logs/mysql-bin.000010 | mysql -uroot -p myapp
# 7. 或者使用时间戳恢复
mysqlbinlog --start-datetime="2026-05-19 14:00:00" \
--stop-datetime="2026-05-19 14:29:59" \
/data/mysql/logs/mysql-bin.* | mysql -uroot -p myapp

1.5 性能监控与调优#

数据库性能直接影响业务体验,运维人员需要能够及时发现性能问题并进行优化。

性能监控指标体系#

系统层面监控

系统资源监控脚本
#!/bin/bash
# MySQL服务器性能监控脚本
echo "========== CPU使用率 =========="
top -bn1 | grep "Cpu(s)" | awk '{print "CPU使用率: " 100 - $8 "%"}'
echo ""
echo "========== 内存使用情况 =========="
free -h
echo ""
echo "========== 磁盘IO统计 =========="
iostat -x 1 3
echo ""
echo "========== MySQL进程资源使用 =========="
ps aux | grep mysqld | grep -v grep

MySQL层面监控

MySQL性能监控查询
-- 1. 查看连接数统计
SHOW STATUS LIKE 'Threads_connected'; -- 当前连接数
SHOW STATUS LIKE 'Threads_running'; -- 正在运行的线程数
SHOW STATUS LIKE 'Max_used_connections'; -- 最大连接数峰值
SHOW VARIABLES LIKE 'max_connections'; -- 最大连接数配置
-- 2. 查看QPS和TPS
SHOW GLOBAL STATUS LIKE 'Questions'; -- 总查询数
SHOW GLOBAL STATUS LIKE 'Com_select'; -- SELECT查询数
SHOW GLOBAL STATUS LIKE 'Com_insert'; -- INSERT查询数
SHOW GLOBAL STATUS LIKE 'Com_update'; -- UPDATE查询数
SHOW GLOBAL STATUS LIKE 'Com_delete'; -- DELETE查询数
SHOW GLOBAL STATUS LIKE 'Com_commit'; -- 提交事务数
SHOW GLOBAL STATUS LIKE 'Com_rollback'; -- 回滚事务数
-- 3. 查看缓冲池使用情况
SHOW STATUS LIKE 'Innodb_buffer_pool_pages%';
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
SELECT
(1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100
AS buffer_pool_hit_rate
FROM (
SELECT VARIABLE_VALUE AS Innodb_buffer_pool_reads
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'
) AS t1,
(
SELECT VARIABLE_VALUE AS Innodb_buffer_pool_read_requests
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'
) AS t2;
-- 4. 查看锁等待情况
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;
SHOW ENGINE INNODB STATUS\G
-- 5. 查看表锁情况
SHOW OPEN TABLES WHERE In_use > 0;
-- 6. 查看死锁信息
SHOW ENGINE INNODB STATUS\G -- 查看LATEST DETECTED DEADLOCK部分

实时性能监控工具#

综合性能监控查询
-- MySQL 8.0 性能监控视图
SELECT
-- 连接信息
(SELECT COUNT(*) FROM performance_schema.threads WHERE TYPE='FOREGROUND') AS current_connections,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Max_used_connections') AS max_used_connections,
(SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME='max_connections') AS max_connections,
-- QPS统计
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Questions') AS total_questions,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Com_select') AS total_selects,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Com_insert') AS total_inserts,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Com_update') AS total_updates,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Com_delete') AS total_deletes,
-- 缓冲池统计
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests') AS buffer_pool_read_requests,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') AS buffer_pool_reads,
ROUND((1 - (
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests')
)) * 100, 2) AS buffer_pool_hit_rate_pct\G
推荐监控工具
  • Prometheus + Grafana + mysqld_exporter:开源监控解决方案
  • PMM (Percona Monitoring and Management):专业MySQL监控工具
  • Zabbix:企业级监控平台
  • DataDog/New Relic:云端监控服务
  • pt-tools:Percona Toolkit工具集

1.6 高可用架构设计#

在生产环境中,单点故障是不可接受的,你需要设计和维护MySQL高可用架构。

主从复制架构配置#

主库配置
-- 1. 修改主库配置文件 /etc/my.cnf
-- [mysqld]
-- server-id = 1
-- log_bin = /data/mysql/logs/mysql-bin
-- binlog_format = ROW
-- gtid_mode = ON
-- enforce_gtid_consistency = ON
-- log_slave_updates = ON
-- 2. 创建复制用户
CREATE USER 'repl'@'192.168.1.%'
IDENTIFIED WITH mysql_native_password BY 'Repl@2026!Strong';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;
-- 3. 查看主库状态
SHOW MASTER STATUS\G
从库配置
-- 1. 修改从库配置文件 /etc/my.cnf
-- [mysqld]
-- server-id = 2
-- relay_log = /data/mysql/logs/mysql-relay-bin
-- read_only = ON
-- super_read_only = ON
-- gtid_mode = ON
-- enforce_gtid_consistency = ON
-- log_slave_updates = ON
-- 2. 配置主从复制(使用GTID)
STOP SLAVE;
CHANGE MASTER TO
MASTER_HOST='192.168.1.100',
MASTER_PORT=3306,
MASTER_USER='repl',
MASTER_PASSWORD='Repl@2026!Strong',
MASTER_AUTO_POSITION=1; -- 使用GTID自动定位
START SLAVE;
-- 3. 查看从库状态
SHOW SLAVE STATUS\G
-- 重点检查以下字段:
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0或很小的值
-- Last_IO_Error: 空
-- Last_SQL_Error: 空

主从复制故障排查#

常见主从复制问题排查
-- 1. 主从延迟过大
SHOW SLAVE STATUS\G
-- 查看延迟原因
-- 可能原因:
-- a) 从库配置低于主库
-- b) 从库有大量查询占用资源
-- c) 网络延迟
-- d) 主库写入压力过大
-- 解决方案:
-- 升级从库硬件、优化查询、启用并行复制
-- 2. 主从复制中断
SHOW SLAVE STATUS\G
-- 查看Last_IO_Error和Last_SQL_Error
-- 如果是SQL线程错误,可以跳过错误事务(慎用)
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;
-- 或者使用GTID跳过错误事务
STOP SLAVE;
SET GTID_NEXT='错误事务的GTID';
BEGIN;
COMMIT;
SET GTID_NEXT='AUTOMATIC';
START SLAVE;
-- 3. 主从数据不一致
-- 使用pt-table-checksum检查数据一致性
-- pt-table-checksum --host=主库IP --replicate=test.checksums
-- 使用pt-table-sync修复数据不一致
-- pt-table-sync --execute --sync-to-master 从库IP

MHA高可用方案#

yoshinorim
/
mha4mysql-manager
Waiting for api.github.com...
00K
0K
0K
Waiting...
MHA配置文件
# /etc/mha/app1.cnf
[server default]
# MySQL用户和密码
user=mha
password=MHA@2026!
ssh_user=root
# MHA工作目录
manager_workdir=/var/log/mha/app1
manager_log=/var/log/mha/app1/manager.log
remote_workdir=/var/log/mha/app1
# 复制用户
repl_user=repl
repl_password=Repl@2026!Strong
# 监控间隔
ping_interval=3
ping_type=CONNECT
# 主库切换脚本
master_ip_failover_script=/usr/local/bin/master_ip_failover
shutdown_script=""
[server1]
hostname=192.168.1.100
port=3306
candidate_master=1
check_repl_delay=0
[server2]
hostname=192.168.1.101
port=3306
candidate_master=1
check_repl_delay=0
[server3]
hostname=192.168.1.102
port=3306
no_master=1

1.7 故障排查与应急处理#

当数据库出现故障时,运维人员需要能够快速定位问题并恢复服务。

常见故障场景与处理#

1. 连接数打满故障处理
-- 查看当前连接数
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';
-- 查看所有连接详情
SHOW FULL PROCESSLIST;
-- 找出长时间Sleep的连接
SELECT
id, user, host, db, command, time, state, info
FROM information_schema.processlist
WHERE command = 'Sleep' AND time > 300
ORDER BY time DESC;
-- 杀掉空闲连接(批量kill)
SELECT
CONCAT('KILL ', id, ';') AS kill_cmd
FROM information_schema.processlist
WHERE command = 'Sleep' AND time > 300;
-- 临时增加最大连接数(重启后失效)
SET GLOBAL max_connections = 1000;
-- 优化wait_timeout参数(自动断开空闲连接)
SET GLOBAL wait_timeout = 300;
SET GLOBAL interactive_timeout = 300;
2. 慢查询导致数据库卡死
-- 查看正在执行的查询
SHOW FULL PROCESSLIST;
-- 查找耗时最长的查询
SELECT
id, user, host, db, command, time, state,
LEFT(info, 100) AS query_preview
FROM information_schema.processlist
WHERE command != 'Sleep'
ORDER BY time DESC
LIMIT 10;
-- 杀掉慢查询
KILL QUERY 进程ID; -- 只杀查询,不断开连接
KILL 进程ID; -- 杀进程并断开连接
-- 分析慢查询日志
-- pt-query-digest /var/log/mysql/slow.log
-- 使用EXPLAIN分析SQL
EXPLAIN SELECT ...;
3. 数据库无法启动故障排查
# 1. 查看错误日志
tail -100 /data/mysql/logs/error.log
# 常见启动失败原因:
# a) 端口被占用
netstat -tunlp | grep 3306
lsof -i:3306
# b) 数据目录权限问题
ls -la /data/mysql/data
chown -R mysql:mysql /data/mysql
# c) 配置文件错误
mysqld --help --verbose | grep my.cnf
mysqld --validate-config
# d) InnoDB数据损坏
# 在my.cnf中添加
innodb_force_recovery = 1 # 1-6,级别越高恢复力度越大,但数据丢失风险越高
# e) 磁盘空间不足
df -h
du -sh /data/mysql/*
# 2. 尝试安全模式启动
mysqld_safe --skip-grant-tables --skip-networking &
# 3. 查看系统日志
journalctl -xe | grep mysql
dmesg | grep -i mysql
4. 主从同步中断处理
-- 查看从库状态
SHOW SLAVE STATUS\G
-- 场景1:主库binlog被清理导致同步中断
-- 需要重新搭建主从复制
-- 1) 在主库做全量备份
-- 2) 恢复到从库
-- 3) 重新配置主从复制
-- 场景2:SQL线程错误(数据冲突)
-- 方法1:跳过错误(谨慎使用)
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;
-- 方法2:手动修复数据后重启复制
STOP SLAVE;
-- 手动修复数据...
START SLAVE;
-- 场景3:IO线程错误(网络问题)
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
-- 场景4:主从数据不一致
-- 使用pt-table-checksum + pt-table-sync工具修复

二、MySQL常用指令详解与实战#

2.1 连接与退出指令#

多种连接方式
# 1. 基本连接方式
mysql -u用户名 -p
# 2. 连接指定主机和端口
mysql -h192.168.1.100 -P3306 -uroot -p
# 3. 连接时指定数据库
mysql -h192.168.1.100 -uroot -p myapp
# 4. 使用SSL加密连接
mysql -h192.168.1.100 -uroot -p --ssl-mode=REQUIRED
# 5. 使用socket文件连接(本地连接更快)
mysql -uroot -p --socket=/tmp/mysql.sock
# 6. 执行SQL语句后立即退出
mysql -uroot -p -e "SHOW DATABASES;"
# 7. 从文件读取SQL并执行
mysql -uroot -p < backup.sql
# 8. 使用配置文件存储密码(避免明文密码)
cat > ~/.my.cnf << 'EOF'
[client]
user=root
password=YourPassword
host=localhost
EOF
chmod 600 ~/.my.cnf
mysql # 直接连接,无需输入密码
密码安全

绝不要在命令行中使用-p密码的方式输入密码,因为密码会被记录在shell历史中。应该使用以下安全方式:

  1. 使用-p后回车输入密码
  2. 使用~/.my.cnf配置文件
  3. 使用环境变量MYSQL_PWD(不推荐)
退出MySQL
-- 方式1(推荐)
exit;
-- 方式2
quit;
-- 方式3
\q
-- Ctrl + D 快捷键

2.2 数据库操作指令#

数据库管理完整示例
-- 1. 查看所有数据库
SHOW DATABASES;
-- 2. 查看数据库创建语句
SHOW CREATE DATABASE myapp\G
-- 3. 创建数据库(生产环境标准写法)
CREATE DATABASE IF NOT EXISTS myapp
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;
-- 4. 修改数据库字符集
ALTER DATABASE myapp
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- 5. 删除数据库(危险操作,需谨慎)
DROP DATABASE IF EXISTS old_database;
-- 6. 切换数据库
USE myapp;
-- 7. 查看当前所在数据库
SELECT DATABASE();
-- 8. 查看数据库大小
SELECT
table_schema AS 'Database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.tables
WHERE table_schema = 'myapp'
GROUP BY table_schema;

2.3 表操作指令#

表结构管理详解
-- 1. 查看所有表
SHOW TABLES;
-- 2. 查看表结构(多种方式)
DESC users;
DESCRIBE users;
SHOW COLUMNS FROM users;
-- 3. 查看表的详细创建语句
SHOW CREATE TABLE users\G
-- 4. 查看表的索引信息
SHOW INDEX FROM users;
-- 5. 查看表的统计信息
SHOW TABLE STATUS LIKE 'users'\G
-- 6. 创建表(完整示例)
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
email VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱',
password VARCHAR(255) NOT NULL COMMENT '密码哈希',
nickname VARCHAR(50) DEFAULT NULL COMMENT '昵称',
avatar VARCHAR(255) DEFAULT NULL COMMENT '头像URL',
status TINYINT DEFAULT 1 COMMENT '状态:0禁用 1正常',
last_login_at TIMESTAMP NULL DEFAULT NULL COMMENT '最后登录时间',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_status (status),
INDEX idx_created_at (created_at),
INDEX idx_last_login (last_login_at)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='用户表';
-- 7. 复制表结构(不复制数据)
CREATE TABLE users_backup LIKE users;
-- 8. 复制表结构和数据
CREATE TABLE users_backup AS SELECT * FROM users;
-- 9. 修改表结构 - 添加列
ALTER TABLE users
ADD COLUMN phone VARCHAR(20) DEFAULT NULL COMMENT '手机号'
AFTER email;
-- 10. 修改表结构 - 删除列
ALTER TABLE users DROP COLUMN old_column;
-- 11. 修改表结构 - 修改列类型
ALTER TABLE users
MODIFY COLUMN nickname VARCHAR(100) DEFAULT NULL COMMENT '昵称';
-- 12. 修改表结构 - 修改列名和类型
ALTER TABLE users
CHANGE COLUMN old_name new_name VARCHAR(50) DEFAULT NULL COMMENT '新字段';
-- 13. 添加主键
ALTER TABLE users ADD PRIMARY KEY (id);
-- 14. 删除主键
ALTER TABLE users DROP PRIMARY KEY;
-- 15. 添加索引
ALTER TABLE users ADD INDEX idx_username (username);
ALTER TABLE users ADD UNIQUE INDEX uniq_email (email);
ALTER TABLE users ADD FULLTEXT INDEX ft_nickname (nickname);
-- 16. 删除索引
ALTER TABLE users DROP INDEX idx_username;
-- 17. 添加外键约束
ALTER TABLE orders
ADD CONSTRAINT fk_user_id
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE ON UPDATE CASCADE;
-- 18. 删除外键约束
ALTER TABLE orders DROP FOREIGN KEY fk_user_id;
-- 19. 修改表名
ALTER TABLE users RENAME TO app_users;
RENAME TABLE app_users TO users;
-- 20. 修改表引擎
ALTER TABLE users ENGINE = InnoDB;
-- 21. 优化表(整理碎片)
OPTIMIZE TABLE users;
-- 22. 分析表(更新索引统计信息)
ANALYZE TABLE users;
-- 23. 检查表完整性
CHECK TABLE users;
-- 24. 修复表
REPAIR TABLE users;
-- 25. 删除表
DROP TABLE IF EXISTS old_table;
-- 26. 清空表(保留表结构)
TRUNCATE TABLE users; -- 快速,无法回滚
DELETE FROM users; -- 慢,可以回滚
表结构设计最佳实践
  1. 主键设计:优先使用自增整数作为主键,避免使用UUID(占用空间大)
  2. 字符集选择:统一使用utf8mb4字符集,支持emoji等特殊字符
  3. 字段命名:使用蛇形命名法(snake_case),避免使用MySQL保留字
  4. 合理使用NULL:尽量避免NULL字段,使用默认值代替
  5. 时间字段:使用TIMESTAMP或DATETIME,配合DEFAULT CURRENT_TIMESTAMP
  6. 添加注释:为表和字段添加详细的COMMENT

2.4 数据操作指令#

数据增删改查完整示例
-- ================ 插入数据 ================
-- 1. 插入单行数据
INSERT INTO users (username, email, password)
VALUES ('zhangsan', 'zhangsan@example.com', 'hashed_password_123');
-- 2. 插入多行数据(高效)
INSERT INTO users (username, email, password, nickname)
VALUES
('lisi', 'lisi@example.com', 'hashed_pwd_456', '李四'),
('wangwu', 'wangwu@example.com', 'hashed_pwd_789', '王五'),
('zhaoliu', 'zhaoliu@example.com', 'hashed_pwd_012', '赵六');
-- 3. 插入时忽略重复数据
INSERT IGNORE INTO users (username, email, password)
VALUES ('zhangsan', 'zhangsan@example.com', 'new_password');
-- 4. 插入时如果重复则更新
INSERT INTO users (username, email, password)
VALUES ('zhangsan', 'zhangsan@example.com', 'new_password')
ON DUPLICATE KEY UPDATE
password = VALUES(password),
updated_at = CURRENT_TIMESTAMP;
-- 5. 从另一个表插入数据
INSERT INTO users_backup (username, email, password)
SELECT username, email, password FROM users WHERE status = 1;
-- 6. 批量插入优化(使用事务)
START TRANSACTION;
INSERT INTO users (username, email, password) VALUES (...);
INSERT INTO users (username, email, password) VALUES (...);
-- ... 更多插入
COMMIT;
-- ================ 查询数据 ================
-- 7. 基本查询
SELECT * FROM users;
SELECT id, username, email FROM users;
-- 8. 条件查询
SELECT * FROM users WHERE status = 1;
SELECT * FROM users WHERE username = 'zhangsan' AND status = 1;
SELECT * FROM users WHERE username IN ('zhangsan', 'lisi', 'wangwu');
SELECT * FROM users WHERE username LIKE 'zhang%';
SELECT * FROM users WHERE created_at BETWEEN '2026-01-01' AND '2026-12-31';
SELECT * FROM users WHERE email IS NOT NULL;
-- 9. 排序查询
SELECT * FROM users ORDER BY created_at DESC;
SELECT * FROM users ORDER BY status ASC, created_at DESC;
-- 10. 分页查询(重要)
SELECT * FROM users LIMIT 20; -- 只取前20条
SELECT * FROM users LIMIT 20 OFFSET 40; -- 跳过40条,取20条
SELECT * FROM users LIMIT 40, 20; -- 等同于上面
-- 11. 聚合查询
SELECT COUNT(*) FROM users;
SELECT COUNT(DISTINCT username) FROM users;
SELECT SUM(amount), AVG(amount), MAX(amount), MIN(amount) FROM orders;
-- 12. 分组查询
SELECT status, COUNT(*) AS user_count
FROM users
GROUP BY status;
SELECT DATE(created_at) AS date, COUNT(*) AS daily_users
FROM users
GROUP BY DATE(created_at)
ORDER BY date DESC;
-- 13. HAVING子句(对分组结果进行过滤)
SELECT status, COUNT(*) AS user_count
FROM users
GROUP BY status
HAVING user_count > 100;
-- 14. 连接查询
-- INNER JOIN
SELECT u.username, o.order_no, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- LEFT JOIN
SELECT u.username, o.order_no, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- RIGHT JOIN
SELECT u.username, o.order_no, o.amount
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- 15. 子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);
SELECT * FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-- 16. UNION查询(合并多个查询结果)
SELECT username FROM users WHERE status = 1
UNION
SELECT username FROM users_backup WHERE status = 1;
-- 17. 去重查询
SELECT DISTINCT status FROM users;
-- 18. 计算字段
SELECT
username,
CONCAT(username, '@', email) AS full_info,
YEAR(created_at) AS register_year,
DATEDIFF(NOW(), created_at) AS days_since_register
FROM users;
-- ================ 更新数据 ================
-- 19. 基本更新(务必加WHERE条件!)
UPDATE users
SET password = 'new_hashed_password'
WHERE username = 'zhangsan';
-- 20. 批量更新
UPDATE users
SET status = 0
WHERE last_login_at < DATE_SUB(NOW(), INTERVAL 6 MONTH);
-- 21. 使用计算更新
UPDATE users
SET nickname = CONCAT('User_', id)
WHERE nickname IS NULL;
-- 22. 多表更新
UPDATE users u
INNER JOIN orders o ON u.id = o.user_id
SET u.total_amount = u.total_amount + o.amount
WHERE o.status = 'paid';
-- ================ 删除数据 ================
-- 23. 基本删除(务必加WHERE条件!)
DELETE FROM users WHERE id = 999;
-- 24. 批量删除
DELETE FROM users
WHERE status = 0 AND last_login_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);
-- 25. 使用子查询删除
DELETE FROM users
WHERE id IN (SELECT user_id FROM banned_users);
-- 26. 清空表(快速但无法回滚)
TRUNCATE TABLE temp_table;
数据操作安全提示

在生产环境执行UPDATE和DELETE前,必须遵循以下流程:

  1. 先执行SELECT:用同样的WHERE条件查询要操作的数据

    SELECT * FROM users WHERE status = 0 AND created_at < '2020-01-01';
  2. 确认数据无误后再执行UPDATE/DELETE

    DELETE FROM users WHERE status = 0 AND created_at < '2020-01-01';
  3. 使用事务保护:对于重要操作,先开启事务测试

    START TRANSACTION;
    DELETE FROM users WHERE ...;
    SELECT * FROM users; -- 验证结果
    ROLLBACK; -- 如果不对就回滚
    -- COMMIT; -- 确认无误后提交
  4. 定期备份:在执行重要操作前先备份相关表

2.5 用户权限管理指令#

用户权限管理实战
-- ================ 用户管理 ================
-- 1. 查看所有用户
SELECT user, host, account_locked, password_expired
FROM mysql.user;
-- 2. 查看当前用户
SELECT USER(), CURRENT_USER();
-- 3. 创建用户(不同场景)
-- 本地用户
CREATE USER 'localuser'@'localhost'
IDENTIFIED BY 'LocalPass@2026!';
-- 指定IP用户
CREATE USER 'appuser'@'192.168.1.100'
IDENTIFIED BY 'AppPass@2026!';
-- 指定IP段用户
CREATE USER 'appuser'@'192.168.1.%'
IDENTIFIED BY 'AppPass@2026!';
-- 任意IP用户(不推荐)
CREATE USER 'remoteuser'@'%'
IDENTIFIED BY 'RemotePass@2026!';
-- 使用特定认证插件
CREATE USER 'nativeuser'@'%'
IDENTIFIED WITH mysql_native_password BY 'NativePass@2026!';
-- ================ 权限管理 ================
-- 4. 授予权限(遵循最小权限原则)
-- 授予单个数据库的所有权限
GRANT ALL PRIVILEGES ON myapp.* TO 'appuser'@'192.168.1.%';
-- 授予特定操作权限
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'appuser'@'192.168.1.%';
-- 授予只读权限
GRANT SELECT ON myapp.* TO 'readonly'@'10.0.%.%';
-- 授予单个表的权限
GRANT SELECT, UPDATE ON myapp.users TO 'partialuser'@'%';
-- 授予复制权限
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.1.%';
-- 授予备份权限
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER, RELOAD
ON *.* TO 'backup'@'localhost';
-- 授予管理员权限
GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' WITH GRANT OPTION;
-- 5. 查看用户权限
SHOW GRANTS;
SHOW GRANTS FOR 'appuser'@'192.168.1.%';
-- 6. 撤销权限
REVOKE DELETE ON myapp.* FROM 'appuser'@'192.168.1.%';
REVOKE ALL PRIVILEGES ON *.* FROM 'olduser'@'%';
-- 7. 刷新权限(使权限立即生效)
FLUSH PRIVILEGES;
-- ================ 密码管理 ================
-- 8. 修改密码
-- 修改当前用户密码
ALTER USER USER() IDENTIFIED BY 'NewPassword@2026!';
-- 修改指定用户密码
ALTER USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'NewPassword@2026!';
-- 使用SET PASSWORD(老版本语法)
SET PASSWORD FOR 'appuser'@'192.168.1.%' = PASSWORD('NewPassword@2026!');
-- 9. 设置密码过期策略
-- 设置密码90天后过期
ALTER USER 'appuser'@'192.168.1.%' PASSWORD EXPIRE INTERVAL 90 DAY;
-- 设置密码永不过期
ALTER USER 'appuser'@'192.168.1.%' PASSWORD EXPIRE NEVER;
-- 使用全局默认策略
ALTER USER 'appuser'@'192.168.1.%' PASSWORD EXPIRE DEFAULT;
-- 立即过期(强制用户下次登录时修改密码)
ALTER USER 'appuser'@'192.168.1.%' PASSWORD EXPIRE;
-- 10. 查看密码策略
SHOW VARIABLES LIKE 'default_password_lifetime';
-- ================ 账户管理 ================
-- 11. 锁定/解锁用户
ALTER USER 'suspicious_user'@'%' ACCOUNT LOCK;
ALTER USER 'suspicious_user'@'%' ACCOUNT UNLOCK;
-- 12. 限制用户资源使用
ALTER USER 'limiteduser'@'%'
WITH MAX_QUERIES_PER_HOUR 1000
MAX_UPDATES_PER_HOUR 100
MAX_CONNECTIONS_PER_HOUR 50
MAX_USER_CONNECTIONS 10;
-- 13. 删除用户
DROP USER 'olduser'@'%';
DROP USER IF EXISTS 'tempuser'@'localhost';
-- ================ 角色管理(MySQL 8.0+) ================
-- 14. 创建角色
CREATE ROLE 'app_read', 'app_write', 'app_admin';
-- 15. 给角色授权
GRANT SELECT ON myapp.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON myapp.* TO 'app_write';
GRANT ALL PRIVILEGES ON myapp.* TO 'app_admin';
-- 16. 将角色分配给用户
GRANT 'app_read' TO 'user1'@'%';
GRANT 'app_read', 'app_write' TO 'user2'@'%';
-- 17. 设置默认角色
SET DEFAULT ROLE ALL TO 'user1'@'%';
-- 18. 激活角色
SET ROLE 'app_read';
SET ROLE ALL;
-- 19. 查看角色
SELECT * FROM mysql.role_edges;
SHOW GRANTS FOR 'app_read';
-- 20. 撤销角色
REVOKE 'app_write' FROM 'user1'@'%';
-- 21. 删除角色
DROP ROLE 'app_write';

2.6 备份与恢复指令#

mysqldump备份实战脚本
#!/bin/bash
# 生产环境MySQL自动备份脚本
# 功能:全量备份、增量备份、自动清理、异地传输
# ================ 配置区域 ================
BACKUP_ROOT="/data/backup/mysql"
MYSQL_USER="backup"
MYSQL_PASSWORD="Backup@2026!"
MYSQL_HOST="localhost"
MYSQL_PORT="3306"
RETENTION_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/mysql_backup.log"
# ================ 函数定义 ================
# 日志函数
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}
# 备份单个数据库
backup_database() {
local db_name=$1
local backup_file="$BACKUP_ROOT/daily/${db_name}_${DATE}.sql.gz"
log_message "开始备份数据库: $db_name"
mysqldump -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASSWORD \
--single-transaction \
--routines \
--triggers \
--events \
--hex-blob \
--quick \
--max_allowed_packet=512M \
--default-character-set=utf8mb4 \
--set-gtid-purged=OFF \
$db_name | gzip > $backup_file
if [ $? -eq 0 ]; then
local file_size=$(du -sh $backup_file | cut -f1)
log_message "备份成功: $db_name (大小: $file_size)"
return 0
else
log_message "备份失败: $db_name"
return 1
fi
}
# 清理过期备份
cleanup_old_backups() {
log_message "开始清理${RETENTION_DAYS}天前的备份文件..."
find $BACKUP_ROOT/daily -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
log_message "清理完成"
}
# 上传备份到远程服务器(可选)
upload_to_remote() {
local backup_file=$1
# 使用rsync上传到远程备份服务器
# rsync -avz $backup_file backup@remote-server:/backup/mysql/
# 或者上传到对象存储
# aws s3 cp $backup_file s3://my-backup-bucket/mysql/
}
# ================ 主流程 ================
log_message "========== MySQL备份任务开始 =========="
# 创建备份目录
mkdir -p $BACKUP_ROOT/daily
# 获取数据库列表(排除系统数据库)
databases=$(mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASSWORD \
-e "SHOW DATABASES;" | grep -Ev "Database|information_schema|performance_schema|mysql|sys")
# 备份每个数据库
for db in $databases; do
backup_database $db
done
# 清理过期备份
cleanup_old_backups
# 备份binlog位置信息
mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASSWORD \
-e "SHOW MASTER STATUS\G" > $BACKUP_ROOT/daily/binlog_position_${DATE}.txt
log_message "========== MySQL备份任务完成 =========="
# 发送备份报告邮件(可选)
# echo "MySQL备份完成" | mail -s "MySQL Backup Report" admin@example.com
mysqldump恢复实战
# ================ 恢复前准备 ================
# 1. 查看备份文件
ls -lh /data/backup/mysql/daily/
# 2. 验证备份文件完整性
gzip -t myapp_20260519_030000.sql.gz
# 3. 解压备份文件
gunzip myapp_20260519_030000.sql.gz
# ================ 恢复整个数据库 ================
# 4. 删除现有数据库(如果需要完全重建)
mysql -uroot -p -e "DROP DATABASE IF EXISTS myapp;"
# 5. 创建新数据库
mysql -uroot -p -e "CREATE DATABASE myapp DEFAULT CHARACTER SET utf8mb4;"
# 6. 恢复数据
mysql -uroot -p myapp < myapp_20260519_030000.sql
# ================ 恢复单个表 ================
# 7. 从完整备份中提取单个表的SQL
sed -n '/CREATE TABLE.*users/,/UNLOCK TABLES/p' myapp_20260519_030000.sql > users_table.sql
# 8. 恢复单个表
mysql -uroot -p myapp < users_table.sql
# ================ 时间点恢复 ================
# 9. 先恢复最近的全量备份
mysql -uroot -p myapp < myapp_20260519_030000.sql
# 10. 使用binlog进行时间点恢复
# 查找binlog文件
ls -lh /data/mysql/logs/mysql-bin.*
# 查看binlog内容,找到需要恢复的时间点
mysqlbinlog --base64-output=decode-rows -v \
/data/mysql/logs/mysql-bin.000010 | less
# 恢复指定时间段的binlog
mysqlbinlog --start-datetime="2026-05-19 14:00:00" \
--stop-datetime="2026-05-19 14:29:59" \
--database=myapp \
/data/mysql/logs/mysql-bin.000010 | mysql -uroot -p myapp
# ================ 验证恢复 ================
# 11. 验证数据完整性
mysql -uroot -p myapp -e "
SELECT COUNT(*) FROM users;
SELECT MAX(id) FROM users;
SELECT * FROM users ORDER BY id DESC LIMIT 10;
"
Percona XtraBackup实战
# ================ 全量备份 ================
#!/bin/bash
# XtraBackup自动备份脚本
BACKUP_ROOT="/data/backup/xtrabackup"
MYSQL_USER="backup"
MYSQL_PASSWORD="Backup@2026!"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_DIR="$BACKUP_ROOT/full/$DATE"
RETENTION_DAYS=7
# 创建备份目录
mkdir -p $FULL_BACKUP_DIR
echo "[$(date)] 开始XtraBackup全量备份..."
# 执行全量备份
xtrabackup --user=$MYSQL_USER --password=$MYSQL_PASSWORD \
--backup \
--target-dir=$FULL_BACKUP_DIR \
--parallel=4 \
--compress \
--compress-threads=4 \
--stream=xbstream | gzip > $FULL_BACKUP_DIR.tar.gz
if [ $? -eq 0 ]; then
rm -rf $FULL_BACKUP_DIR
echo "[$(date)] 全量备份成功: $FULL_BACKUP_DIR.tar.gz"
# 清理过期备份
find $BACKUP_ROOT/full -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
else
echo "[$(date)] 全量备份失败"
exit 1
fi
# ================ 增量备份 ================
#!/bin/bash
# XtraBackup增量备份脚本
BACKUP_ROOT="/data/backup/xtrabackup"
MYSQL_USER="backup"
MYSQL_PASSWORD="Backup@2026!"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_BASE="/data/backup/xtrabackup/full/20260519_030000"
INC_BACKUP_DIR="$BACKUP_ROOT/inc/$DATE"
mkdir -p $INC_BACKUP_DIR
echo "[$(date)] 开始XtraBackup增量备份..."
# 执行增量备份
xtrabackup --user=$MYSQL_USER --password=$MYSQL_PASSWORD \
--backup \
--target-dir=$INC_BACKUP_DIR \
--incremental-basedir=$FULL_BACKUP_BASE \
--parallel=4
if [ $? -eq 0 ]; then
echo "[$(date)] 增量备份成功: $INC_BACKUP_DIR"
else
echo "[$(date)] 增量备份失败"
exit 1
fi
# ================ XtraBackup恢复 ================
#!/bin/bash
# XtraBackup恢复脚本
FULL_BACKUP="/data/backup/xtrabackup/full/20260519_030000.tar.gz"
INC_BACKUP_1="/data/backup/xtrabackup/inc/20260519_090000"
INC_BACKUP_2="/data/backup/xtrabackup/inc/20260519_150000"
RESTORE_DIR="/data/restore/xtrabackup"
MYSQL_DATADIR="/data/mysql/data"
echo "[$(date)] 开始XtraBackup恢复流程..."
# 1. 停止MySQL
echo "停止MySQL服务..."
systemctl stop mysqld
# 2. 解压全量备份
echo "解压全量备份..."
mkdir -p $RESTORE_DIR/full
tar -izxf $FULL_BACKUP -C $RESTORE_DIR/full
# 3. 解压xbstream格式
cd $RESTORE_DIR/full
xbstream -x < $RESTORE_DIR/full/*.xbstream
# 4. 解压备份文件
xtrabackup --decompress --target-dir=$RESTORE_DIR/full
# 5. 准备全量备份
echo "准备全量备份..."
xtrabackup --prepare --apply-log-only --target-dir=$RESTORE_DIR/full
# 6. 应用增量备份1
if [ -d "$INC_BACKUP_1" ]; then
echo "应用增量备份1..."
xtrabackup --prepare --apply-log-only \
--target-dir=$RESTORE_DIR/full \
--incremental-dir=$INC_BACKUP_1
fi
# 7. 应用增量备份2(最后一个不加--apply-log-only)
if [ -d "$INC_BACKUP_2" ]; then
echo "应用增量备份2..."
xtrabackup --prepare \
--target-dir=$RESTORE_DIR/full \
--incremental-dir=$INC_BACKUP_2
fi
# 8. 备份当前数据目录
echo "备份当前数据目录..."
mv $MYSQL_DATADIR ${MYSQL_DATADIR}.bak_$(date +%Y%m%d_%H%M%S)
# 9. 恢复数据
echo "恢复数据..."
xtrabackup --copy-back --target-dir=$RESTORE_DIR/full
# 10. 修改权限
echo "修改数据目录权限..."
chown -R mysql:mysql $MYSQL_DATADIR
# 11. 启动MySQL
echo "启动MySQL服务..."
systemctl start mysqld
# 12. 验证
echo "验证恢复结果..."
sleep 5
mysql -uroot -p -e "SHOW DATABASES; SELECT NOW();"
echo "[$(date)] XtraBackup恢复完成!"

2.7 性能监控指令#

综合性能监控查询
-- ================ 连接与线程监控 ================
-- 1. 查看当前连接数和最大连接数
SELECT
(SELECT COUNT(*) FROM information_schema.processlist) AS current_connections,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Max_used_connections') AS max_used_connections,
(SELECT VARIABLE_VALUE FROM performance_schema.global_variables
WHERE VARIABLE_NAME='max_connections') AS max_connections,
ROUND(
(SELECT COUNT(*) FROM information_schema.processlist) /
(SELECT VARIABLE_VALUE FROM performance_schema.global_variables
WHERE VARIABLE_NAME='max_connections') * 100, 2
) AS connection_usage_pct;
-- 2. 查看所有连接详情
SHOW FULL PROCESSLIST;
-- 3. 查看长时间运行的查询
SELECT
id, user, host, db, command, time, state,
LEFT(info, 100) AS query_preview
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 10
ORDER BY time DESC;
-- 4. 查看Sleep连接统计
SELECT
user, COUNT(*) AS sleep_count,
AVG(time) AS avg_sleep_time,
MAX(time) AS max_sleep_time
FROM information_schema.processlist
WHERE command = 'Sleep'
GROUP BY user
ORDER BY sleep_count DESC;
-- ================ QPS/TPS监控 ================
-- 5. 查看各类操作统计
SELECT
VARIABLE_NAME AS operation,
VARIABLE_VALUE AS count
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
'Questions', 'Com_select', 'Com_insert',
'Com_update', 'Com_delete', 'Com_commit', 'Com_rollback'
)
ORDER BY VARIABLE_NAME;
-- 6. 计算实时QPS(需要两次采样)
-- 第一次采样
CREATE TEMPORARY TABLE qps_sample1 AS
SELECT VARIABLE_NAME, VARIABLE_VALUE, UNIX_TIMESTAMP() AS sample_time
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN ('Questions', 'Com_select', 'Com_insert', 'Com_update', 'Com_delete');
-- 等待1秒
SELECT SLEEP(1);
-- 第二次采样并计算QPS
SELECT
s2.VARIABLE_NAME,
ROUND((s2.VARIABLE_VALUE - s1.VARIABLE_VALUE) / (s2.sample_time - s1.sample_time), 2) AS qps
FROM qps_sample1 s1
JOIN (
SELECT VARIABLE_NAME, VARIABLE_VALUE, UNIX_TIMESTAMP() AS sample_time
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN ('Questions', 'Com_select', 'Com_insert', 'Com_update', 'Com_delete')
) s2 ON s1.VARIABLE_NAME = s2.VARIABLE_NAME;
-- ================ 缓冲池监控 ================
-- 7. 查看缓冲池命中率
SELECT
VARIABLE_NAME,
VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
'Innodb_buffer_pool_read_requests',
'Innodb_buffer_pool_reads',
'Innodb_buffer_pool_pages_total',
'Innodb_buffer_pool_pages_free',
'Innodb_buffer_pool_pages_data',
'Innodb_buffer_pool_pages_dirty'
);
-- 8. 计算缓冲池命中率
SELECT
ROUND(
(1 - (
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests')
)) * 100, 2
) AS buffer_pool_hit_rate_pct;
-- 9. 查看缓冲池使用详情
SELECT
ROUND(
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_data') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_total') * 100, 2
) AS data_page_pct,
ROUND(
(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, 2
) AS dirty_page_pct,
ROUND(
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_free') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_total') * 100, 2
) AS free_page_pct;
-- ================ 锁监控 ================
-- 10. 查看当前锁等待
SELECT
r.trx_id AS waiting_trx_id,
r.trx_mysql_thread_id AS waiting_thread,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_thread,
b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
-- 11. 查看表锁情况
SELECT
object_schema AS db,
object_name AS table_name,
lock_type,
lock_mode,
lock_status,
thread_id
FROM performance_schema.data_locks
WHERE object_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys');
-- 12. 查看死锁历史(MySQL 8.0+)
SELECT * FROM performance_schema.events_statements_history
WHERE errors_count > 0 AND sql_text LIKE '%Deadlock%';
-- ================ 慢查询监控 ================
-- 13. 查看慢查询配置
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';
-- 14. 查看慢查询统计
SHOW GLOBAL STATUS LIKE 'Slow_queries';
-- 15. 使用performance_schema分析慢查询
SELECT
DIGEST_TEXT AS query_pattern,
COUNT_STAR AS exec_count,
AVG_TIMER_WAIT / 1000000000000 AS avg_time_sec,
MAX_TIMER_WAIT / 1000000000000 AS max_time_sec,
SUM_ROWS_EXAMINED AS total_rows_examined,
SUM_ROWS_SENT AS total_rows_sent
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
-- ================ 表统计监控 ================
-- 16. 查看表大小排行
SELECT
table_schema AS 'Database',
table_name AS 'Table',
ROUND((data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)',
ROUND(data_length / 1024 / 1024, 2) AS 'Data (MB)',
ROUND(index_length / 1024 / 1024, 2) AS 'Index (MB)',
table_rows AS 'Rows'
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
ORDER BY (data_length + index_length) DESC
LIMIT 20;
-- 17. 查看表的碎片率
SELECT
table_schema AS 'Database',
table_name AS 'Table',
ROUND(data_free / 1024 / 1024, 2) AS 'Fragmentation (MB)',
ROUND(data_free / (data_length + index_length + data_free) * 100, 2) AS 'Fragmentation (%)'
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
AND (data_length + index_length) > 0
AND data_free > 0
ORDER BY data_free DESC
LIMIT 20;
-- ================ 复制监控 ================
-- 18. 查看主库复制状态
SHOW MASTER STATUS\G
-- 19. 查看从库复制状态
SHOW SLAVE STATUS\G
-- 20. 查看从库延迟(秒)
SELECT
CASE
WHEN Seconds_Behind_Master IS NULL THEN 'Not Replicating'
ELSE CONCAT(Seconds_Behind_Master, ' seconds')
END AS replication_lag
FROM (
SHOW SLAVE STATUS
) AS slave_status;

2.8 其他实用指令#

MySQL工具指令集
-- ================ 系统信息查询 ================
-- 1. 查看MySQL版本
SELECT VERSION();
SELECT @@version;
-- 2. 查看服务器状态
SHOW STATUS;
-- 3. 查看系统变量
SHOW VARIABLES;
-- 4. 查看字符集
SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';
-- 5. 查看存储引擎
SHOW ENGINES;
-- 6. 查看插件
SHOW PLUGINS;
-- 7. 查看当前时间
SELECT NOW(), CURDATE(), CURTIME();
SELECT UNIX_TIMESTAMP(), FROM_UNIXTIME(UNIX_TIMESTAMP());
-- 8. 查看运行时间
SHOW GLOBAL STATUS LIKE 'Uptime';
SELECT
CONCAT(
FLOOR(VARIABLE_VALUE / 86400), ' days ',
FLOOR((VARIABLE_VALUE % 86400) / 3600), ' hours'
) AS uptime
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Uptime';
-- ================ 数据库大小查询 ================
-- 9. 查看所有数据库大小
SELECT
table_schema AS 'Database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)',
ROUND(SUM(data_length) / 1024 / 1024, 2) AS 'Data (MB)',
ROUND(SUM(index_length) / 1024 / 1024, 2) AS 'Index (MB)'
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;
-- 10. 查看指定数据库大小
SELECT
table_name AS 'Table',
ROUND((data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)',
table_rows AS 'Rows'
FROM information_schema.tables
WHERE table_schema = 'myapp'
ORDER BY (data_length + index_length) DESC;
-- ================ 索引分析 ================
-- 11. 查看未使用的索引
SELECT
object_schema AS database_name,
object_name AS table_name,
index_name
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
AND count_star = 0
AND object_schema NOT IN ('mysql', 'performance_schema', 'sys')
ORDER BY object_schema, object_name;
-- 12. 查看重复索引
SELECT
a.table_schema,
a.table_name,
a.index_name AS index1,
b.index_name AS index2,
a.column_name
FROM information_schema.statistics a
JOIN information_schema.statistics b
ON a.table_schema = b.table_schema
AND a.table_name = b.table_name
AND a.column_name = b.column_name
AND a.seq_in_index = b.seq_in_index
AND a.index_name < b.index_name
WHERE a.table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
ORDER BY a.table_schema, a.table_name;
-- ================ 事务管理 ================
-- 13. 开启事务
START TRANSACTION;
BEGIN;
-- 14. 提交事务
COMMIT;
-- 15. 回滚事务
ROLLBACK;
-- 16. 设置保存点
SAVEPOINT sp1;
ROLLBACK TO SAVEPOINT sp1;
RELEASE SAVEPOINT sp1;
-- 17. 查看当前事务
SELECT * FROM information_schema.innodb_trx\G
-- 18. 查看长时间未提交的事务
SELECT
trx_id,
trx_mysql_thread_id AS thread_id,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS running_seconds,
trx_query AS current_query
FROM information_schema.innodb_trx
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 60
ORDER BY running_seconds DESC;
-- ================ 维护操作 ================
-- 19. 分析表(更新索引统计信息)
ANALYZE TABLE users;
-- 20. 优化表(整理碎片)
OPTIMIZE TABLE users;
-- 21. 检查表完整性
CHECK TABLE users;
-- 22. 修复表
REPAIR TABLE users;
-- 23. 刷新表
FLUSH TABLES;
FLUSH TABLES WITH READ LOCK; -- 加全局读锁
UNLOCK TABLES; -- 解锁
-- 24. 刷新日志
FLUSH LOGS; -- 刷新所有日志
FLUSH BINARY LOGS; -- 刷新binlog
FLUSH ERROR LOGS; -- 刷新错误日志
FLUSH SLOW LOGS; -- 刷新慢查询日志
-- 25. 清理binlog
PURGE BINARY LOGS BEFORE '2026-01-01 00:00:00';
PURGE BINARY LOGS TO 'mysql-bin.000100';
-- ================ 导入导出 ================
-- 26. 导出查询结果到CSV
SELECT * FROM users
INTO OUTFILE '/tmp/users.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
-- 27. 从CSV导入数据
LOAD DATA INFILE '/tmp/users.csv'
INTO TABLE users
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES;

三、运维人员MySQL进阶技能#

3.1 慢查询分析与SQL优化#

慢查询是导致数据库性能下降的最常见原因之一。你需要掌握如何分析慢查询日志并优化慢SQL。

开启慢查询日志#

/etc/my.cnf 慢查询日志配置
[mysqld]
# 启用慢查询日志
slow_query_log = ON
slow_query_log_file = /data/mysql/logs/slow.log
# 慢查询阈值(秒)
long_query_time = 2
# 记录未使用索引的查询
log_queries_not_using_indexes = ON
# 限制未使用索引日志的记录频率(每分钟最多10条)
log_throttle_queries_not_using_indexes = 10
# 记录管理语句(ALTER TABLE等)
log_slow_admin_statements = ON
# 记录从库上的慢查询
log_slow_slave_statements = ON
运行时开启慢查询日志
-- 临时开启(重启后失效)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
SET GLOBAL log_queries_not_using_indexes = ON;
-- 查看配置
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
-- 查看慢查询统计
SHOW GLOBAL STATUS LIKE 'Slow_queries';

分析慢查询日志#

使用mysqldumpslow分析慢查询
# 查看执行次数最多的10条慢查询
mysqldumpslow -s c -t 10 /data/mysql/logs/slow.log
# 查看返回记录集最多的10条慢查询
mysqldumpslow -s r -t 10 /data/mysql/logs/slow.log
# 查看执行时间最长的10条慢查询
mysqldumpslow -s t -t 10 /data/mysql/logs/slow.log
# 查看平均执行时间最长的10条慢查询
mysqldumpslow -s at -t 10 /data/mysql/logs/slow.log
# 组合使用:查看访问次数最多的10条慢查询,不区分大小写
mysqldumpslow -s c -t 10 -i /data/mysql/logs/slow.log

使用pt-query-digest深度分析(推荐)#

percona
/
percona-toolkit
Waiting for api.github.com...
00K
0K
0K
Waiting...
pt-query-digest使用示例
# 安装percona-toolkit
yum install percona-toolkit -y
# 分析慢查询日志
pt-query-digest /data/mysql/logs/slow.log > slow_query_report.txt
# 只分析最近1小时的日志
pt-query-digest --since '1h' /data/mysql/logs/slow.log
# 只分析特定数据库
pt-query-digest --filter '($event->{db} || "") eq "myapp"' /data/mysql/logs/slow.log
# 输出为JSON格式
pt-query-digest --output json /data/mysql/logs/slow.log > slow.json
# 将分析结果存入数据库
pt-query-digest --review h=localhost,D=slow_query_log,t=global_query_review \
--create-review-table /data/mysql/logs/slow.log
# 对比两个日志文件
pt-query-digest --review h=localhost,D=slow_query_log,t=global_query_review \
--create-review-table slow1.log
pt-query-digest --review h=localhost,D=slow_query_log,t=global_query_review \
--report slow2.log

EXPLAIN执行计划分析#

EXPLAIN详解
-- 基本用法
EXPLAIN SELECT * FROM users WHERE username = 'zhangsan';
-- 查看JSON格式输出(更详细)
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE username = 'zhangsan'\G
-- 查看实际执行信息(MySQL 8.0.18+)
EXPLAIN ANALYZE SELECT * FROM users WHERE username = 'zhangsan'\G
-- 示例1:全表扫描(性能差)
EXPLAIN SELECT * FROM users WHERE YEAR(created_at) = 2026;
-- type: ALL(全表扫描)
-- Extra: Using where
-- 优化后:使用索引
EXPLAIN SELECT * FROM users
WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01';
-- type: range(范围扫描)
-- key: idx_created_at
-- 示例2:索引失效
EXPLAIN SELECT * FROM users WHERE username LIKE '%zhang%';
-- type: ALL(索引失效)
-- 优化后:前缀匹配可以使用索引
EXPLAIN SELECT * FROM users WHERE username LIKE 'zhang%';
-- type: range
-- key: uniq_username
-- 示例3:JOIN查询优化
EXPLAIN SELECT u.*, o.*
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 1;
-- 检查要点:
-- 1. type是否为ALL(全表扫描)
-- 2. key是否为NULL(未使用索引)
-- 3. rows是否过大(扫描行数)
-- 4. Extra是否包含Using filesort或Using temporary

EXPLAIN输出字段详解#

字段说明关注要点
id查询序列号数字越大越先执行
select_type查询类型SIMPLE/PRIMARY/SUBQUERY等
table表名访问哪个表
type访问类型system>const>eq_ref>ref>range>index>ALL
possible_keys可能用到的索引查询涉及的索引
key实际用到的索引NULL表示未使用索引
key_len索引长度越短越好
ref索引的哪一列被使用const/字段名
rows扫描的行数越少越好
filtered过滤比例越高越好
Extra额外信息重点关注

Extra字段重要值:

  • Using index:使用覆盖索引(最优)
  • Using where:使用WHERE过滤
  • Using index condition:索引下推
  • Using filesort:文件排序(需优化)
  • Using temporary:使用临时表(需优化)
  • Using join buffer:使用连接缓冲(需优化)

SQL优化实战案例#

案例1:索引优化
-- 问题SQL:慢查询
SELECT * FROM users
WHERE status = 1 AND DATE(created_at) = '2026-05-19';
-- 问题分析:
-- 1. DATE()函数导致索引失效
-- 2. status字段基数低,索引效率不高
-- 优化方案1:去掉函数,使用范围查询
SELECT * FROM users
WHERE status = 1
AND created_at >= '2026-05-19 00:00:00'
AND created_at < '2026-05-20 00:00:00';
-- 优化方案2:创建联合索引
ALTER TABLE users ADD INDEX idx_status_created (status, created_at);
-- 优化后的查询
SELECT * FROM users USE INDEX (idx_status_created)
WHERE status = 1
AND created_at >= '2026-05-19 00:00:00'
AND created_at < '2026-05-20 00:00:00';
案例2:分页优化
-- 问题SQL:深分页性能差
SELECT * FROM users
ORDER BY created_at DESC
LIMIT 1000000, 20;
-- 问题分析:需要扫描1000020行数据
-- 优化方案1:使用子查询
SELECT * FROM users
WHERE id >= (
SELECT id FROM users
ORDER BY created_at DESC
LIMIT 1000000, 1
)
ORDER BY created_at DESC
LIMIT 20;
-- 优化方案2:使用游标方式(记录上次查询的最后一条记录ID)
SELECT * FROM users
WHERE id < 上次最后一条记录的ID
ORDER BY id DESC
LIMIT 20;
-- 优化方案3:使用延迟关联
SELECT a.* FROM users a
JOIN (
SELECT id FROM users
ORDER BY created_at DESC
LIMIT 1000000, 20
) b ON a.id = b.id;
案例3:JOIN优化
-- 问题SQL:多表JOIN性能差
SELECT u.username, o.order_no, p.product_name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
LEFT JOIN order_items oi ON o.id = oi.order_id
LEFT JOIN products p ON oi.product_id = p.id
WHERE u.status = 1;
-- 优化方案:
-- 1. 确保JOIN字段都有索引
ALTER TABLE orders ADD INDEX idx_user_id (user_id);
ALTER TABLE order_items ADD INDEX idx_order_id (order_id);
ALTER TABLE order_items ADD INDEX idx_product_id (product_id);
-- 2. 先过滤再JOIN
SELECT u.username, o.order_no, p.product_name
FROM (SELECT * FROM users WHERE status = 1) u
LEFT JOIN orders o ON u.id = o.user_id
LEFT JOIN order_items oi ON o.id = oi.order_id
LEFT JOIN products p ON oi.product_id = p.id;
-- 3. 使用STRAIGHT_JOIN强制JOIN顺序
SELECT STRAIGHT_JOIN u.username, o.order_no, p.product_name
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE u.status = 1;

3.2 索引设计与优化#

索引是数据库性能优化的核心,合理的索引可以将查询速度提升几个数量级。

索引类型与选择#

不同类型索引的创建
-- 1. 普通索引
CREATE INDEX idx_username ON users(username);
ALTER TABLE users ADD INDEX idx_email (email);
-- 2. 唯一索引
CREATE UNIQUE INDEX uniq_email ON users(email);
ALTER TABLE users ADD UNIQUE INDEX uniq_username (username);
-- 3. 主键索引
ALTER TABLE users ADD PRIMARY KEY (id);
-- 4. 全文索引(用于文本搜索)
CREATE FULLTEXT INDEX ft_content ON articles(content);
ALTER TABLE articles ADD FULLTEXT INDEX ft_title_content (title, content);
-- 5. 联合索引(最左前缀原则)
CREATE INDEX idx_status_created ON users(status, created_at);
-- 6. 空间索引(用于地理位置数据)
CREATE SPATIAL INDEX idx_location ON stores(location);
-- 7. 前缀索引(节省空间)
CREATE INDEX idx_email_prefix ON users(email(20));
-- 8. 降序索引(MySQL 8.0+)
CREATE INDEX idx_created_desc ON users(created_at DESC);

索引设计原则#

索引设计黄金法则
  1. 选择性原则:索引列的值区分度越高,索引效果越好
  2. 最左前缀原则:联合索引遵循最左匹配原则
  3. 覆盖索引原则:尽量使用覆盖索引,避免回表
  4. 适度原则:不是索引越多越好,过多索引影响写入性能
  5. 监控原则:定期检查和清理无用索引
索引选择性分析
-- 计算字段的选择性(值越接近1越好)
SELECT
COUNT(DISTINCT status) / COUNT(*) AS status_selectivity,
COUNT(DISTINCT email) / COUNT(*) AS email_selectivity,
COUNT(DISTINCT username) / COUNT(*) AS username_selectivity
FROM users;
-- 对于低选择性字段(如性别、状态),不适合单独建索引
-- 可以考虑与其他字段组成联合索引
-- 示例:status字段选择性很低(只有0和1),但与created_at组成联合索引效果好
ALTER TABLE users ADD INDEX idx_status_created (status, created_at);

联合索引优化#

联合索引最左前缀原则
-- 创建联合索引
CREATE INDEX idx_abc ON table(a, b, c);
-- 以下查询可以使用该索引:
SELECT * FROM table WHERE a = 1; -- 使用a
SELECT * FROM table WHERE a = 1 AND b = 2; -- 使用a,b
SELECT * FROM table WHERE a = 1 AND b = 2 AND c = 3;-- 使用a,b,c
SELECT * FROM table WHERE a = 1 AND c = 3; -- 使用a(c用不上)
-- 以下查询不能使用该索引:
SELECT * FROM table WHERE b = 2; -- 不符合最左原则
SELECT * FROM table WHERE c = 3; -- 不符合最左原则
SELECT * FROM table WHERE b = 2 AND c = 3; -- 不符合最左原则
-- 实战案例:用户查询索引设计
-- 查询条件:status、created_at、last_login_at
-- 最优索引顺序:WHERE条件中常量 > 范围 > 排序
-- 方案1:status是常量,created_at是范围
CREATE INDEX idx_status_created ON users(status, created_at);
-- 方案2:如果经常按last_login_at排序
CREATE INDEX idx_status_login_created ON users(status, last_login_at, created_at);

覆盖索引优化#

覆盖索引实战
-- 问题查询:需要回表
SELECT id, username, email FROM users WHERE status = 1;
-- 如果只有 INDEX idx_status (status),需要:
-- 1. 通过idx_status找到所有status=1的id
-- 2. 通过id回表查询username和email
-- 优化:创建覆盖索引(包含所有需要的列)
CREATE INDEX idx_status_username_email ON users(status, username, email);
-- 优化后查询(Using index,无需回表)
EXPLAIN SELECT username, email FROM users WHERE status = 1;
-- Extra: Using index
-- 实战案例:订单查询优化
-- 原始查询
SELECT order_no, user_id, amount, status
FROM orders
WHERE user_id = 12345 AND status = 'paid';
-- 创建覆盖索引
CREATE INDEX idx_user_status_order_amount
ON orders(user_id, status, order_no, amount);
-- 优化效果:
-- Before: type=ref, Extra=Using where
-- After: type=ref, Extra=Using where; Using index

索引维护#

索引管理与维护
-- 1. 查看表的所有索引
SHOW INDEX FROM users;
-- 2. 查看索引使用统计
SELECT
object_schema AS db,
object_name AS table_name,
index_name,
count_star AS index_usage_count,
sum_timer_wait / 1000000000000 AS total_time_sec
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = 'myapp' AND object_name = 'users'
ORDER BY count_star DESC;
-- 3. 找出未使用的索引
SELECT
object_schema AS db,
object_name AS table_name,
index_name
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
AND count_star = 0
AND object_schema NOT IN ('mysql', 'performance_schema', 'sys')
ORDER BY object_schema, object_name;
-- 4. 删除无用索引
ALTER TABLE users DROP INDEX idx_unused;
-- 5. 重建索引(优化碎片)
ALTER TABLE users DROP INDEX idx_username,
ADD INDEX idx_username (username);
-- 或者使用OPTIMIZE TABLE
OPTIMIZE TABLE users;
-- 6. 在线DDL(MySQL 5.6+,不锁表)
ALTER TABLE users
ADD INDEX idx_new_column (new_column),
ALGORITHM=INPLACE, LOCK=NONE;

3.3 主从复制架构深入#

主从复制是MySQL高可用架构的基础,它可以实现数据备份、读写分离和故障切换。

主从复制原理#

主从复制基于**二进制日志(binlog)**实现,主要包括三个线程:

  1. 主库Binlog Dump线程:读取binlog并发送给从库
  2. 从库I/O线程:接收binlog并写入relay log
  3. 从库SQL线程:读取relay log并执行SQL
主从复制流程图
主库 (Master)
│
├─> 执行SQL语句
├─> 写入binlog
├─> Binlog Dump线程读取binlog
└─> 发送给从库
│
▼
从库 (Slave)
│
├─> I/O线程接收binlog
├─> 写入relay log
├─> SQL线程读取relay log
└─> 执行SQL语句

主从复制完整配置#

主库配置 /etc/my.cnf
[mysqld]
server-id = 1 # 服务器ID,主从环境中必须唯一
# binlog配置
log_bin = /data/mysql/logs/mysql-bin # binlog文件路径
binlog_format = ROW # binlog格式:ROW/STATEMENT/MIXED
max_binlog_size = 1G # 单个binlog文件最大大小
binlog_expire_logs_seconds = 604800 # binlog保留时间(7天)
# GTID配置(推荐)
gtid_mode = ON # 启用GTID
enforce_gtid_consistency = ON # 强制GTID一致性
log_slave_updates = ON # 从库是否记录复制的binlog
# 其他配置
sync_binlog = 1 # binlog同步频率(1最安全)
binlog_cache_size = 4M # binlog缓存大小
max_binlog_cache_size = 512M # binlog缓存最大值
# 半同步复制(可选,提高数据安全性)
plugin_load = "rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled = ON
rpl_semi_sync_master_timeout = 1000 # 超时时间(毫秒)
从库配置 /etc/my.cnf
[mysqld]
server-id = 2 # 从库ID,必须与主库不同
# relay log配置
relay_log = /data/mysql/logs/mysql-relay-bin
relay_log_index = /data/mysql/logs/mysql-relay-bin.index
relay_log_recovery = ON # 崩溃恢复时自动修复relay log
# 只读配置(防止从库写入)
read_only = ON # 普通用户只读
super_read_only = ON # 超级用户也只读(MySQL 5.7+)
# GTID配置
gtid_mode = ON
enforce_gtid_consistency = ON
log_slave_updates = ON
# 从库并行复制(提高复制性能)
slave_parallel_workers = 4 # 并行复制线程数
slave_parallel_type = LOGICAL_CLOCK # 并行复制类型
# 半同步复制(可选)
plugin_load = "rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled = ON
主库配置步骤
-- 1. 创建复制用户
CREATE USER 'repl'@'192.168.1.%'
IDENTIFIED WITH mysql_native_password BY 'Repl@2026!Strong';
-- 2. 授予复制权限
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;
-- 3. 查看主库状态(记录File和Position)
SHOW MASTER STATUS\G
-- *************************** 1. row ***************************
-- File: mysql-bin.000001
-- Position: 154
-- Binlog_Do_DB:
-- Binlog_Ignore_DB:
-- Executed_Gtid_Set:
-- 4. 备份主库数据(用于初始化从库)
-- 方法1:使用mysqldump
mysqldump -uroot -p --single-transaction --master-data=2 \
--all-databases > full_backup.sql
-- 方法2:使用xtrabackup
xtrabackup --user=root --password=xxx --backup --target-dir=/backup/full
从库配置步骤
-- 1. 停止从库(如果之前配置过)
STOP SLAVE;
-- 2. 配置主库信息(使用GTID)
CHANGE MASTER TO
MASTER_HOST='192.168.1.100',
MASTER_PORT=3306,
MASTER_USER='repl',
MASTER_PASSWORD='Repl@2026!Strong',
MASTER_AUTO_POSITION=1; -- 使用GTID自动定位
-- 如果不使用GTID,需要指定binlog位置
CHANGE MASTER TO
MASTER_HOST='192.168.1.100',
MASTER_PORT=3306,
MASTER_USER='repl',
MASTER_PASSWORD='Repl@2026!Strong',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;
-- 3. 启动从库复制
START SLAVE;
-- 4. 查看从库状态
SHOW SLAVE STATUS\G
-- 重点检查以下字段:
-- Slave_IO_Running: Yes # I/O线程运行状态
-- Slave_SQL_Running: Yes # SQL线程运行状态
-- Seconds_Behind_Master: 0 # 主从延迟(秒)
-- Last_IO_Error: # I/O错误信息
-- Last_SQL_Error: # SQL错误信息
-- Executed_Gtid_Set: # 已执行的GTID集合

主从复制故障处理#

常见主从复制问题及解决方案
-- ============ 问题1:主从延迟过大 ============
-- 1. 查看延迟情况
SHOW SLAVE STATUS\G
-- Seconds_Behind_Master: 300 # 延迟300秒
-- 2. 分析原因
-- a) 查看从库是否有慢查询
SHOW FULL PROCESSLIST;
-- b) 查看从库负载
-- CPU、内存、磁盘IO是否过高
-- c) 查看是否启用并行复制
SHOW VARIABLES LIKE 'slave_parallel%';
-- 3. 解决方案
-- 方案1:启用并行复制
STOP SLAVE;
SET GLOBAL slave_parallel_workers = 4;
SET GLOBAL slave_parallel_type = LOGICAL_CLOCK;
START SLAVE;
-- 方案2:升级从库硬件
-- 方案3:优化慢查询
-- 方案4:读写分离,减少从库查询压力
-- ============ 问题2:主从同步中断 ============
-- 1. 查看错误信息
SHOW SLAVE STATUS\G
-- Slave_IO_Running: No
-- Last_IO_Error: error connecting to master 'repl@192.168.1.100:3306'
-- 2. 解决方案:网络问题
-- 检查网络连接
ping 192.168.1.100
telnet 192.168.1.100 3306
-- 重启I/O线程
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
-- 3. SQL线程错误(数据冲突)
SHOW SLAVE STATUS\G
-- Slave_SQL_Running: No
-- Last_SQL_Error: Duplicate entry '123' for key 'PRIMARY'
-- 方案1:跳过错误事务(谨慎使用,可能导致数据不一致)
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;
-- 方案2:使用GTID跳过错误事务
STOP SLAVE;
SET GTID_NEXT='错误事务的GTID'; -- 从Last_SQL_Error中获取
BEGIN; COMMIT;
SET GTID_NEXT='AUTOMATIC';
START SLAVE;
-- 方案3:手动修复数据后重启
-- 例如:删除重复数据
DELETE FROM users WHERE id = 123;
START SLAVE;
-- ============ 问题3:主从数据不一致 ============
-- 使用pt-table-checksum检查数据一致性
pt-table-checksum --host=主库IP \
--user=checksum_user \
--password=password \
--databases=myapp \
--replicate=percona.checksums
-- 使用pt-table-sync修复数据不一致
pt-table-sync --execute \
--sync-to-master \
--user=sync_user \
--password=password \
从库IP
-- ============ 问题4:主从切换 ============
-- 场景:主库故障,需要提升从库为主库
-- 1. 在从库上停止复制
STOP SLAVE;
RESET SLAVE ALL;
-- 2. 在从库上禁用只读
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;
-- 3. 配置新的从库指向新的主库
-- 在其他从库上执行:
STOP SLAVE;
CHANGE MASTER TO
MASTER_HOST='新主库IP',
MASTER_AUTO_POSITION=1;
START SLAVE;

3.4 分库分表基础#

当单表数据量达到千万级以上时,查询性能会明显下降,这时就需要考虑分库分表。

分库分表方式#

分库分表策略选择
垂直拆分:
├─ 垂直分库:按业务模块拆分到不同数据库
│ └─ 例:用户库、订单库、商品库
└─ 垂直分表:按字段拆分到不同表
└─ 例:用户基础表、用户扩展表
水平拆分:
├─ 水平分库:同一类数据分散到多个数据库
│ └─ 例:user_db_0, user_db_1, user_db_2
└─ 水平分表:同一类数据分散到多个表
└─ 例:user_0, user_1, user_2

常见分片算法#

分片算法实现
-- ============ 1. 范围分片 ============
-- 按用户ID范围分片(简单但容易数据倾斜)
-- 分片规则:
-- user_0: id 1-1000000
-- user_1: id 1000001-2000000
-- user_2: id 2000001-3000000
-- 路由算法(应用层实现)
table_suffix = (user_id - 1) / 1000000
table_name = 'user_' + table_suffix
-- ============ 2. 哈希分片 ============
-- 按用户ID哈希值分片(数据分布均匀)
-- 分片规则:4个分片
-- 路由算法
table_suffix = user_id % 4
table_name = 'user_' + table_suffix
-- ============ 3. 一致性哈希 ============
-- 解决哈希分片的扩容问题
-- 使用虚拟节点技术
-- ============ 4. 时间分片 ============
-- 按时间范围分片(适合日志、订单等场景)
-- 分片规则:按月分表
-- order_202601, order_202602, order_202603
-- 路由算法
table_name = 'order_' + DATE_FORMAT(order_time, '%Y%m')
-- ============ 5. 地理位置分片 ============
-- 按地区分片
-- 分片规则:
-- user_cn_north: 华北用户
-- user_cn_south: 华南用户
-- user_cn_east: 华东用户

分库分表中间件#

主流分库分表中间件
  1. ShardingSphere:Apache开源项目,功能强大

    apache
    /
    shardingsphere
    Waiting for api.github.com...
    00K
    0K
    0K
    Waiting...
  2. MyCat:国产开源,简单易用

  3. Vitess:YouTube开源,适合大规模场景

    vitessio
    /
    vitess
    Waiting for api.github.com...
    00K
    0K
    0K
    Waiting...
  4. TDDL:阿里巴巴开源

  5. 云厂商方案:阿里云PolarDB-X、腾讯云TDSQL等

分库分表注意事项#

分库分表的挑战
  1. 跨分片JOIN:需要应用层聚合或使用宽表冗余
  2. 分布式事务:使用两阶段提交或最终一致性
  3. 全局唯一ID:使用雪花算法或分布式ID生成器
  4. 数据迁移:扩容时需要迁移数据,停机时间长
  5. 运维复杂度:多个数据库实例增加运维难度
分库分表最佳实践
-- 1. 设计合理的分片键
-- 选择经常出现在WHERE条件中的字段
-- 避免使用经常变化的字段
-- 2. 避免跨分片查询
-- 不推荐:
SELECT * FROM user WHERE email = 'xxx@example.com';
-- 问题:email不是分片键,需要查询所有分片
-- 推荐:
SELECT * FROM user WHERE user_id = 12345;
-- user_id是分片键,可以精确定位到某个分片
-- 3. 使用全局唯一ID
-- 雪花算法(Snowflake)生成64位ID
-- 结构:1位符号位 + 41位时间戳 + 10位机器ID + 12位序列号
-- 4. 冗余数据解决跨分片JOIN
-- 订单表冗余用户信息
CREATE TABLE order (
id BIGINT PRIMARY KEY,
user_id BIGINT,
user_name VARCHAR(50), -- 冗余
user_mobile VARCHAR(20), -- 冗余
order_no VARCHAR(50),
amount DECIMAL(10,2)
);

四、总结与学习路径#

MySQL是一个非常庞大和复杂的系统,想要完全掌握它需要长期的学习和实践。作为一名系统运维人员,你不需要成为MySQL开发专家,但你必须掌握本文提到的所有核心技能,这样才能在生产环境中从容应对各种问题。

运维人员MySQL技能检查清单#

MySQL运维技能自查表
## 基础技能(必须掌握)
- [ ] 能够独立安装部署MySQL(多种方式)
- [ ] 熟练配置my.cnf核心参数
- [ ] 掌握用户权限管理
- [ ] 掌握数据库、表、数据的基本操作
- [ ] 能够进行数据备份与恢复
- [ ] 掌握慢查询日志分析
## 进阶技能(重点掌握)
- [ ] 理解InnoDB存储引擎原理
- [ ] 掌握EXPLAIN执行计划分析
- [ ] 能够设计和优化索引
- [ ] 掌握主从复制配置与故障处理
- [ ] 掌握常用性能监控指标
- [ ] 能够进行基础SQL优化
## 高级技能(逐步学习)
- [ ] 理解MySQL锁机制和事务原理
- [ ] 掌握binlog和GTID
- [ ] 能够搭建高可用架构(MHA/MGR)
- [ ] 掌握MySQL性能调优方法论
- [ ] 了解分库分表原理和方案
- [ ] 能够处理复杂的数据库故障
## 专家技能(深入研究)
- [ ] 理解MySQL内核架构
- [ ] 能够进行深度性能调优
- [ ] 掌握MySQL源码阅读
- [ ] 能够设计大规模数据库架构
- [ ] 掌握数据库中间件使用

学习路径建议#

阶段1:基础入门(1-3个月)

  • 学习MySQL安装部署
  • 掌握SQL基本语法
  • 理解数据库、表、索引概念
  • 学习用户权限管理
  • 实践备份恢复操作

阶段2:运维进阶(3-6个月)

  • 深入学习InnoDB原理
  • 掌握慢查询分析与SQL优化
  • 学习主从复制配置
  • 实践性能监控与调优
  • 学习高可用架构方案

阶段3:高级运维(6-12个月)

  • 深入研究MySQL内核
  • 学习分库分表方案
  • 掌握大规模集群运维
  • 学习容器化部署(K8s)
  • 研究云原生数据库

阶段4:专家级(1年以上)

  • 阅读MySQL源码
  • 参与开源社区贡献
  • 设计企业级数据库架构
  • 输出技术分享和文章

推荐学习资源#

书籍推荐:

  1. 《高性能MySQL》(第4版)- MySQL优化圣经
  2. 《MySQL技术内幕:InnoDB存储引擎》- 深入理解InnoDB
  3. 《MySQL运维内参》- 运维实战经验
  4. 《数据库系统概念》- 数据库理论基础

在线资源:

实用工具:

percona
/
percona-toolkit
Waiting for api.github.com...
00K
0K
0K
Waiting...
prometheus
/
mysqld_exporter
Waiting for api.github.com...
00K
0K
0K
Waiting...
github
/
gh-ost
Waiting for api.github.com...
00K
0K
0K
Waiting...

持续学习建议#

成长建议
  1. 动手实践:搭建本地MySQL环境,模拟各种故障场景
  2. 阅读源码:从简单模块开始,逐步深入MySQL内核
  3. 参与社区:在Stack Overflow、GitHub上回答问题和贡献代码
  4. 写技术博客:记录学习过程和踩坑经验
  5. 定期总结:每月总结工作中遇到的问题和解决方案
  6. 关注新技术:跟进MySQL新版本特性和云原生数据库发展

最后的话#

作为MySQL运维人员,请牢记以下原则:

  1. 安全第一:任何操作前先备份,生产环境操作需二次确认
  2. 监控为王:完善的监控系统是发现问题的第一步
  3. 自动化运维:重复性工作尽量脚本化、自动化
  4. 文档先行:所有架构和操作都要有详细文档
  5. 持续学习:技术日新月异,保持学习热情

最后,我想强调的是,实践是最好的老师。只有在实际工作中不断遇到问题、解决问题,你才能真正掌握MySQL运维技能。同时,要养成良好的运维习惯,比如操作前备份、变更前测试、定期巡检等,这些习惯可以帮助你避免很多不必要的故障。

祝你在MySQL运维的道路上越走越远,成为一名优秀的数据库专家!🚀


相关文章推荐:

MySQL数据库指南(合集篇):从基础指令到生产实战
https://www.6ixblog.site/posts/linux-basic-13-2/
作者
Licwic
发布于
2025-04-14
许可协议
CC BY-NC-SA 4.0