下一篇:没有了
PHP + MySQL 网站访问速度慢,如何根据慢查询日志进行性能优化
[ 2026/5/27 16:10:01 ]针对 PHP + MySQL 网站访问速度慢的问题,基于慢查询日志(Slow Query Log)进行性能优化是一套标准且高效的排查流程。以下是从开启日志、分析定位到具体优化的完整操作指南:
一、 开启与配置慢查询日志
默认情况下 MySQL 可能未开启慢查询日志,或者阈值设置过高(默认 10 秒),导致无法捕获轻微的性能瓶颈。
1. 临时开启(无需重启,立即生效)
登录 MySQL 命令行执行以下命令:
sql
-- 开启慢查询日志
SET GLOBAL slow_query_log = ''ON'';
-- 设置慢查询阈值(建议线上设为 1 秒或 2 秒,调试时可设更低如 0.5 秒)
SET GLOBAL long_query_time = 1;
-- 指定日志文件路径(确保 MySQL 用户有写入权限)
SET GLOBAL slow_query_log_file = ''/var/log/mysql/mysql-slow.log'';
-- (可选)记录未使用索引的查询,有助于发现潜在问题,但高并发下慎用,以免日志暴涨
SET GLOBAL log_queries_not_using_indexes = ''ON'';
注意:SET GLOBAL 对当前已存在的连接不生效,仅对新建立的连接生效。且重启 MySQL 后配置会失效。永久生效需修改配置文件。
2. 永久配置(推荐)
编辑 MySQL 配置文件(通常是 /etc/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf),在 [mysqld] 段落下添加:
ini
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log_queries_not_using_indexes = OFF # 生产环境建议关闭,除非在低峰期排查
修改后重启 MySQL 服务:sudo systemctl restart mysql
二、 分析慢查询日志
直接阅读原始日志效率较低,建议使用工具进行聚合分析。
1. 使用 mysqldumpslow(MySQL 自带)
这是最基础的分析工具,适合快速查看最耗时的 SQL。
bash
# 按执行时间排序,显示前 10 条最慢的 SQL
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
# 按扫描行数排序(往往扫描行数多意味着缺少索引)
mysqldumpslow -s r -t 10 /var/log/mysql/mysql-slow.log
2. 使用 pt-query-digest(Percona Toolkit,推荐)
功能更强大,能生成详细的报表,识别高频慢查询和资源消耗大户。
bash
# 安装 Percona Toolkit (Ubuntu/Debian)
sudo apt-get install percona-toolkit
# 分析日志并输出报告
pt-query-digest /var/log/mysql/mysql-slow.log > slow_report.txt
# 只分析最近 24 小时的日志
pt-query-digest --since=24h /var/log/mysql/mysql-slow.log
报告解读重点:
Rank 1 的查询:通常占用总响应时间比例最高,优先优化。
Query_time:执行时间。
Rows_examined:扫描行数。如果该值远大于 Rows_sent(返回行数),说明存在全表扫描或索引效率极低。
Count:执行频率。高频次的中等慢查询比低频次的极慢查询更影响整体性能。
三、 诊断与优化策略
拿到具体的慢 SQL 后,按以下步骤进行优化:
1. 使用 EXPLAIN 分析执行计划
在 MySQL 客户端对慢 SQL 执行 EXPLAIN:
sql
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = ''pending'' ORDER BY created_at DESC;
关键指标检查:
type:若为 ALL,表示全表扫描,必须优化;理想情况是 ref、eq_ref 或 range。
key:若为 NULL,表示未使用索引。
rows:预估扫描行数,数值越大性能越差。
Extra:
Using filesort:表示需要额外排序,可通过复合索引优化。
Using temporary:表示使用了临时表,常见于 GROUP BY 或 DISTINCT,需优化查询结构或索引。
2. 索引优化
添加缺失索引:根据 WHERE 和 ORDER BY 字段建立索引。
遵循最左前缀原则建立复合索引:
例如查询 WHERE a=1 AND b=2 ORDER BY c,应建立联合索引 (a, b, c)。
避免在索引列上使用函数或计算(如 WHERE YEAR(created_at) = 2023 会导致索引失效,应改为范围查询 created_at >= ''2023-01-01'' AND created_at < ''2024-01-01'')。
覆盖索引:如果查询只涉及索引列,MySQL 可直接从索引树获取数据,无需回表,速度极快。
3. PHP 代码层优化(解决 N+1 问题)
很多慢查询并非单条 SQL 慢,而是 PHP 循环中执行了大量查询。
错误示例:
php
foreach ($users as $user) {
// 每次循环都查一次数据库,造成 N+1 问题
$profile = $db->query("SELECT * FROM profiles WHERE user_id = {$user[''id'']}");
}
优化方案:
使用 JOIN 或 IN 查询一次性获取数据。
php
// 先获取所有用户 ID
$ids = array_column($users, ''id'');
// 一次性查询所有关联数据
$profiles = $db->query("SELECT * FROM profiles WHERE user_id IN (" . implode('','', $ids) . ")");
// 在 PHP 内存中组装数据
4. SQL 语句重构
避免 SELECT *:只查询需要的字段,减少网络传输和内存占用。
限制结果集:务必添加 LIMIT,防止意外返回数万行数据。
拆分复杂查询:如果包含多个 JOIN 或子查询导致性能下降,考虑拆分为多个简单查询,在 PHP 层组装数据(特别是当关联表数据量巨大时)。
5. 引入缓存机制
对于读取频繁但更新较少的数据(如配置信息、热点文章列表):
使用 Redis 或 Memcached 缓存查询结果。
在 PHP 中使用 APCu 缓存少量静态数据。
启用 MySQL 查询缓存(注意:MySQL 8.0 已移除查询缓存,建议应用层缓存)。
四、 其他注意事项
日志轮转:慢查询日志会持续增长,务必配置 logrotate 定期切割和清理日志,防止磁盘写满。
IO 影响:开启慢查询日志本身有轻微的 IO 开销。在高并发生产环境,建议将 long_query_time 设得稍大(如 2-5 秒),或仅在从库开启详细日志分析。
系统资源瓶颈:如果 SQL 已经优化且索引命中良好,但依然慢,需检查服务器 CPU、内存、磁盘 IO 是否达到瓶颈,或是否存在锁等待(Lock Time 高)。
PHP 连接优化:
使用持久连接(Persistent Connection)减少握手开销。
确保 PDO 或 MySQLi 正确关闭连接或使用连接池。
检查 PDO::ATTR_EMULATE_PREPARES 设置,某些情况下禁用模拟预处理能提升性能并避免特定索引提示失效。
通过上述“开启日志 -> 工具分析 -> EXPLAIN 诊断 -> 索引/代码/架构优化”的闭环流程,可以显著降低 PHP + MySQL 网站的响应时间。
下一篇:没有了


