SHOW PROFILE 不可信,因其仅记录极简内存分配事件且不跟踪释放,CPU统计不包含系统调用;真实资源消耗须用 performance_schema 按线程粒度分析,并辅以 ps 等系统工具交叉验证。

不能靠 SHOW PROFILE 看真实 CPU 和内存消耗,它在 MySQL 8.0 已弃用,Memory 列基本为 0 或空,CPU 统计也不准确。
为什么 SHOW PROFILE 不可信
它只记录极简的分配事件(比如 sort buffer 分配),完全不包含:join_buffer、tmp_table 内存、InnoDB 行锁结构、缓冲区预分配等关键开销;不跟踪内存释放,也不区分堆/栈/临时文件回退路径;并发下线程间统计互相干扰。执行 SET profiling = 1 后,SHOW PROFILES 中的 Memory 值多数为空或为 0,CPU 列也仅反映用户态粗略时间,不含系统调用、上下文切换等真实负载。
必须用 performance_schema 查线程级真实资源
真实 CPU 和内存只能通过 performance_schema 按线程粒度归因。关键步骤是:
- 确认 memory instruments 已启用:
SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'memory/%' AND ENABLED = 'YES';—— 若结果极少,需执行:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'memory/sql/%' OR NAME LIKE 'memory/innodb/%'; - 查当前线程 ID:
SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID = CONNECTION_ID(); - 执行目标 SQL 前后,分别查该线程的 CPU 时间和内存变化:
SELECT EVENT_NAME, SUM_TIMER_WAIT, SUM_NUMBER_OF_BYTES_ALLOC FROM performance_schema.events_statements_summary_by_thread_by_event_name WHERE THREAD_ID = ? AND EVENT_NAME LIKE 'statement/sql/%';和SELECT EVENT_NAME, SUM_NUMBER_OF_BYTES_ALLOC FROM performance_schema.memory_summary_by_thread_by_event_name WHERE THREAD_ID = ?; - 对比两次
SUM_NUMBER_OF_BYTES_ALLOC差值,才是本次语句实际申请的堆内存总量
别误用 memory_summary_global_by_event_name
这个表汇总了所有线程的内存事件,无法绑定到某条 SQL。生产库中多个查询并发时,你看到的“增长”可能是其他会话触发的缓冲区预分配、DDL 元数据缓存,甚至后台 purge 线程行为。它适合看趋势,不适合归因单条语句。同理,events_statements_summary_global_by_event_name 的 SUM_TIMER_WAIT 是全局累计值,没有线程上下文,不能用于定位哪条 SQL 吃了 CPU。
配合系统工具交叉验证更可靠
MySQL 是单进程多线程模型,mysqld 主进程的系统级资源才反映真实压力:
- 查 CPU 占用最高的前 10 进程:
ps aux --sort=-%cpu | head -11,确认mysqld行的%CPU是否异常 - 查内存占用:
ps aux | grep mysqld | grep -v grep,看RSS列(单位 KB),除以 1024 得 MB - 验证 PID 是否真属 MySQL:
cat /proc/<pid>/cmdline | tr '\0' ' '</pid>,避免被同名进程干扰 - 若发现某个 OS 线程(
thread_os_id)CPU 飙高,可用:SELECT * FROM performance_schema.threads WHERE THREAD_OS_ID = ?;反查对应 SQL
真正难的是把 OS 层线程、Performance Schema 线程 ID、SQL 执行上下文三者对齐——稍有延迟或并发干扰,差值就失真。务必在低峰期、单线程复现下做测量。


















