目录
(2)使用 EXPLAIN 进行 SQL 优化的步骤及实验验证
一、前置知识点
MySQL的运行原理
1. 客户端连接
客户端通过 TCP/IP 或 Socket 连接到 MySQL 服务端。
连接管理器(Connection Manager)负责管理线程池和用户认证。
2. SQL 解析与优化
解析器:将 SQL 语句解析为语法树,验证语法合法性。
优化器:生成执行计划,选择索引或全表扫描等策略。
3. 存储引擎处理
执行引擎:调用存储引擎(如 InnoDB、MyISAM)执行读写操作。
数据存储:
InnoDB:数据存储在表空间文件(
.ibd
)中,支持事务和行级锁。MyISAM:数据存储在
.MYD
(数据文件)和.MYI
(索引文件)中,不支持事务。
4. 日志与持久化
Redo Log(重做日志):确保事务的持久性,崩溃恢复时重放未提交的事务。
Undo Log(回滚日志):支持事务回滚和多版本并发控制(MVCC)。
Binlog(二进制日志):记录所有数据变更,用于主从复制和增量备份。
二、MySQL 单实例故障排查
(1)故障现象1
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/data/mysql/mysql.sock' (2)
问题分析:以上这种情况一般都是数据库未启动、mysql 配置文件未指定 socket 文件或者数据库端口被防火墙拦截导致。
解决方法:启动数据库或者防火墙开放数据库监听端口。
(2)故障现象2
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
问题分析:密码不正确或者没有权限访问。
解决方法:
修改 my.cnf 主配置文件,在 [mysqld] 下添加 skip - grant - tables=on,重启数据库。最后修改密码命令如下:
Mysql5.7 版本
mysql> update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
mysql> flush privileges;
Mysql8.0
mysql> UPDATE mysql.user SET authentication_string='' WHERE user='root' AND Host='localhost';
mysql> FLUSH PRIVILEGES;
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
再删除刚刚添加的 skip-grant-tables 参数,重启数据库,使用新密码即可登录。重新授权,命令如下。
Mysql5.7
mysql>grant all on *.* to 'root'@'mysql-server' identified by '123456';
Mysql8.0
mysql> CREATE USER 'root'@'mysql-server' IDENTIFIED BY '123456';
mysql> GRANT all ON *.* TO 'root'@'mysql-server';
(3)故障现象3
使用远程连接数据库时偶尔会发生远程连接数据库很慢的问题
问题分析:如果MySQL主机查询DNS很慢或有很多客户端连接导致很慢,由于开发机器是不可以连接外网的,在进行MySQL连接时,DNS解析是不可能完成的,从而也就明白了连接那么慢的原因。
解决方法:修改配置文件my.cnf ,在[mysql] 下添加 skip-name-resolve,重启数据库可以解决,注意在以后授权里面不能再使用主机名授权。
(4)故障现象4
Can’t open file: 'xxx_forums.MYI'. (errno: 145)
问题分析:服务器非正常关机,数据库所在空间已满,或一些其他未知原因,对数据库表造成了损坏。可能是系统下直接将数据库文件拷贝,会因为文件的属组问题产生这个错误。
解决方法:
1.可以使用两种方式修复数据表
- 使用 MySQL 自带的专门用户数据表检查和修复工具 myisamchk。一般情况下只有在命令行下面才能运行 myisamchk 命令。常用的修复命令为:
myisamchk -r 数据文件目录/数据表名.MYI;
- 通过 phpMyAdmin 修复,phpMyAdmin 带有修复数据表的功能,进入到某一个表中后,点击 “操作”,在下方的 “表维护” 中点击 “修复表” 即可。
注意:以上两种修复方式在执行前一定要备份数据库。
- 修改文件的属组(仅适合独立主机用户):
- 复制数据库文件的过程中没有将数据库文件设置为 MySQL 运行的帐号可读写(一般适用于 Linux 和 FreeBSD 用户)。
(5)故障现象5
ERROR 1129 (HY000): Host 'xxx.xxx.xxx' is blocked because of many connection erros;
unblock with 'mysqladmin flush-hosts'
问题分析:由于数据库参数:max_connect_errors , 其默认值是10.当大量的主机去连接MySQL ,总连接请求超过了,新的连接就无法连上MySQL。同一个IP在短时间内产生太多中断的数据库连接而导致的阻塞。
解决方法:使用 mysqladmin flush-hosts 命令清除缓存
(6)故障现象6
客户端报 Too many connections.
问题分析:连接数超出MySQL的最大连接数限制
解决方法:1.在 /etc/my.cnf 配置文件增大连接数,然后重启MySQL
max_connections = 10000
2.临时修改最大连接数,在my.cnf 里修改
set GLOBL max_connections = 10000
(7)故障现象7
Warning:World-writable config file '/etc/my.cnf' is ignored
ERROR! MySQL is running but PID file could not be found
问题分析:MySQL 的配置文件/etc/my.cnf 权限不对
解决方法:给它权限 chmod 644 /etc/my.cnf
(8)故障现象8
InnoDB: Error:page 14178 log sequence number 29455369832
InnoDB:is in the future!Current system log sequence number 29455369832f
问题分析: innodb 数据文件损坏。
解决方法:修改 my.cnf 配置文件,在[mysql] 下添加 innodb_force_recovery=4,启动数据库后备份数据文件,然后去掉该参数,利用备份文件恢复数据。
三、MySQL 主从故障排查
(1)故障现象1
从库的 Slave_IO_Running 为 NO
The slave I/O thread stops because master and slave have equal MySQL server ids; these ids must be different for replication to work (or the --replicate-same-server-id option must be used on slave but this does not always make sense; please check the manual before using it).
问题分析:主库和从库的 server-id 值一样。
解决方法:修改从库的 server-id 的值,修改为和主库不一样。修改完后重启,再同步即可。
(2)故障现象2
从库的 Slave_IO_Running 为 NO
问题分析:造成从库线程为 NO 的原因会有很多,主要原因是主键冲突或者主库删除或更新数据,从库找不到记录,数据被修改导致。通常状态码报错有 1007、1032、1062、1452 等。
解决方法一:
mysql> stop slave;
mysql> set GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
mysql> start slave;
解决方法二:
设置用户权限,设置从库只读权限
set global read_only=true;
(3)故障现象3
Error initializing relay log position: I/O error reading the header from the binary log
分析问题:从库的中继日志 relay-bin 损坏。
解决方法:手工修复,重新找到同步的 binlog 和 pos 点,然后重新同步即可。
mysql>CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.xxx', MASTER_LOG_POS=xxx;
四、MySQL 优化
1、硬件方面
(1)关于 CPU
优化目标:
提升并行处理能力,减少 SQL 执行和锁竞争带来的 CPU 瓶颈。
关键策略:
选择多核 CPU:
MySQL 支持多线程操作(如并行查询、连接处理),多核 CPU 能更好地处理高并发请求。
建议:优先选择主频高、核心数多的 CPU(如 16 核以上)。
监控 CPU 使用率:
# 查看 CPU 负载 top # 检查用户态(us)和内核态(sy)的 CPU 占用 vmstat 1
常见问题:
us
高:SQL 执行消耗过多 CPU(需优化慢查询)。sy
高:系统内核资源争用(如锁竞争、线程切换)。
优化锁与并发:
减少表锁(MyISAM)或行锁(InnoDB)竞争:
-- 查看锁等待 SHOW ENGINE INNODB STATUS\G -- 查看当前运行事务 SELECT * FROM information_schema.INNODB_TRX;
(2)关于内存
优化目标:
最大化利用内存缓存数据,减少磁盘 I/O。
关键策略:
配置 InnoDB 缓冲池:
innodb_buffer_pool_size
是 InnoDB 引擎的核心参数,用于缓存数据和索引。建议:设置为系统总内存的 70%~80%(需预留内存给操作系统和其他进程)。
# my.cnf 示例 innodb_buffer_pool_size = 64G
优化其他内存区域:
连接线程内存:
thread_cache_size = 100 # 缓存线程数 max_connections = 1000 # 最大连接数
排序和临时表内存:
sort_buffer_size = 4M # 排序缓冲区 tmp_table_size = 256M # 内存临时表大小
监控内存使用:
-- 查看 InnoDB 缓冲池命中率 SHOW STATUS LIKE 'Innodb_buffer_pool_read%'; -- 计算命中率公式: -- (1 - Innodb_buffer_
(3)关于磁盘
优化目标:
提升 I/O 吞吐量,降低数据读写延迟。
关键策略:
选择高性能存储设备:
SSD:比 HDD 快 10 倍以上,尤其适合随机 I/O 密集场景。
RAID 配置:
RAID 10:兼顾性能与冗余,适合 OLTP 场景。
RAID 5:适合读多写少的 OLAP 场景。
分离日志与数据文件:
日志文件(binlog、redo log):
写入频繁且顺序写入,建议单独挂载高速 SSD。
数据文件:
随机读写较多,使用独立磁盘或 RAID 10。
优化文件系统与 I/O 调度:
文件系统:使用 XFS 或 ext4(启用
noatime
减少元数据写入)。I/O 调度器:
# 查看当前调度策略 cat /sys/block/sda/queue/scheduler # 设置为 deadline 或 noop(SSD 推荐) echo 'deadline' > /sys/block/sda/queue/scheduler
监控磁盘 I/O:
# 查看磁盘吞吐量和延迟 iostat -x 1 # 检查 I/O 等待时间 top(关注 %wa 指标)
2、MySQL 配置文件
(1)核心功能优化项
参数 | 作用 | 建议配置 | 注意事项 |
---|---|---|---|
innodb_buffer_pool_size | InnoDB 缓冲池大小,缓存数据和索引,直接影响读性能 | 设置为物理内存的 50%~70%(如 64GB 内存配 40G) | 避免超过物理内存,防止系统交换(Swap) |
innodb_log_file_size | 单个 InnoDB 重做日志文件大小,影响事务提交速度和崩溃恢复时间 | 建议 1G~4G(如 2G) | 修改需停止 MySQL,删除旧日志文件后重启 |
innodb_flush_log_at_trx_commit | 控制事务日志刷新策略,平衡性能与数据安全 | 1(默认,完全持久化);2(折中,每秒刷盘);0(高性能,风险高) | 高并发写入场景可设为 2,但需容忍最多 1 秒数据丢失 |
max_connections | 最大客户端连接数,避免连接耗尽 | 根据业务需求设置,建议 500~2000 | 监控 Threads_connected 和 Threads_running 调整 |
tmp_table_size、max_heap_table_size | 内存临时表大小上限,影响复杂查询(如 GROUP BY、JOIN) | 建议 64M~256M(如 128M),两者值需一致 | 过小会导致磁盘临时表,降低性能;过大可能耗尽内存 |
(2)查询优化项
参数 | 作用 | 建议配置 |
---|---|---|
query_cache_type | 查询缓存类型(MySQL 8.0 已移除,旧版本慎用) | OFF(默认,高并发下建议关闭) |
sort_buffer_size | 排序操作缓冲区大小 | 2M~8M(如 4M) |
join_buffer_size | JOIN 操作缓冲区大小 | 4M~16M(如 8M,仅对无索引 JOIN 有效) |
read_buffer_size | 顺序读缓冲区大小 | 2M~8M(如 4M) |
read_rnd_buffer_size | 随机读缓冲区大小 | 4M~16M(如 8M) |
(3)日志与监控
参数 | 作用 | 建议配置 |
---|---|---|
slow_query_log | 启用慢查询日志 | ON |
long_query_time | 定义慢查询阈值(秒),记录执行时间长的 SQL | 1~2(根据业务容忍度调整) |
log_error | 错误日志路径,用于故障排查 | 指定路径(如 /var/log/mysql/error.log ) |
binlog_format | 二进制日志格式(主从复制依赖) | ROW(推荐,数据一致性高) |
expire_logs_days | 自动清理旧的二进制日志天数 | 7~14(根据备份策略调整) |
(4)InnoDB 高级优化
参数 | 作用 | 建议配置 |
---|---|---|
innodb_io_capacity | InnoDB 后台任务的 I/O 能力(如刷新脏页) | SSD 建议 2000~4000,HDD 建议 200~400 |
innodb_flush_method | 控制数据文件与日志文件的刷新方式 | O_DIRECT(默认,避免双缓冲) |
innodb_thread_concurrency | InnoDB 并发线程数(默认,自适应),高并发场景影响性能 | 可设为 CPU 核数 * 2 |
innodb_autoinc_lock_mode | 自增锁模式,影响插入(连续模式,高并发插入性能) | 2 |
(5)示例配置(my.cnf)
物理资源 32 核 CPU、64G 内存、500G SSD
[mysqld]
# 核心配置
innodb_buffer_pool_size = 40G
innodb_log_file_size = 2G
innodb_flush_log_at_trx_commit = 2
max_connections = 1000
thread_cache_size = 100
# 查询优化
tmp_table_size = 128M
max_heap_table_size = 128M
sort_buffer_size = 4M
join_buffer_size = 8M
# 日志与监控
slow_query_log = ON
long_query_time = 1
log_error = /var/log/mysql/error.log
binlog_format = ROW
expire_logs_days = 7
# InnoDB 高级
innodb_io_capacity = 2000
innodb_flush_method = O_DIRECT
innodb_thread_concurrency = 0
innodb_autoinc_lock_mode = 2
3、SQL 方面
SQL 优化是保障数据库高效运行的关键,核心是减少 CPU、内存、磁盘 I/O 等资源消耗,提升查询响应。未优化 SQL 在大数据量或高并发时会引发全表扫描等问题,致服务器负载高、响应慢,影响业务。可借助索引优化等手段降低数据库压力,支持业务扩展,控制成本与运维复杂度。
(1)创建测试表并插入数据
-- 创建数据库
Create database test;
-- 使用数据库
Use test;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
age INT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
--插入 10 万条测试数据(使用存储过程生成)
DELIMITER $$
CREATE PROCEDURE insert_users()
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < 100000 DO
INSERT INTO users (name, email, age)
VALUES (CONCAT('user', i), CONCAT('user', i, '@example.com'), FLOOR(RAND() * 100));
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
CALL insert_users();
(2)使用 EXPLAIN 进行 SQL 优化的步骤及实验验证
EXPLAIN 是 MySQL 分析 SQL 执行计划的工具,模拟查询执行输出访问类型、索引使用、扫描行数、额外操作等关键信息,能帮开发者找出全表扫描、索引失效等性能问题,进而从添加索引、改写查询、调整表结构等方面优化,是提升数据库效率的重要诊断工具。
例如:
mysql>EXPLAIN SELECT * FROM users WHERE name = 'user123';
EXPLAIN 用于显示 MySQL 如何执行一条 SQL 语句,关键字段如下:
字段 | 说明 | 优化关注点 |
---|---|---|
id | 查询序列号,相同 id 为同一执行层,不同 id 按序执行(如查看复杂查询的嵌套层级) | - |
select_type | 查询类型(SIMPLE、PRIMARY、SUBQUERY、DERIVED 等) | 识别子查询或临时表操作 |
table | 访问的表名或别名 | 确认查询涉及的表 |
type | 访问类型,性能从优到劣:system > const > eq_ref > ref > range > index > ALL | 避免 ALL(全表扫描),优先优化为 ref 或 range |
possible_keys | 可能使用的索引 | 检查是否有合适索引未被使用 |
key | 实际使用的索引 | 确认是否命中最佳索引 |
rows | 预估扫描的行数 | 行数越少,查询效率越高 |
Extra | 附加信息(如 Using where、Using index、Using temporary 等) | 发现潜在性能问题( |
根据关键字段说明,对 explain SELECT * FROM users WHERE name = 'user123' 的结果分析如下:
- type=ALL:全表扫描,效率极低。
- possible_keys=NULL:未命中索引。
- rows=100000:扫描全部数据。
(3)优化步骤:添加索引
mysql>ALTER TABLE users ADD INDEX idx_name (name);
(4)优化后查询及 EXPLAIN 分析
mysql>EXPLAIN SELECT * FROM users WHERE name = 'user123';
优化后结果分析如下:
- type=ref:索引查找,效率高。
- key=idx_name:命中新创建的索引。
- rows=1:仅扫描一行数据。