文档首页> 云数据库 RDS> 最佳实践> RDS for MySQL> 性能调优> RDS for MySQL性能调优-解决内存使用率高问题
更新时间:2023-12-18 GMT+08:00

RDS for MySQL性能调优-解决内存使用率高问题

实例内存使用率和缓冲池命中率是MySQL的关键指标。如果内存使用率过高,会有内存耗尽风险,如果缓冲池命中率低,大量的数据页无法命中缓冲池的数据页,需要从磁盘读取数据,造成I/O吞吐增加和延迟增加。

查看内存使用情况

  • 通过智能DBA助手查看实例的内存使用情况,具体请参见查看性能指标

  • 您还可以使用performance_schema,设置相关的内存仪表(instrumentation),通过内存占用统计表查看内存占用。更多信息,请参见MySQL官方文档

    MySQL5.6不支持performance_schema内存检测,不能使用performance_schema进行内存检测。

  1. 参考修改当前实例参数,设置performance_schema = ON。

  2. 重启实例使参数修改生效。
  3. 查询sys.memory_global_total视图,查看当前总的内存使用。

    select * from sys.memory_global_total;

  4. 查询sys.session视图。
    1. 查询sys.session视图,current_memory字段记录每个会话的内存使用。

      select thd_id,conn_id, current_memory from sys.session;

    2. 查看performance_schema下占用内存较高的会话线程内存明细。

      select * from memory_summary_by_thread_by_event_name where thread_id= "占用内存较高的线程id" order by CURRENT_NUMBER_OF_BYTES_USED;

  5. 查询sys.memory_by_thread_by_current_bytes。
    1. 查询sys.memory_by_thread_by_current_bytes,current_allocated字段记录每个后台线程的内存使用。

      select thread_id, user, current_allocated from memory_by_thread_by_current_bytes;

    2. 查看performance_schema下占用内存较高的后台线程内存明细。

      select * from memory_summary_by_thread_by_event_name where thread_id= "占用内存较高的线程id" order by CURRENT_NUMBER_OF_BYTES_USED;

  6. 查询memory_global_by_current_bytes视图,按照内存分配类型获取内存使用统计信息。

    select event_name,current_alloc from sys.memory_global_by_current_bytes where event_name not like 'memory/performance_schema%' ;

  7. 根据查询结果分析内存使用过高原因。

内存高常见原因

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

  • 多语句(multiple statements)

    MySQL支持将多个SQL语句用英文分号(;)分隔,然后一起发给MySQL,MySQL会逐条处理SQL,但是某些内存需要等到所有的SQL执行结束才释放。

    这种multiple statements的发送方式,如果一次性发送的SQL非常多,例如达到数百兆,SQL实际执行过程中各种对象分配累积消耗的内存非常大,很有可能导致MySQL进程内存耗尽。

    使用multiple statements发送方式,网络流量会有突增,可以从网络流量监控和SQL洞察,判断是否有这种现象。建议业务实现中尽量避免multiple statements的SQL发送方式。

  • 缓冲池(Buffer Pool)问题

    所有表的数据页都存放在缓冲池中,查询执行的时候如果需要的数据页直接命中缓冲池,就不会发生物理I/O,SQL执行的效率较高,缓冲池采用LRU算法管理数据页,所有的脏页放到Flush List链表中。

    InnoDB Buffer Pool的内存通常是实例内存中占比最大的。

    Buffer Pool相关的常见问题:

    • 数据页预热不足导致查询的延迟较高。通常发生在实例重启、冷数据读取或缓冲池命中率较低的场景,建议升级实例规格或大促前预热数据。
    • 脏页累积太多。当未刷新脏页的最旧LSN和当前LSN的距离超过76%时,会触发用户线程同步刷新脏页,导致实例性能严重下降。优化方式是均衡写入负载、避免写入吞吐过高、调整刷新脏页参数或升级实例规格等。
  • 临时表

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

  • 其他原因

    如果实例内表特别多或QPS很高,Table Cache可能也会消耗内存,建议实例避免创建太多表或设置参数table_open_cache过大。

    自适应哈希索引占用的内存默认是Bufffer Pool的1/64。如果查询或写入长度非常大的Blob大字段,会对大字段动态分配内存,也会造成内存增加。

    如果出现内存使用率异常增加或实例内存耗尽,您可以参考官方文档排查上涨原因。