在mysql的运行过程中,SQL语句的内存消耗是影响数据库性能的核心指标之一,尤其是执行复杂联表查询、大批量数据写入等操作时,内存占用过高可能导致查询卡顿甚至数据库服务异常。mysql从特定版本开始提供了内存监控相关的系统表,我们可以通过这些表获取SQL语句执行过程中执行器的真实内存消耗情况。

开启mysql内存监控功能
首先要确保mysql的内存监控功能处于开启状态,默认情况下部分内存监控项可能是关闭的,我们可以通过修改系统变量来开启。执行以下SQL语句开启所有内存监控项:
-- 开启内存监控功能 UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'memory/%'; -- 开启消费者配置,让监控数据可以被查询到 UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%memory%';
执行完成后,我们可以通过查询performance_schema.setup_instruments表确认内存监控项是否已经开启:
SELECT NAME, ENABLED, TIMED FROM performance_schema.setup_instruments WHERE NAME LIKE 'memory/%' LIMIT 5;
核心内存监控表说明
mysql中与内存监控相关的核心表主要有以下几个,我们可以通过这些表获取到不同维度的内存消耗数据:
| 表名 | 作用说明 |
|---|---|
| memory_summary_by_thread_by_event_name | 按线程和事件名称统计内存分配情况,可关联到具体SQL执行的线程 |
| memory_summary_global_by_event_name | 全局维度的内存分配统计,汇总所有线程的内存消耗 |
| threads | 存储所有线程的基础信息,可通过PROCESSLIST_ID关联到正在执行的SQL |
| events_statements_current | 存储当前正在执行的SQL语句信息,可获取SQL对应的线程ID |
获取单条SQL的真实内存消耗
我们可以通过以下步骤获取指定SQL语句执行过程中的真实内存消耗:
步骤1:定位目标SQL的线程ID
先执行我们要分析的SQL语句,然后在另一个会话中查询当前正在执行的语句,获取对应的线程ID:
-- 查询当前正在执行的SQL,获取THREAD_ID和PROCESSLIST_ID SELECT THREAD_ID, PROCESSLIST_ID, SQL_TEXT FROM performance_schema.events_statements_current WHERE SQL_TEXT IS NOT NULL;
步骤2:查询该线程的内存消耗
拿到THREAD_ID之后,我们可以查询该线程下所有内存事件的消耗总和,得到这条SQL执行时的总内存占用:
-- 查询指定线程的内存总消耗,单位是字节
SELECT
SUM(CURRENT_NUMBER_OF_BYTES_USED) AS total_memory_bytes,
SUM(CURRENT_NUMBER_OF_BYTES_USED) / 1024 / 1024 AS total_memory_mb
FROM performance_schema.memory_summary_by_thread_by_event_name
WHERE THREAD_ID = 替换为你获取到的线程ID;
步骤3:分析执行器各阶段的内存分配
如果需要进一步分析执行器在不同阶段的内存行为,比如解析阶段、优化阶段、执行阶段的内存分配,可以按事件名称分组查询:
-- 按事件名称分组查询线程的内存消耗,定位高内存占用的执行阶段
SELECT
EVENT_NAME,
CURRENT_NUMBER_OF_BYTES_USED AS used_bytes,
CURRENT_NUMBER_OF_BYTES_USED / 1024 / 1024 AS used_mb
FROM performance_schema.memory_summary_by_thread_by_event_name
WHERE THREAD_ID = 替换为你获取到的线程ID
ORDER BY used_bytes DESC
LIMIT 10;
事件名称中通常会包含内存分配的模块信息,比如memory/sql/THD表示线程相关的内存,memory/sql/Filesort表示排序操作的内存,memory/sql/Join_cache表示联表缓存的内存,通过这些名称我们可以明确执行器不同行为对应的内存消耗。
分析执行器行为的注意事项
在利用内存监控表分析执行器行为时,需要注意以下几点:
- 内存监控表的数据是实时更新的,SQL执行完成后对应的内存可能会被释放,因此需要在SQL执行过程中或者刚执行完成时查询数据,避免数据被清空。
- 部分内存是会话级别的,会话断开后对应的内存统计会从线程表中移除,因此分析时尽量保持会话存活。
- 如果SQL执行时间很短,可能需要在SQL执行前开启 profiling 功能,结合 profiling 的执行阶段时间点和内存监控数据做关联分析。
示例:完整分析一条联表查询的内存消耗
以下是一个完整的分析示例,假设我们要分析的SQL是SELECT a.id, b.name FROM test_table_a a JOIN test_table_b b ON a.id = b.a_id WHERE a.status = 1 LIMIT 1000:
-- 1. 先开启内存监控(如果已经开启可跳过) UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'memory/%'; UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%memory%'; -- 2. 执行目标SQL(在会话1执行) SELECT a.id, b.name FROM test_table_a a JOIN test_table_b b ON a.id = b.a_id WHERE a.status = 1 LIMIT 1000; -- 3. 在会话2查询当前执行的SQL对应的线程ID(如果SQL已经执行完,可查询events_statements_history表) SELECT THREAD_ID, PROCESSLIST_ID, SQL_TEXT FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE 'SELECT a.id, b.name FROM test_table_a%'; -- 假设查询到的THREAD_ID是45 -- 4. 查询该线程的总内存消耗 SELECT SUM(CURRENT_NUMBER_OF_BYTES_USED) AS total_bytes, SUM(CURRENT_NUMBER_OF_BYTES_USED)/1024/1024 AS total_mb FROM performance_schema.memory_summary_by_thread_by_event_name WHERE THREAD_ID = 45; -- 5. 查询各执行阶段的内存消耗 SELECT EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED/1024/1024 AS used_mb FROM performance_schema.memory_summary_by_thread_by_event_name WHERE THREAD_ID = 45 ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 10;
通过上面的查询结果,我们可以明确这条联表查询总共消耗了多少内存,其中联表缓存、排序操作、临时表等分别占用了多少内存,从而针对性地优化SQL,比如调整联表顺序、减少查询字段、增加索引避免临时表生成等。
mysqlmemory_monitor_tableSQL_memory_consumptionexecutor_behavior修改时间:2026-07-12 11:18:15