PHP慢查询怎么抓

wen PHP项目 2

本文目录导读:

PHP慢查询怎么抓

  1. 数据库慢查询(MySQL)
  2. PHP 应用层面慢查询
  3. 生产环境最佳实践组合
  4. 实时监控与告警
  5. 终极建议

在 PHP 中抓取慢查询,通常涉及两个层面:数据库慢查询PHP 自身执行慢,下面我分两部分详细说明,并给出最佳实践方案。

数据库慢查询(MySQL)

MySQL 慢查询日志(最常用)

-- 查看当前状态
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
-- 开启慢查询(临时)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- 记录超过1秒的查询
SET GLOBAL log_queries_not_using_indexes = 'ON'; -- 记录未使用索引的查询
-- 永久开启(my.cnf 配置)
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

查看慢查询日志:

# 使用 mysqldumpslow 工具分析
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
# 实时查看
tail -f /var/log/mysql/slow.log

performance_schema(8.0+ 推荐)

-- 查询执行时间最长的SQL
SELECT 
    DIGEST_TEXT,
    COUNT_STAR,
    SUM_TIMER_WAIT/1000000000 AS total_seconds,
    AVG_TIMER_WAIT/1000000000 AS avg_seconds
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT NOT LIKE '%performance_schema%'
ORDER BY avg_seconds DESC
LIMIT 10;

使用 pt-query-digest(Percona Toolkit)

# 安装后使用
pt-query-digest /var/log/mysql/slow.log > slow_analysis.txt
pt-query-digest --since="1 hour ago" /var/log/mysql/slow.log

PHP 应用层面慢查询

启用内置查询日志(最推荐)

<?php
// 在数据库连接初始化处添加
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_TIMEOUT => 3, // 连接超时
];
// 使用 PDO 列子,自行封装记录慢SQL
class MySQLTrace {
    private static $slowQueries = [];
    public static function traceQuery($sql, $params = [], $threshold = 1.0) {
        $start = microtime(true);
        $result = self::query($sql, $params);
        $elapsed = microtime(true) - $start;
        if ($elapsed > $threshold) {
            self::$slowQueries[] = [
                'sql' => $sql,
                'params' => $params,
                'time' => $elapsed,
                'time_str' => date('Y-m-d H:i:s')
            ];
            // 可以记录到日志或发邮件
            self::logSlowQuery();
        }
        return $result;
    }
}
?>

使用框架内置的日志功能

Laravel 监听 SQL:

// AppServiceProvider.php
use Illuminate\Support\Facades\DB;
DB::listen(function ($query) {
    if ($query->time > 1000) { // 超过1秒
        Log::warning('慢SQL', [
            'sql' => $query->sql,
            'bindings' => $query->bindings,
            'time' => $query->time
        ]);
    }
});

Symfony Doctrine:

// config/packages/doctrine.yaml
doctrine:
    dbal:
        connections:
            default:
                profiling: true
                server_version: '%database_server_version%'
# 监听器
class SlowQueryLogger {
    public function onKernelRequest(GetResponseEvent $event) {
        $logger = $event->getRequest()->attributes->get('logger');
        // 注册SQL日志记录器
    }
}

使用 Tinker/自定义工具(小型项目)

<?php
// 创建一个通用查询函数
function query($sql) {
    $start = microtime(true);
    $result = DB::query($sql);
    $elapsed = microtime(true) - $start;
    if ($elapsed > 1) {
        error_log("[SLOW] " . date('Y-m-d H:i:s') . " - " . $elapsed . "s - " . $sql . "\n", 3, "/tmp/slow_php.log");
    }
    return $result;
}

生产环境最佳实践组合

组合方案推荐

# 1. 系统监控(OPCache, Xdebug Profile)
php -d xdebug.mode=profile -d xdebug.profiler_output_dir=/tmp/profiles -d xdebug.profiler_output_name=trace.%s.xt php test.php
# 2. 使用 Blackfire / Tideways(商业工具价格高但功能强大)
# 3. 引入 APM 工具(如 New Relic, Datadog,开源的用 Skywalking/Pinpoint)
# 4. 日志记录 + 监控告警

轻量级解决(Laravel 优雅方案)

<?php
// app/Providers/AppServiceProvider.php
public function boot()
{
    DB::listen(function ($query) {
        $threshold = config('app.slow_query_threshold');
        if ($query->time > $threshold) {
            Log::channel('database')->warning('慢查询', [
                'sql' => $query->sql,
                'bindings' => $query->bindings,
                'time_ms' => $query->time,
                'url' => request()->fullUrl(),
                'session_id' => session()->getId()
            ]);
        }
    });
}

实时监控与告警

简单监控脚本

<?php
// monitor_slow.php - 定时执行
$cmd = "pt-query-digest /var/log/mysql/slow.log --since='5 minutes' --limit=5";
exec($cmd, $output, $return);
if ($return == 0) {
    $slowQueries = implode("\n", $output);
    if (strlen($slowQueries) > 100) {
        // 发送邮件/钉钉通知
        sendNotify("发现慢查询", $slowQueries);
    }
}

终极建议

  1. 优先使用 MySQL 原生慢日志——最有效且影响最小
  2. 应用程序内增加慢查询探针——能记录到具体的业务上下文
  3. 定期分析与优化——用 pt-query-digest 每周分析一次
  4. 建立SOP: 一旦发现慢查询,按照:查询执行计划 → 索引检查 → SQL重写 → 缓存优化 顺序处理
  5. 监控级联: 把慢查询和基础设施监控(CPU、内存、连接数)联动起来

实际案例参考

# 某电商系统,他们通过以下方式定位问题:
# 1. MySQL slow log 里发现了一个跑了 25 秒的查询
# 2. app 内日志显示这个查询在 14:30 - 14:45 出现高频调用
# 3. 结合 Redis 缓存发现缓存穿透,最终通过改造 SQL 加索引和 Redis 三层缓存解决

这套组合拳基本能覆盖绝大多数 PHP 慢查询场景,关键是要尽早建立监控体系,而不是等到用户反馈才去排查。

抱歉,评论功能暂时关闭!