如何排查 RDS for MySQL 内存占用问题

MySQL
问题描述

我的 RDS for MySQL 实例内存持续走低,我该如何排查 MySQL 内存占用问题?

问题分析

MySQL 内存分为两部分,全局内存和线程级内存[1]:

  • 独享内存参数的配置:关联 join_buffer_size,,线程栈 thread_stack,随机读 read_rnd_buffer_size,排序 sort_buffer_size,顺序读 read_buffer_size,客户端结果集暂存 net_buffer_length,二进制日志 binlog_cache_size,插入缓存 bulk_insert_buffer_size,临时表 tmp_table_sizequery_cache_siz等。

  • 全局共享内存参数的配置:表缓存 table_open_cache,连接线程 thread_cache_size,表定义缓存 table_definition_cache,innodb日志缓存 innodb_log_buffer_size,数据索引缓存 innodb_buffer_pool_size,数据字典缓存 innodb_additional_mem_pool_sizeback_log队列等。

注: 8.0 中已经废弃了 query_cache_size。

分析完 MySQL 有那些内存消耗的理论基础之后,我们可以依据这些线索进行后续的调优操作。

解决方案

我们可以使用如下公式来计算 RDS for MySQL 内存大致使用量:

Maximum MySQL Memory Usage = innodb_buffer_pool_size + key_buffer_size + ((read_buffer_size + read_rnd_buffer_size + sort_buffer_size + join_buffer_size) X max_connections)

或者您可以使用如下方式:

mysql> SELECT @@query_cache_size,
     @@key_buffer_size,
     @@innodb_buffer_pool_size ,
     @@innodb_log_buffer_size ,
     @@tmp_table_size ,
     @@read_buffer_size,
     @@sort_buffer_size,
     @@join_buffer_size ,
     @@read_rnd_buffer_size,
     @@binlog_cache_size,
     @@thread_stack,
     (SELECT COUNT(host) FROM  information_schema.processlist where command<>'Sleep')\G
     
示例输出如下:

************* 1. row ************

@@query_cache_size: 0                           
@@key_buffer_size: 8388608
@@innodb_buffer_pool_size: 4294967296
@@innodb_log_buffer_size: 16777216
@@tmp_table_size: 16777216
@@read_buffer_size: 262144
@@sort_buffer_size: 262144
@@join_buffer_size: 262144
@@read_rnd_buffer_size: 262144
@@binlog_cache_size: 32768
@@thread_stack: 262144

同时,如果您开启了 performance_schema, 我们也可以从如下统计信息表来查看占用内存情况:

sys.x$memory_by_host_by_current_bytes;


sys.x$memory_by_thread_by_current_bytes; 


sys.x$memory_by_user_by_current_bytes; 


sys.x$memory_global_by_current_bytes; 


sys.x$memory_global_total; 


performance_schema.memory_summary_by_account_by_event_name; 


performance_schema.memory_summary_by_host_by_event_name; 


performance_schema.memory_summary_by_thread_by_event_name; 


performance_schema.memory_summary_by_user_by_event_name; 


performance_schema.memory_summary_global_by_event_name;

例如使用如下 SQL 来查看总内存使用:

mysql> SELECT 
SUM(CAST(replace(current_alloc,'MiB','')  as DECIMAL(10, 2))  ) 
FROM sys.memory_global_by_current_bytes
WHERE current_alloc like '%MiB%';

使用如下 SQL 来查看账号级别的内存统计信息:

mysql> SELECT user,event_name,current_number_of_bytes_used/1024/1024 as MB_CURRENTLY_USED
FROM performance_schema.memory_summary_by_account_by_event_name
WHERE host<>"localhost"
ORDER BY  current_number_of_bytes_used DESC LIMIT 5;


示例输出如下:

+--------+-----------------------------------------------------+-------------------+
| user   | event_name                                          | MB_CURRENTLY_USED |
+--------+-----------------------------------------------------+-------------------+
| rudonx | memory/sql/Locked_tables_list::m_locked_tables_root |        0.00000000 |
| rudonx | memory/sql/display_table_locks                      |        0.00000000 |
| rudonx | memory/sql/gdl                                      |        0.00000000 |
| rudonx | memory/sql/Table_triggers_list                      |        0.00000000 |
| rudonx | memory/sql/servers                                  |        0.00000000 |
+--------+-----------------------------------------------------+-------------------+
10 rows in set (0.00 sec)

:打开 performance schema 会对性能有一定影响,对于小规格的实例尤为明显,请谨慎评估是否需要打开。

使用 show engine innodb status 来查看内存占用:

mysql> show engine innodb status\G

内存部分输出如下:

---------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 412286976
Dictionary memory allocated 134531
Buffer pool size   24576
Free buffers       23989
Database pages     584
Old database pages 227
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 520, created 64, written 8097
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
#这行显示了缓冲池的命中率,它用来衡量innodb在缓冲池中查找到所需页的比例,它度量自上次Innodb状态输出后到本次输出这段时间内的命中率
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
#这行显示了页面预读,随机预读的每秒页数,如果evicted without access 意味着有一部分数据被加载到了内存池中,在还没有被读取到的时候,被踢出了buffer pool,如果这个值持续走高,我们就需要增大缓冲池了
LRU len: 584, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]

最佳实践

  • 通过将大型查询分解为多个较小的查询来避免长时间运行的事务。

  • 优化业务逻辑,优化 SQL,show processlist 输出中的 status 列,如:sending data,Copying to tmp table,Copying to tmp table on disk,Sorting result,locked,Using filesort,这些都是有性能问题的 SQL。

  • 尽可能少使用视图,存储过程,函数。

  • SQL 的结果集要尽量小。

  • 避免长事务,事务结束后及时断开连接。

参考文档

[1] https://dev.mysql.com/doc/refman/5.7/en/memory-use.html

如果您有其他问题,欢迎您联系火山引擎技术支持服务

206
0
0
0
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论