ThinkPHP6不内置事件驱动统计机制,所谓“用事件做统计”实为在Query等事件钩子中手动埋点;需注册think\db\Query事件、成对捕获before/after、校验sql非空、用配置控制慢查阈值,且count/sum等聚合方法本身不触发事件,须主动封装。

ThinkPHP 本身不内置「事件驱动型数据统计」机制,所谓“用事件做统计”,本质是借事件钩子拦截关键行为,手动埋点、聚合、落库。直接在 Connect、Query、ResponseSend 等事件里写统计逻辑,比硬编码在控制器里更解耦,但容易误判统计口径或漏掉异常路径。
怎么监听 Query 事件统计 SQL 执行频次和耗时
这是最常用也最容易出错的统计点:你以为统计了所有查询,其实 Db::query()、原生 pdo->query()、事务内嵌套查询都可能绕过事件。
- 必须在
app\common\Event.php或全局中间件中注册think\db\Query事件,而不是只监听think\db\Connection - 事件回调里要判断
$event->sql是否为空,避免统计 prepare 阶段或空语句 - 记录
microtime(true)差值前,需确认该事件是QueryBefore+QueryAfter成对触发;单侧触发会导致耗时为 0 或负数 - 别把慢查询阈值设死在事件里(比如
> 500ms),应抽成配置项,方便灰度开关
示例片段:
// app/common/Event.php
return [
'think\db\Query' => [
'before' => function ($event) {
$event->startTime = microtime(true);
},
'after' => function ($event) {
$duration = (microtime(true) - $event->startTime) * 1000;
if ($duration > config('database.slow_query_threshold', 500)) {
\think\Log::write("Slow query: {$event->sql} | {$duration}ms", 'slow_sql');
}
}
],
];Count / Sum / Avg 这些聚合方法为什么不能直接当事件用
因为 count()、sum() 等是查询构建器方法,不是事件;它们执行时不会自动触发任何事件,除非你主动 wrap 它们。
立即学习“PHP免费学习笔记(深入)”;
- 想统计“每天调用了多少次
sum('amount')”,得在模型基类或 Db 门面里重载方法,加日志,而不是等事件 - TP6 的
Db::name('order')->sum('amount')底层走的是selectAggregate,它不触发Query事件——只触发QueryBefore和QueryAfter,但没带聚合语义 - 若强行用事件捕获,需在 SQL 字符串里匹配
SUM\(、COUNT\(,但会被注释、子查询、函数嵌套干扰,不可靠
事件统计和数据库连接复用的关系很弱
有人以为监听 Connect 就能反映连接复用率,其实不能。TP 默认用非持久连接(PDO::ATTR_PERSISTENT => false),每次请求新建连接,Connect 事件只会告诉你“这次连上了”,不是“复用了上次的”。
- 真要统计复用效果,得看 Swoole 协程下是否启用
think-swoole扩展;没启的话,静态连接池被多协程共享,Connect事件日志会显示高频创建,但实际底层 socket 可能被复用 - 部署模式(
deploy)只影响读写分离路由,跟连接对象是否复用完全无关 - 验证是否真复用,得看 MySQL 的
SHOW STATUS LIKE 'Threads_connected'增长曲线,而不是 PHP 层事件次数
真正难的不是写事件监听,而是定义清楚:你要统计的是「行为频次」,还是「业务指标」?前者适合事件埋点,后者必须走专门的统计表+定时任务补全。事件只是毛细血管,不是主干道。



















