You need to enable JavaScript to run this app.
导航
如何解决内存使用率过高的问题
最近更新时间:2025.01.08 17:01:40首次发布时间:2025.01.08 17:01:40

使用云数据库 MySQL 版时,如果内存使用率过高,会有内存耗尽风险。本文中提供了几种常见原因的定位以及解决方法。

问题确认

可通过查看实例内存使用率的监控项,确认实例当前的内存使用率。

  1. 实例列表页面,单击目标实例的实例名称,进入实例信息页。

  2. 在页面上方,单击监控告警页签。

  3. 资源监控分类下,查看内存使用率



快速止损

如果内存使用率长时间超过 90%,并且没有下降趋势,可以通过以下两种手段快速恢复实例性能。

  • 重启实例

  • 切换实例主节点

注意

  • MySQL 本身具有内存动态平衡机制,内存使用率在 90% 以下时可无需关注,同时建议设置内存使用率告警阈值不低于 90%。
  • 云数据库 MySQL 版提供了内存使用率监控项,您可以为实例配置针对此监控项的告警策略,为实例内存使用率设置告警阈值。当内存使用率超过阈值时,及时收到告警信息。关于创建告警策略的详细信息,请参见创建告警策略

原因定位及解决

通常 InnoDB Buffer Pool 对内存的占用是最多的,除 Buffer Pool 的内存占用上限受到 Buffer Pool 配置参数的限制外,还有很多内存是在请求执行中动态分配和调整的,例如内存临时表消耗的内存、table cache、哈希索引、行锁对象等,详细的内存占用和相关参数限制,请参见 MySQL官方文档

开启实例的内存监控功能(可选

为了更方便的排查内存问题,可以开启 performance_schema 的内存监控功能。开启 performance_schema 后,在 performance_schema 库中查询名字为 memory_summary 开头的表来得知内存使用情况,例如,全局维度的内存利用率分析表:memory_summary_global_by_event_name。
常见的查询:

SELECT EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED FROM performance_schema.memory_summary_global_by_event_name
    WHERE COUNT_ALLOC != 0 
       OR COUNT_FREE != 0
       OR SUM_NUMBER_OF_BYTES_ALLOC != 0 
       OR SUM_NUMBER_OF_BYTES_FREE != 0 
       OR LOW_COUNT_USED != 0 
       OR CURRENT_COUNT_USED != 0 
       OR HIGH_COUNT_USED != 0 
       OR LOW_NUMBER_OF_BYTES_USED != 0 
       OR CURRENT_NUMBER_OF_BYTES_USED != 0 
       OR HIGH_NUMBER_OF_BYTES_USED != 0 
    ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC
    LIMIT 10;

说明

如果对于查询时延不敏感,或者热数据访问量比较小,可以通过调整 buffer pool size 来降低 buffer pool 的使用,给 SQL 动态运行预留更多内存。

3.1 QPS 上升

原因分析
首先我们要先确认流量是否发生了变化、业务是否上线了新的 SQL。如果业务流量突增,则可能导致内存使用率上升。
操作路径:登录数据库工作台 > 观测诊断 > 性能监控 > 总查询数。更详细信息,请参见查看性能监控指标

解决办法
通过数据库工作台的全量 SQL 洞察功能确定是哪些 SQL 有较多写入,并通过 SQL 限流来限制 SQL 的执行频率。

说明

全量 SQL 洞察及 SQL 限流当前为数据库工作台的邀测功能,如需使用请提交工单申请。

  1. 定位 SQL:确定是哪些 SQL 有较多写入。 进行 SQL 洞察的操作路径:登录数据库工作台 > 观测诊断 > 全量 SQL 洞察。更详细信息,请参见全量 SQL 洞察


  1. 处理 SQL。 定位到 SQL 后,可通过 SQL 限流KILL 会话的方式进行处理。

3.2 连接数上升

原因分析
由于 MySQL 会为每个连接分配内存,如果连接数增量,也会导致 MySQL 内存使用率上升。
操作路径:登录**数据库工作台** > 观测诊断 > 性能监控 > 当前打开连接数。更详细信息,请参见查看性能监控指标

解决办法
释放空闲连接。

3.3 SQL 导致内存上升

3.3.1 复杂 SQL

原因分析
Server 层的内存占用较高的包括 Thread Cache、BinLog Cache、Sort Buffer、Read Buffer、Join Buffer 等线程缓存,这类缓存非常驻内存,往往会随着连接关闭而释放。如果出现大量 join、order by 类型的复杂 SQL,可能会导致内存上升。
解决办法

  1. 减小 sort_buffer_size / join_buffer_size/binlog_cahce_size(可能会造成查询效率变低)。

  2. 定位 SQL:可以通过执行 show processlist 或通过数据库工作台的慢日志分析,通过 explain 语句查看,是否存在 JOIN、AGG 等操作。操作方法同上。 进行慢日志分析的操作路径:登录**数据库工作台** > 观测诊断 > 慢日志分析。更详细信息,请参见慢日志分析

  1. 处理 SQL:定位到 SQL 后,可通过 SQL 限流KILL 会话的方式进行处理。

3.3.2 临时表

原因分析
MySQL 在处理SQL的时候可能还用到临时表,内存临时表大小受到参数 tmp_table_size 和 max_heap_table_size 限制,超过限制后将转化为磁盘临时表。如果瞬间有大量的连接创建大量的临时表,可能会造成内存突增。MySQL 8.0 实现了新的 temptable engine,所有线程分配的内存临时表大小之和必须小于参数temptable_max_ram,temptable_max_ram 默认为1 GB,超出后转换为磁盘临时表。
解决办法

  1. 减小 tmp_table_size 和 max_heap_table_size (MySQL 5.7) 或者 temptable_max_ram (MySQL 8.0)。

  2. 定位 SQL:可以通过执行 show processlist 或通过数据库工作台的慢日志分析 ,通过 explain 查看是否使用临时表。

  3. 处理 SQL:定位到 SQL 后,可通过 SQL 限流KILL 会话的方式进行处理。

3.3.3 多语句 (Multiple statements)

原因分析
MySQL支持将多个SQL语句用英文分号(;)分隔,然后一起发给 MySQL,MySQL 会逐条处理 SQL,但是某些内存需要等到所有的 SQL 执行结束才释放。
采用这种 multiple statements 的发送方式时,如果一次性发送的 SQL 非常多,例如达到数百兆,SQL 实际执行过程中各种对象分配累积消耗的内存非常大,很有可能导致 MySQL 进程内存耗尽。除了multiple statements,bulk insert(一条 insert 语句批量插入多条记录)、框架工具生成的超长 SQL ,也会在网络协议和 sql parser 阶段分配较多内存,也需要重点关注。
一般场景下,如果存在大批量的 multiple statements 或者 bulk insert,网络流量会有突增,可以从网络流量监控,判断是否有这种现象。
操作路径:登录**数据库工作台** > 观测诊断 > 性能监控 > 平均每秒从所有客户端收到的字节数。更详细信息,请参见查看性能监控指标

解决办法

  1. 定位 SQL:可以通过执行 show processlist 或通过数据库工作台的慢日志分析 ,查看是否有可疑的 SQL,操作方法同上。

  2. 建议业务实现中尽量避免 multiple statements 的 SQL 发送方式。拆分语句,减少单次执行语句的数据量;

  3. 减少并发,避免太多的上述语句并发执行。

3.3.4 blob/text 字段

原因分析
如果查询或写入长度非常大的Blob大字段,会对大字段动态分配内存,部分内存需要在table关闭的时候才释放,会造成内存增加。
解决办法

  1. 定位 SQL:可以通过执行 show processlist 或通过数据库工作台的慢日志分析 ,查看是否有可疑的 SQL,操作方法同上。

  2. 减少并发,避免太多的上述语句并发执行;

  3. 在低峰期执行 flush table table_name 释放对应 table 的 Cache。

3.3.5 全文索引

原因分析
全文索引需要占用大量的内存来处理索引数据,所以需要重点关注。
解决办法

  1. 定位 SQL:可以通过执行 show processlist 或通过数据库工作台的慢日志分析 ,查看是否有可疑的SQL,操作方法同上。

  2. 在低峰期执行 flush table table_name 释放对应 table 的 Cache。

3.4 库表较多

原因分析
在 MySQL 中每个表也是有内存开销的,除了 InnoDB 层 table 对象消耗内存外,Server 层的 Table Cache 也会消耗内存。如果实例内表特别多或 QPS 很高,可能也会导致内存使用率上升。
解决办法

  1. 查看最近是否调大过 table_open_cache 参数。

    1. 实例列表页面,单击目标实例的实例名称,进入实例信息页。

    2. 在页面上方,单击参数配置页签。

    3. 查看MySQL参数修改历史

  2. 确认下业务的库表数量是否在持续增加,如果一直增加,建议提升实例规格。

3.5 存储过程

原因分析
store procedure/function/trigger/event 的调用要额外消耗内存,需要重点关注。

可以通过数据库工作台查看是否有业务创建的存储过程等。登录数据库工作台即可查看实例对存储过程的使用。

  1. 如果开启了 performance_schema,可以通过一下 SQL 查看存储过程的内存占用
select * from memory_summary_global_by_event_name where  EVENT_NAME like "%memory/sql/sp_head%"\G;

解决办法
登录数据库工作台,确认实例使用的触发器、存储过程、函数和事件的数量,并减少相关使用。

3.6 performance_schema 导致的内存上升

原因分析
performance_schema 虽然可以监控 MySQL 内存使用情况,但是 performance_schema 的维护也会有额外内的存开销,所以打开 performance_schema 或者 loose_rds_performance_schema 也可能导致内存使用率上升。尤其是在实例规格较低时,使用此功能可能会给实例带来较大压力。因此,在开启此功能后,需持续关注实例的内存使用率。
可以执行 SHOW ENGINE PERFORMANCE_SCHEMA STATUS 获取 performance_schema 消耗的内存,重点关注输出结果中的 performance_schema.memory。
解决办法

  1. 关闭 performance_schema 或者 loose_rds_performance_schema

  2. 云数据库 MySQL 版提供了可修改参数 performance_schema_max_thread_instances,该参数用于设定观测线程的最大数量,即 performance_schema.threads 表的最大行数,进而对 performance_schema 功能对内存的消耗进行控制。该参数默认值为 1024,您可根据实例规格和业务需求自行调整。关于修改参数的详细信息,请参见修改参数

3.7 提升实例规格

经过以上手段进行排查和解决后,如果内存使用率仍持续升高,则可能是业务压力导致的正常情况,此时可考虑通过常规变配或临时升配提高实例规格,增加实例的内存。
关于如何对实例进行规格变更,请参见变更配置

常用操作

SQL 限流

定位到问题 SQL 后,可以通过 SQL 限流限制 SQL 的执行频率。
操作步骤

  1. 登录**数据库工作台** > 观测诊断 > 会话管理 > SQL限流 > 打开SQL限流功能 > 创建任务

  2. 在创建 SQL 限流任务窗口中,输入问题 SQL,调整最大并发数(最大并发数为 0 时会完全屏蔽该 SQL),单击关键字生成和校验拆分关键词,关键词会用波浪线(~)分隔。

  1. 单击确定提交任务。

说明 更多详细信息,请参见SQL 限流管理

KILL 会话

操作步骤

  1. 登录**数据库工作台** > 观测诊断 > 会话管理

  2. 实时会话区域,关注会话的 TimeState