必须先执行 SELECT @@performance_schema; 确认返回值为1,否则后续线程内存查询均为空;若为0需在my.cnf中设performance_schema=ON并重启,RDS用户则在控制台开启;仅启用不够,还需UPDATE performance_schema.setup_instruments SET ENABLED='YES', TIMED='YES' WHERE NAME LIKE 'memory/%';

确认 Performance Schema 是否已启用
没开 performance_schema,后面所有线程内存查询都会返回空。先执行 SELECT @@performance_schema;,结果必须是 1。如果不是,需在配置文件中设置 performance_schema = ON 并重启 MySQL;RDS 用户则去控制台参数设置里打开该选项。
仅开启还不够:内存采集默认是关闭的。必须手动启用 instruments:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'memory/%';
这条命令不持久,重启后失效——建议写进 my.cnf 的 [mysqld] 段落里加一行:performance-schema-instrument='memory/%=ON'。
查当前活跃线程的实时内存占用
最常用、最直观的视图是 sys.memory_by_thread_by_current_bytes,它把原始 performance_schema 数据做了聚合和可读化处理:
SELECT thread_id AS tid, user, current_allocated, current_statement FROM sys.memory_by_thread_by_current_bytes WHERE current_allocated > 0 ORDER BY current_allocated DESC LIMIT 10;
注意几个关键点:
-
current_allocated是当前线程已分配但尚未释放的内存总量(字节),不是峰值 -
current_statement显示线程正在执行或刚执行完的语句,CALL开头的大概率是存储过程调用 - 如果
user是NULL,说明是内部线程(如innodb/io、thread/sql/replication),这类线程也可能吃大量内存 - 返回为空?要么没活跃线程,要么
memory/%instruments 没开,或线程已退出
定位 CALL 存储过程背后的内存大户 SQL
看到 CALL my_proc() 占内存高,不能直接断定过程本身写得差——真正吃内存的往往是它内部执行的某条 SQL(比如隐式临时表、未索引的 GROUP BY、游标遍历大结果集)。
分两步追:
第一步,从 events_statements_history_long 找出该线程最近执行过的语句摘要:
SELECT THREAD_ID, DIGEST_TEXT, CURRENT_SCHEMA, SUM_TIMER_WAIT, SUM_NUMBER_OF_BYTES_ALLOC FROM performance_schema.events_statements_history_long WHERE THREAD_ID = ? -- 替换为上一步查到的 tid AND SUM_NUMBER_OF_BYTES_ALLOC > 0 ORDER BY EVENT_ID DESC LIMIT 20;
第二步,结合 threads 表确认线程归属:
SELECT PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_INFO FROM performance_schema.threads WHERE THREAD_ID = ?;
常见陷阱:
-
DIGEST_TEXT会把SELECT * FROM t WHERE id = 1和id = 2合并成同一摘要,看不出具体参数,无法直接判断哪次调用异常 - 隐式临时表不会单独出现在
DIGEST_TEXT中,但会在SUM_NUMBER_OF_BYTES_ALLOC或sys.io_by_thread_by_bytes里体现突增 - 如果过程里用了游标 +
FETCH循环,每 fetch 一次都可能触发新内存分配,但历史表只保留最近几十条,容易漏掉
区分全局内存 vs 线程独占内存
MySQL 内存分两大块:全局共享(如 innodb_buffer_pool_size)和每个连接线程私有(如 sort_buffer_size)。查线程内存时,别误把缓冲池当“某个线程”在用。
验证方式:
- 看
SHOW VARIABLES LIKE '%buffer_pool%';和SHOW STATUS LIKE 'Innodb_buffer_pool_bytes_%';,这些是全局的,跟线程 ID 无关 - 线程级缓冲(
sort_buffer_size、join_buffer_size)是按需分配、用完即释,但若语句复杂或数据量大,单次分配可能达几 MB 到上百 MB -
sys.memory_by_thread_by_current_bytes统计的是实际 malloc 出来的内存,比变量设置值更真实——比如sort_buffer_size = 2M,但一条排序只用了 300KB,这里就显示 300KB 左右
真正难排查的是那些“不释放”的内存:比如长期未关闭的游标、未显式 DROP TEMPORARY TABLE 的临时表、或存储过程中反复 PREPARE/EXECUTE 却没 DEALLOCATE 的语句。这些不会立刻报错,但会让 current_allocated 持续缓慢上涨,直到 OOM。


















